1*53a374c1SSascha Wildner /*-
2*53a374c1SSascha Wildner * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3*53a374c1SSascha Wildner * All rights reserved.
4*53a374c1SSascha Wildner *
5*53a374c1SSascha Wildner * Redistribution and use in source and binary forms, with or without
6*53a374c1SSascha Wildner * modification, are permitted provided that the following conditions
7*53a374c1SSascha Wildner * are met:
8*53a374c1SSascha Wildner * 1. Redistributions of source code must retain the above copyright
9*53a374c1SSascha Wildner * notice, this list of conditions and the following disclaimer.
10*53a374c1SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
11*53a374c1SSascha Wildner * notice, this list of conditions and the following disclaimer in the
12*53a374c1SSascha Wildner * documentation and/or other materials provided with the distribution.
13*53a374c1SSascha Wildner *
14*53a374c1SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*53a374c1SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*53a374c1SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*53a374c1SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*53a374c1SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*53a374c1SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*53a374c1SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*53a374c1SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*53a374c1SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*53a374c1SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*53a374c1SSascha Wildner * SUCH DAMAGE.
25*53a374c1SSascha Wildner *
26*53a374c1SSascha Wildner * $FreeBSD: head/sys/dev/ipmi/ipmi_ssif.c 172836 2007-10-20 23:23:23Z julian $
27*53a374c1SSascha Wildner */
28*53a374c1SSascha Wildner
29*53a374c1SSascha Wildner #include <sys/param.h>
30*53a374c1SSascha Wildner #include <sys/systm.h>
31*53a374c1SSascha Wildner #include <sys/bus.h>
32*53a374c1SSascha Wildner #include <sys/condvar.h>
33*53a374c1SSascha Wildner #include <sys/eventhandler.h>
34*53a374c1SSascha Wildner #include <sys/kernel.h>
35*53a374c1SSascha Wildner #include <sys/kthread.h>
36*53a374c1SSascha Wildner #include <sys/module.h>
37*53a374c1SSascha Wildner #include <sys/conf.h>
38*53a374c1SSascha Wildner
39*53a374c1SSascha Wildner #include <bus/smbus/smbconf.h>
40*53a374c1SSascha Wildner #include <dev/smbus/smb/smb.h>
41*53a374c1SSascha Wildner
42*53a374c1SSascha Wildner #include "smbus_if.h"
43*53a374c1SSascha Wildner
44*53a374c1SSascha Wildner #ifdef LOCAL_MODULE
45*53a374c1SSascha Wildner #include <ipmivars.h>
46*53a374c1SSascha Wildner #else
47*53a374c1SSascha Wildner #include <dev/misc/ipmi/ipmivars.h>
48*53a374c1SSascha Wildner #endif
49*53a374c1SSascha Wildner
50*53a374c1SSascha Wildner #define SMBUS_WRITE_SINGLE 0x02
51*53a374c1SSascha Wildner #define SMBUS_WRITE_START 0x06
52*53a374c1SSascha Wildner #define SMBUS_WRITE_CONT 0x07
53*53a374c1SSascha Wildner #define SMBUS_READ_START 0x03
54*53a374c1SSascha Wildner #define SMBUS_READ_CONT 0x09
55*53a374c1SSascha Wildner #define SMBUS_DATA_SIZE 32
56*53a374c1SSascha Wildner
57*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
58*53a374c1SSascha Wildner static void
dump_buffer(device_t dev,const char * msg,u_char * bytes,int len)59*53a374c1SSascha Wildner dump_buffer(device_t dev, const char *msg, u_char *bytes, int len)
60*53a374c1SSascha Wildner {
61*53a374c1SSascha Wildner int i;
62*53a374c1SSascha Wildner
63*53a374c1SSascha Wildner device_printf(dev, "%s:", msg);
64*53a374c1SSascha Wildner for (i = 0; i < len; i++)
65*53a374c1SSascha Wildner kprintf(" %02x", bytes[i]);
66*53a374c1SSascha Wildner kprintf("\n");
67*53a374c1SSascha Wildner }
68*53a374c1SSascha Wildner #endif
69*53a374c1SSascha Wildner
70*53a374c1SSascha Wildner static int
ssif_polled_request(struct ipmi_softc * sc,struct ipmi_request * req)71*53a374c1SSascha Wildner ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
72*53a374c1SSascha Wildner {
73*53a374c1SSascha Wildner u_char ssif_buf[SMBUS_DATA_SIZE];
74*53a374c1SSascha Wildner device_t dev = sc->ipmi_dev;
75*53a374c1SSascha Wildner device_t smbus = sc->ipmi_ssif_smbus;
76*53a374c1SSascha Wildner u_char *cp, block, count, offset;
77*53a374c1SSascha Wildner size_t len;
78*53a374c1SSascha Wildner int error;
79*53a374c1SSascha Wildner
80*53a374c1SSascha Wildner /* Acquire the bus while we send the request. */
81*53a374c1SSascha Wildner if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
82*53a374c1SSascha Wildner return (0);
83*53a374c1SSascha Wildner
84*53a374c1SSascha Wildner /*
85*53a374c1SSascha Wildner * First, send out the request. Begin by filling out the first
86*53a374c1SSascha Wildner * packet which includes the NetFn/LUN and command.
87*53a374c1SSascha Wildner */
88*53a374c1SSascha Wildner ssif_buf[0] = req->ir_addr;
89*53a374c1SSascha Wildner ssif_buf[1] = req->ir_command;
90*53a374c1SSascha Wildner if (req->ir_requestlen > 0)
91*53a374c1SSascha Wildner bcopy(req->ir_request, &ssif_buf[2],
92*53a374c1SSascha Wildner min(req->ir_requestlen, SMBUS_DATA_SIZE - 2));
93*53a374c1SSascha Wildner
94*53a374c1SSascha Wildner /* Small requests are sent with a single command. */
95*53a374c1SSascha Wildner if (req->ir_requestlen <= 30) {
96*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
97*53a374c1SSascha Wildner dump_buffer(dev, "WRITE_SINGLE", ssif_buf,
98*53a374c1SSascha Wildner req->ir_requestlen + 2);
99*53a374c1SSascha Wildner #endif
100*53a374c1SSascha Wildner error = smbus_error(smbus_bwrite(smbus,
101*53a374c1SSascha Wildner sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE,
102*53a374c1SSascha Wildner req->ir_requestlen + 2, ssif_buf));
103*53a374c1SSascha Wildner if (error) {
104*53a374c1SSascha Wildner #ifdef SSIF_ERROR_DEBUG
105*53a374c1SSascha Wildner device_printf(dev, "SSIF: WRITE_SINGLE error %d\n",
106*53a374c1SSascha Wildner error);
107*53a374c1SSascha Wildner #endif
108*53a374c1SSascha Wildner goto fail;
109*53a374c1SSascha Wildner }
110*53a374c1SSascha Wildner } else {
111*53a374c1SSascha Wildner /* Longer requests are sent out in 32-byte messages. */
112*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
113*53a374c1SSascha Wildner dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE);
114*53a374c1SSascha Wildner #endif
115*53a374c1SSascha Wildner error = smbus_error(smbus_bwrite(smbus,
116*53a374c1SSascha Wildner sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START,
117*53a374c1SSascha Wildner SMBUS_DATA_SIZE, ssif_buf));
118*53a374c1SSascha Wildner if (error) {
119*53a374c1SSascha Wildner #ifdef SSIF_ERROR_DEBUG
120*53a374c1SSascha Wildner device_printf(dev, "SSIF: WRITE_START error %d\n",
121*53a374c1SSascha Wildner error);
122*53a374c1SSascha Wildner #endif
123*53a374c1SSascha Wildner goto fail;
124*53a374c1SSascha Wildner }
125*53a374c1SSascha Wildner
126*53a374c1SSascha Wildner len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2);
127*53a374c1SSascha Wildner cp = req->ir_request + (SMBUS_DATA_SIZE - 2);
128*53a374c1SSascha Wildner while (len > 0) {
129*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
130*53a374c1SSascha Wildner dump_buffer(dev, "WRITE_CONT", cp,
131*53a374c1SSascha Wildner min(len, SMBUS_DATA_SIZE));
132*53a374c1SSascha Wildner #endif
133*53a374c1SSascha Wildner error = smbus_error(smbus_bwrite(smbus,
134*53a374c1SSascha Wildner sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
135*53a374c1SSascha Wildner min(len, SMBUS_DATA_SIZE), cp));
136*53a374c1SSascha Wildner if (error) {
137*53a374c1SSascha Wildner #ifdef SSIF_ERROR_DEBUG
138*53a374c1SSascha Wildner device_printf(dev, "SSIF: WRITE_CONT error %d\n",
139*53a374c1SSascha Wildner error);
140*53a374c1SSascha Wildner #endif
141*53a374c1SSascha Wildner goto fail;
142*53a374c1SSascha Wildner }
143*53a374c1SSascha Wildner cp += SMBUS_DATA_SIZE;
144*53a374c1SSascha Wildner len -= SMBUS_DATA_SIZE;
145*53a374c1SSascha Wildner }
146*53a374c1SSascha Wildner
147*53a374c1SSascha Wildner /*
148*53a374c1SSascha Wildner * The final WRITE_CONT transaction has to have a non-zero
149*53a374c1SSascha Wildner * length that is also not SMBUS_DATA_SIZE. If our last
150*53a374c1SSascha Wildner * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE
151*53a374c1SSascha Wildner * bytes, then len will be 0, and we send an extra 0x00 byte
152*53a374c1SSascha Wildner * to terminate the transaction.
153*53a374c1SSascha Wildner */
154*53a374c1SSascha Wildner if (len == 0) {
155*53a374c1SSascha Wildner char c = 0;
156*53a374c1SSascha Wildner
157*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
158*53a374c1SSascha Wildner dump_buffer(dev, "WRITE_CONT", &c, 1);
159*53a374c1SSascha Wildner #endif
160*53a374c1SSascha Wildner error = smbus_error(smbus_bwrite(smbus,
161*53a374c1SSascha Wildner sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
162*53a374c1SSascha Wildner 1, &c));
163*53a374c1SSascha Wildner if (error) {
164*53a374c1SSascha Wildner #ifdef SSIF_ERROR_DEBUG
165*53a374c1SSascha Wildner device_printf(dev, "SSIF: WRITE_CONT error %d\n",
166*53a374c1SSascha Wildner error);
167*53a374c1SSascha Wildner #endif
168*53a374c1SSascha Wildner goto fail;
169*53a374c1SSascha Wildner }
170*53a374c1SSascha Wildner }
171*53a374c1SSascha Wildner }
172*53a374c1SSascha Wildner
173*53a374c1SSascha Wildner /* Release the bus. */
174*53a374c1SSascha Wildner smbus_release_bus(smbus, dev);
175*53a374c1SSascha Wildner
176*53a374c1SSascha Wildner /* Give the BMC 100ms to chew on the request. */
177*53a374c1SSascha Wildner tsleep(ssif_polled_request, 0, "ssifwt", hz / 10);
178*53a374c1SSascha Wildner
179*53a374c1SSascha Wildner /* Try to read the first packet. */
180*53a374c1SSascha Wildner read_start:
181*53a374c1SSascha Wildner if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
182*53a374c1SSascha Wildner return (0);
183*53a374c1SSascha Wildner count = SMBUS_DATA_SIZE;
184*53a374c1SSascha Wildner error = smbus_error(smbus_bread(smbus,
185*53a374c1SSascha Wildner sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf));
186*53a374c1SSascha Wildner if (error == ENXIO || error == EBUSY) {
187*53a374c1SSascha Wildner smbus_release_bus(smbus, dev);
188*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
189*53a374c1SSascha Wildner device_printf(dev, "SSIF: READ_START retry\n");
190*53a374c1SSascha Wildner #endif
191*53a374c1SSascha Wildner /* Give the BMC another 10ms. */
192*53a374c1SSascha Wildner tsleep(ssif_polled_request, 0, "ssifwt", hz / 100);
193*53a374c1SSascha Wildner goto read_start;
194*53a374c1SSascha Wildner }
195*53a374c1SSascha Wildner if (error) {
196*53a374c1SSascha Wildner #ifdef SSIF_ERROR_DEBUG
197*53a374c1SSascha Wildner device_printf(dev, "SSIF: READ_START failed: %d\n", error);
198*53a374c1SSascha Wildner #endif
199*53a374c1SSascha Wildner goto fail;
200*53a374c1SSascha Wildner }
201*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
202*53a374c1SSascha Wildner device_printf(dev, "SSIF: READ_START: ok\n");
203*53a374c1SSascha Wildner #endif
204*53a374c1SSascha Wildner
205*53a374c1SSascha Wildner /*
206*53a374c1SSascha Wildner * If this is the first part of a multi-part read, then we need to
207*53a374c1SSascha Wildner * skip the first two bytes.
208*53a374c1SSascha Wildner */
209*53a374c1SSascha Wildner if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1)
210*53a374c1SSascha Wildner offset = 2;
211*53a374c1SSascha Wildner else
212*53a374c1SSascha Wildner offset = 0;
213*53a374c1SSascha Wildner
214*53a374c1SSascha Wildner /* We had better get the reply header. */
215*53a374c1SSascha Wildner if (count < 3) {
216*53a374c1SSascha Wildner device_printf(dev, "SSIF: Short reply packet\n");
217*53a374c1SSascha Wildner goto fail;
218*53a374c1SSascha Wildner }
219*53a374c1SSascha Wildner
220*53a374c1SSascha Wildner /* Verify the NetFn/LUN. */
221*53a374c1SSascha Wildner if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) {
222*53a374c1SSascha Wildner device_printf(dev, "SSIF: Reply address mismatch\n");
223*53a374c1SSascha Wildner goto fail;
224*53a374c1SSascha Wildner }
225*53a374c1SSascha Wildner
226*53a374c1SSascha Wildner /* Verify the command. */
227*53a374c1SSascha Wildner if (ssif_buf[offset + 1] != req->ir_command) {
228*53a374c1SSascha Wildner device_printf(dev, "SMIC: Command mismatch\n");
229*53a374c1SSascha Wildner goto fail;
230*53a374c1SSascha Wildner }
231*53a374c1SSascha Wildner
232*53a374c1SSascha Wildner /* Read the completion code. */
233*53a374c1SSascha Wildner req->ir_compcode = ssif_buf[offset + 2];
234*53a374c1SSascha Wildner
235*53a374c1SSascha Wildner /* If this is a single read, just copy the data and return. */
236*53a374c1SSascha Wildner if (offset == 0) {
237*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
238*53a374c1SSascha Wildner dump_buffer(dev, "READ_SINGLE", ssif_buf, count);
239*53a374c1SSascha Wildner #endif
240*53a374c1SSascha Wildner len = count - 3;
241*53a374c1SSascha Wildner bcopy(&ssif_buf[3], req->ir_reply,
242*53a374c1SSascha Wildner min(req->ir_replybuflen, len));
243*53a374c1SSascha Wildner goto done;
244*53a374c1SSascha Wildner }
245*53a374c1SSascha Wildner
246*53a374c1SSascha Wildner /*
247*53a374c1SSascha Wildner * This is the first part of a multi-read transaction, so copy
248*53a374c1SSascha Wildner * out the payload and start looping.
249*53a374c1SSascha Wildner */
250*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
251*53a374c1SSascha Wildner dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2);
252*53a374c1SSascha Wildner #endif
253*53a374c1SSascha Wildner bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5));
254*53a374c1SSascha Wildner len = count - 5;
255*53a374c1SSascha Wildner block = 1;
256*53a374c1SSascha Wildner
257*53a374c1SSascha Wildner for (;;) {
258*53a374c1SSascha Wildner /* Read another packet via READ_CONT. */
259*53a374c1SSascha Wildner count = SMBUS_DATA_SIZE;
260*53a374c1SSascha Wildner error = smbus_error(smbus_bread(smbus,
261*53a374c1SSascha Wildner sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count,
262*53a374c1SSascha Wildner ssif_buf));
263*53a374c1SSascha Wildner if (error) {
264*53a374c1SSascha Wildner #ifdef SSIF_ERROR_DEBUG
265*53a374c1SSascha Wildner kprintf("SSIF: READ_CONT failed: %d\n", error);
266*53a374c1SSascha Wildner #endif
267*53a374c1SSascha Wildner goto fail;
268*53a374c1SSascha Wildner }
269*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
270*53a374c1SSascha Wildner device_printf(dev, "SSIF: READ_CONT... ok\n");
271*53a374c1SSascha Wildner #endif
272*53a374c1SSascha Wildner
273*53a374c1SSascha Wildner /* Verify the block number. 0xff marks the last block. */
274*53a374c1SSascha Wildner if (ssif_buf[0] != 0xff && ssif_buf[0] != block) {
275*53a374c1SSascha Wildner device_printf(dev, "SSIF: Read wrong block %d %d\n",
276*53a374c1SSascha Wildner ssif_buf[0], block);
277*53a374c1SSascha Wildner goto fail;
278*53a374c1SSascha Wildner }
279*53a374c1SSascha Wildner if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) {
280*53a374c1SSascha Wildner device_printf(dev,
281*53a374c1SSascha Wildner "SSIF: Read short middle block, length %d\n",
282*53a374c1SSascha Wildner count);
283*53a374c1SSascha Wildner goto fail;
284*53a374c1SSascha Wildner }
285*53a374c1SSascha Wildner #ifdef SSIF_DEBUG
286*53a374c1SSascha Wildner if (ssif_buf[0] == 0xff)
287*53a374c1SSascha Wildner dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1);
288*53a374c1SSascha Wildner else
289*53a374c1SSascha Wildner dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1);
290*53a374c1SSascha Wildner #endif
291*53a374c1SSascha Wildner if (len < req->ir_replybuflen)
292*53a374c1SSascha Wildner bcopy(&ssif_buf[1], &req->ir_reply[len],
293*53a374c1SSascha Wildner min(req->ir_replybuflen - len, count - 1));
294*53a374c1SSascha Wildner len += count - 1;
295*53a374c1SSascha Wildner
296*53a374c1SSascha Wildner /* If this was the last block we are done. */
297*53a374c1SSascha Wildner if (ssif_buf[0] != 0xff)
298*53a374c1SSascha Wildner break;
299*53a374c1SSascha Wildner block++;
300*53a374c1SSascha Wildner }
301*53a374c1SSascha Wildner
302*53a374c1SSascha Wildner done:
303*53a374c1SSascha Wildner /* Save the total length and return success. */
304*53a374c1SSascha Wildner req->ir_replylen = len;
305*53a374c1SSascha Wildner smbus_release_bus(smbus, dev);
306*53a374c1SSascha Wildner return (1);
307*53a374c1SSascha Wildner
308*53a374c1SSascha Wildner fail:
309*53a374c1SSascha Wildner smbus_release_bus(smbus, dev);
310*53a374c1SSascha Wildner return (0);
311*53a374c1SSascha Wildner }
312*53a374c1SSascha Wildner
313*53a374c1SSascha Wildner static void
ssif_loop(void * arg)314*53a374c1SSascha Wildner ssif_loop(void *arg)
315*53a374c1SSascha Wildner {
316*53a374c1SSascha Wildner struct ipmi_softc *sc = arg;
317*53a374c1SSascha Wildner struct ipmi_request *req;
318*53a374c1SSascha Wildner int i, ok;
319*53a374c1SSascha Wildner
320*53a374c1SSascha Wildner IPMI_LOCK(sc);
321*53a374c1SSascha Wildner while ((req = ipmi_dequeue_request(sc)) != NULL) {
322*53a374c1SSascha Wildner IPMI_UNLOCK(sc);
323*53a374c1SSascha Wildner ok = 0;
324*53a374c1SSascha Wildner for (i = 0; i < 5; i++) {
325*53a374c1SSascha Wildner ok = ssif_polled_request(sc, req);
326*53a374c1SSascha Wildner if (ok)
327*53a374c1SSascha Wildner break;
328*53a374c1SSascha Wildner
329*53a374c1SSascha Wildner /* Wait 60 ms between retries. */
330*53a374c1SSascha Wildner tsleep(ssif_loop, 0, "retry", 60 * hz / 1000);
331*53a374c1SSascha Wildner #ifdef SSIF_RETRY_DEBUG
332*53a374c1SSascha Wildner device_printf(sc->ipmi_dev,
333*53a374c1SSascha Wildner "SSIF: Retrying request (%d)\n", i + 1);
334*53a374c1SSascha Wildner #endif
335*53a374c1SSascha Wildner }
336*53a374c1SSascha Wildner if (ok)
337*53a374c1SSascha Wildner req->ir_error = 0;
338*53a374c1SSascha Wildner else
339*53a374c1SSascha Wildner req->ir_error = EIO;
340*53a374c1SSascha Wildner IPMI_LOCK(sc);
341*53a374c1SSascha Wildner ipmi_complete_request(sc, req);
342*53a374c1SSascha Wildner IPMI_UNLOCK(sc);
343*53a374c1SSascha Wildner
344*53a374c1SSascha Wildner /* Enforce 10ms between requests. */
345*53a374c1SSascha Wildner tsleep(ssif_loop, 0, "delay", hz / 100);
346*53a374c1SSascha Wildner
347*53a374c1SSascha Wildner IPMI_LOCK(sc);
348*53a374c1SSascha Wildner }
349*53a374c1SSascha Wildner IPMI_UNLOCK(sc);
350*53a374c1SSascha Wildner kthread_exit();
351*53a374c1SSascha Wildner }
352*53a374c1SSascha Wildner
353*53a374c1SSascha Wildner static int
ssif_startup(struct ipmi_softc * sc)354*53a374c1SSascha Wildner ssif_startup(struct ipmi_softc *sc)
355*53a374c1SSascha Wildner {
356*53a374c1SSascha Wildner
357*53a374c1SSascha Wildner return (kthread_create(ssif_loop, sc, &sc->ipmi_kthread,
358*53a374c1SSascha Wildner "%s: ssif", device_get_nameunit(sc->ipmi_dev)));
359*53a374c1SSascha Wildner }
360*53a374c1SSascha Wildner
361*53a374c1SSascha Wildner int
ipmi_ssif_attach(struct ipmi_softc * sc,device_t smbus,int smbus_address)362*53a374c1SSascha Wildner ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address)
363*53a374c1SSascha Wildner {
364*53a374c1SSascha Wildner
365*53a374c1SSascha Wildner /* Setup smbus address. */
366*53a374c1SSascha Wildner sc->ipmi_ssif_smbus = smbus;
367*53a374c1SSascha Wildner sc->ipmi_ssif_smbus_address = smbus_address;
368*53a374c1SSascha Wildner
369*53a374c1SSascha Wildner /* Setup function pointers. */
370*53a374c1SSascha Wildner sc->ipmi_startup = ssif_startup;
371*53a374c1SSascha Wildner sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
372*53a374c1SSascha Wildner
373*53a374c1SSascha Wildner return (0);
374*53a374c1SSascha Wildner }
375