]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ichsmb/ichsmb.c
This commit was generated by cvs2svn to compensate for changes in r156803,
[FreeBSD/FreeBSD.git] / sys / dev / ichsmb / ichsmb.c
1 /*-
2  * ichsmb.c
3  *
4  * Author: Archie Cobbs <archie@freebsd.org>
5  * Copyright (c) 2000 Whistle Communications, Inc.
6  * All rights reserved.
7  * 
8  * Subject to the following obligations and disclaimer of warranty, use and
9  * redistribution of this software, in source or object code forms, with or
10  * without modifications are expressly permitted by Whistle Communications;
11  * provided, however, that:
12  * 1. Any and all reproductions of the source or object code must include the
13  *    copyright notice above and the following disclaimer of warranties; and
14  * 2. No rights are granted, in any manner or form, to use Whistle
15  *    Communications, Inc. trademarks, including the mark "WHISTLE
16  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17  *    such appears in the above copyright notice or in the software.
18  * 
19  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35  * OF SUCH DAMAGE.
36  */
37
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 /*
42  * Support for the SMBus controller logical device which is part of the
43  * Intel 81801AA (ICH) and 81801AB (ICH0) I/O controller hub chips.
44  *
45  * This driver assumes that the generic SMBus code will ensure that
46  * at most one process at a time calls into the SMBus methods below.
47  */
48
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/kernel.h>
52 #include <sys/errno.h>
53 #include <sys/lock.h>
54 #include <sys/mutex.h>
55 #include <sys/syslog.h>
56 #include <sys/bus.h>
57
58 #include <machine/bus.h>
59 #include <sys/rman.h>
60 #include <machine/resource.h>
61
62 #include <dev/smbus/smbconf.h>
63
64 #include <dev/ichsmb/ichsmb_var.h>
65 #include <dev/ichsmb/ichsmb_reg.h>
66
67 /*
68  * Enable debugging by defining ICHSMB_DEBUG to a non-zero value.
69  */
70 #define ICHSMB_DEBUG    0
71 #if ICHSMB_DEBUG != 0 && defined(__CC_SUPPORTS___FUNC__)
72 #define DBG(fmt, args...)       \
73         do { log(LOG_DEBUG, "%s: " fmt, __func__ , ## args); } while (0)
74 #else
75 #define DBG(fmt, args...)       do { } while (0)
76 #endif
77
78 /*
79  * Our child device driver name
80  */
81 #define DRIVER_SMBUS    "smbus"
82
83 /*
84  * Internal functions
85  */
86 static int ichsmb_wait(sc_p sc);
87
88 /********************************************************************
89                 BUS-INDEPENDENT BUS METHODS
90 ********************************************************************/
91
92 /*
93  * Handle probe-time duties that are independent of the bus
94  * our device lives on.
95  */
96 int
97 ichsmb_probe(device_t dev)
98 {
99         return (BUS_PROBE_DEFAULT);
100 }
101
102 /*
103  * Handle attach-time duties that are independent of the bus
104  * our device lives on.
105  */
106 int
107 ichsmb_attach(device_t dev)
108 {
109         const sc_p sc = device_get_softc(dev);
110         int error;
111
112         /* Add child: an instance of the "smbus" device */
113         if ((sc->smb = device_add_child(dev, DRIVER_SMBUS, -1)) == NULL) {
114                 log(LOG_ERR, "%s: no \"%s\" child found\n",
115                     device_get_nameunit(dev), DRIVER_SMBUS);
116                 return (ENXIO);
117         }
118
119         /* Clear interrupt conditions */
120         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_STA, 0xff);
121
122         /* Add "smbus" child */
123         if ((error = bus_generic_attach(dev)) != 0) {
124                 log(LOG_ERR, "%s: failed to attach child: %d\n",
125                     device_get_nameunit(dev), error);
126                 return (ENXIO);
127         }
128
129         /* Create mutex */
130         mtx_init(&sc->mutex, device_get_nameunit(dev), "ichsmb", MTX_DEF);
131         return (0);
132 }
133
134 /********************************************************************
135                         SMBUS METHODS
136 ********************************************************************/
137
138 int 
139 ichsmb_callback(device_t dev, int index, caddr_t data)
140 {
141         int smb_error = 0;
142
143         DBG("index=%d how=%d\n", index, data ? *(int *)data : -1);
144         switch (index) {
145         case SMB_REQUEST_BUS:
146                 break;
147         case SMB_RELEASE_BUS:
148                 break;
149         default:
150                 smb_error = SMB_EABORT; /* XXX */
151                 break;
152         }
153         DBG("smb_error=%d\n", smb_error);
154         return (smb_error);
155 }
156
157 int
158 ichsmb_quick(device_t dev, u_char slave, int how)
159 {
160         const sc_p sc = device_get_softc(dev);
161         int smb_error;
162
163         DBG("slave=0x%02x how=%d\n", slave, how);
164         KASSERT(sc->ich_cmd == -1,
165             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
166         switch (how) {
167         case SMB_QREAD:
168         case SMB_QWRITE:
169                 mtx_lock(&sc->mutex);
170                 sc->ich_cmd = ICH_HST_CNT_SMB_CMD_QUICK;
171                 bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
172                     (slave << 1) | (how == SMB_QREAD ?
173                         ICH_XMIT_SLVA_READ : ICH_XMIT_SLVA_WRITE));
174                 bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
175                     ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
176                 smb_error = ichsmb_wait(sc);
177                 mtx_unlock(&sc->mutex);
178                 break;
179         default:
180                 smb_error = SMB_ENOTSUPP;
181         }
182         DBG("smb_error=%d\n", smb_error);
183         return (smb_error);
184 }
185
186 int
187 ichsmb_sendb(device_t dev, u_char slave, char byte)
188 {
189         const sc_p sc = device_get_softc(dev);
190         int smb_error;
191
192         DBG("slave=0x%02x byte=0x%02x\n", slave, (u_char)byte);
193         KASSERT(sc->ich_cmd == -1,
194             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
195         mtx_lock(&sc->mutex);
196         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
197         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
198             (slave << 1) | ICH_XMIT_SLVA_WRITE);
199         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, byte);
200         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
201             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
202         smb_error = ichsmb_wait(sc);
203         mtx_unlock(&sc->mutex);
204         DBG("smb_error=%d\n", smb_error);
205         return (smb_error);
206 }
207
208 int
209 ichsmb_recvb(device_t dev, u_char slave, char *byte)
210 {
211         const sc_p sc = device_get_softc(dev);
212         int smb_error;
213
214         DBG("slave=0x%02x\n", slave);
215         KASSERT(sc->ich_cmd == -1,
216             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
217         mtx_lock(&sc->mutex);
218         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
219         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
220             (slave << 1) | ICH_XMIT_SLVA_READ);
221         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
222             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
223         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
224                 *byte = bus_space_read_1(sc->io_bst, sc->io_bsh, ICH_D0);
225         mtx_unlock(&sc->mutex);
226         DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
227         return (smb_error);
228 }
229
230 int
231 ichsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
232 {
233         const sc_p sc = device_get_softc(dev);
234         int smb_error;
235
236         DBG("slave=0x%02x cmd=0x%02x byte=0x%02x\n",
237             slave, (u_char)cmd, (u_char)byte);
238         KASSERT(sc->ich_cmd == -1,
239             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
240         mtx_lock(&sc->mutex);
241         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
242         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
243             (slave << 1) | ICH_XMIT_SLVA_WRITE);
244         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, cmd);
245         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_D0, byte);
246         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
247             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
248         smb_error = ichsmb_wait(sc);
249         mtx_unlock(&sc->mutex);
250         DBG("smb_error=%d\n", smb_error);
251         return (smb_error);
252 }
253
254 int
255 ichsmb_writew(device_t dev, u_char slave, char cmd, short word)
256 {
257         const sc_p sc = device_get_softc(dev);
258         int smb_error;
259
260         DBG("slave=0x%02x cmd=0x%02x word=0x%04x\n",
261             slave, (u_char)cmd, (u_int16_t)word);
262         KASSERT(sc->ich_cmd == -1,
263             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
264         mtx_lock(&sc->mutex);
265         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
266         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
267             (slave << 1) | ICH_XMIT_SLVA_WRITE);
268         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, cmd);
269         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_D0, word & 0xff);
270         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_D1, word >> 8);
271         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
272             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
273         smb_error = ichsmb_wait(sc);
274         mtx_unlock(&sc->mutex);
275         DBG("smb_error=%d\n", smb_error);
276         return (smb_error);
277 }
278
279 int
280 ichsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
281 {
282         const sc_p sc = device_get_softc(dev);
283         int smb_error;
284
285         DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
286         KASSERT(sc->ich_cmd == -1,
287             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
288         mtx_lock(&sc->mutex);
289         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
290         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
291             (slave << 1) | ICH_XMIT_SLVA_READ);
292         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, cmd);
293         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
294             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
295         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
296                 *byte = bus_space_read_1(sc->io_bst, sc->io_bsh, ICH_D0);
297         mtx_unlock(&sc->mutex);
298         DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
299         return (smb_error);
300 }
301
302 int
303 ichsmb_readw(device_t dev, u_char slave, char cmd, short *word)
304 {
305         const sc_p sc = device_get_softc(dev);
306         int smb_error;
307
308         DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
309         KASSERT(sc->ich_cmd == -1,
310             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
311         mtx_lock(&sc->mutex);
312         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
313         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
314             (slave << 1) | ICH_XMIT_SLVA_READ);
315         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, cmd);
316         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
317             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
318         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
319                 *word = (bus_space_read_1(sc->io_bst,
320                         sc->io_bsh, ICH_D0) & 0xff)
321                   | (bus_space_read_1(sc->io_bst,
322                         sc->io_bsh, ICH_D1) << 8);
323         }
324         mtx_unlock(&sc->mutex);
325         DBG("smb_error=%d word=0x%04x\n", smb_error, (u_int16_t)*word);
326         return (smb_error);
327 }
328
329 int
330 ichsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
331 {
332         const sc_p sc = device_get_softc(dev);
333         int smb_error;
334
335         DBG("slave=0x%02x cmd=0x%02x sdata=0x%04x\n",
336             slave, (u_char)cmd, (u_int16_t)sdata);
337         KASSERT(sc->ich_cmd == -1,
338             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
339         mtx_lock(&sc->mutex);
340         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_PROC_CALL;
341         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
342             (slave << 1) | ICH_XMIT_SLVA_WRITE);
343         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, cmd);
344         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_D0, sdata & 0xff);
345         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_D1, sdata >> 8);
346         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
347             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
348         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
349                 *rdata = (bus_space_read_1(sc->io_bst,
350                         sc->io_bsh, ICH_D0) & 0xff)
351                   | (bus_space_read_1(sc->io_bst,
352                         sc->io_bsh, ICH_D1) << 8);
353         }
354         mtx_unlock(&sc->mutex);
355         DBG("smb_error=%d rdata=0x%04x\n", smb_error, (u_int16_t)*rdata);
356         return (smb_error);
357 }
358
359 int
360 ichsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
361 {
362         const sc_p sc = device_get_softc(dev);
363         int smb_error;
364
365         DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
366 #if ICHSMB_DEBUG
367 #define DISP(ch)        (((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
368         {
369             u_char *p;
370
371             for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
372                 DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
373                     "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
374                     p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
375                     DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]), 
376                     DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
377             }
378         }
379 #undef DISP
380 #endif
381         KASSERT(sc->ich_cmd == -1,
382             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
383         if (count < 1 || count > 32)
384                 return (EINVAL);
385         bcopy(buf, sc->block_data, count);
386         sc->block_count = count;
387         sc->block_index = 1;
388         sc->block_write = 1;
389
390         mtx_lock(&sc->mutex);
391         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
392         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
393             (slave << 1) | ICH_XMIT_SLVA_WRITE);
394         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, cmd);
395         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_D0, count);
396         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_BLOCK_DB, buf[0]);
397         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
398             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
399         smb_error = ichsmb_wait(sc);
400         mtx_unlock(&sc->mutex);
401         DBG("smb_error=%d\n", smb_error);
402         return (smb_error);
403 }
404
405 int
406 ichsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
407 {
408         const sc_p sc = device_get_softc(dev);
409         int smb_error;
410
411         DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
412         KASSERT(sc->ich_cmd == -1,
413             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
414         if (count < 1 || count > 32)
415                 return (EINVAL);
416         bzero(sc->block_data, sizeof(sc->block_data));
417         sc->block_count = count;
418         sc->block_index = 0;
419         sc->block_write = 0;
420
421         mtx_lock(&sc->mutex);
422         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
423         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_XMIT_SLVA,
424             (slave << 1) | ICH_XMIT_SLVA_READ);
425         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CMD, cmd);
426         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_D0, count); /* XXX? */
427         bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_CNT,
428             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
429         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
430                 bcopy(sc->block_data, buf, sc->block_count);
431         mtx_unlock(&sc->mutex);
432         DBG("smb_error=%d\n", smb_error);
433 #if ICHSMB_DEBUG
434 #define DISP(ch)        (((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
435         {
436             u_char *p;
437
438             for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
439                 DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
440                     "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
441                     p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
442                     DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]), 
443                     DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
444             }
445         }
446 #undef DISP
447 #endif
448         return (smb_error);
449 }
450
451 /********************************************************************
452                         OTHER FUNCTIONS
453 ********************************************************************/
454
455 /*
456  * This table describes what interrupts we should ever expect to
457  * see after each ICH command, not including the SMBALERT interrupt.
458  */
459 static const u_int8_t ichsmb_state_irqs[] = {
460         /* quick */
461         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
462         /* byte */
463         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
464         /* byte data */
465         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
466         /* word data */
467         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
468         /* process call */
469         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
470         /* block */
471         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
472             | ICH_HST_STA_BYTE_DONE_STS),
473         /* i2c read (not used) */
474         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
475             | ICH_HST_STA_BYTE_DONE_STS)
476 };
477
478 /*
479  * Interrupt handler. This handler is bus-independent. Note that our
480  * interrupt may be shared, so we must handle "false" interrupts.
481  */
482 void
483 ichsmb_device_intr(void *cookie)
484 {
485         const sc_p sc = cookie;
486         const device_t dev = sc->dev;
487         const int maxloops = 16;
488         u_int8_t status;
489         u_int8_t ok_bits;
490         int cmd_index;
491         int count;
492
493         mtx_lock(&sc->mutex);
494         for (count = 0; count < maxloops; count++) {
495
496                 /* Get and reset status bits */
497                 status = bus_space_read_1(sc->io_bst, sc->io_bsh, ICH_HST_STA);
498 #if ICHSMB_DEBUG
499                 if ((status & ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY))
500                     || count > 0) {
501                         DBG("%d stat=0x%02x\n", count, status);
502                 }
503 #endif
504                 status &= ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY);
505                 if (status == 0)
506                         break;
507
508                 /* Check for unexpected interrupt */
509                 ok_bits = ICH_HST_STA_SMBALERT_STS;
510                 cmd_index = sc->ich_cmd >> 2;
511                 if (sc->ich_cmd != -1) {
512                         KASSERT(cmd_index < sizeof(ichsmb_state_irqs),
513                             ("%s: ich_cmd=%d", device_get_nameunit(dev),
514                             sc->ich_cmd));
515                         ok_bits |= ichsmb_state_irqs[cmd_index];
516                 }
517                 if ((status & ~ok_bits) != 0) {
518                         log(LOG_ERR, "%s: irq 0x%02x during %d\n",
519                             device_get_nameunit(dev), status, cmd_index);
520                         bus_space_write_1(sc->io_bst, sc->io_bsh,
521                             ICH_HST_STA, (status & ~ok_bits));
522                         continue;
523                 }
524
525                 /* Handle SMBALERT interrupt */
526                 if (status & ICH_HST_STA_SMBALERT_STS) {
527                         static int smbalert_count = 16;
528                         if (smbalert_count > 0) {
529                                 log(LOG_WARNING, "%s: SMBALERT# rec'd\n",
530                                     device_get_nameunit(dev));
531                                 if (--smbalert_count == 0) {
532                                         log(LOG_WARNING,
533                                             "%s: not logging anymore\n",
534                                             device_get_nameunit(dev));
535                                 }
536                         }
537                 }
538
539                 /* Check for bus error */
540                 if (status & ICH_HST_STA_BUS_ERR) {
541                         sc->smb_error = SMB_ECOLLI;     /* XXX SMB_EBUSERR? */
542                         goto finished;
543                 }
544
545                 /* Check for device error */
546                 if (status & ICH_HST_STA_DEV_ERR) {
547                         sc->smb_error = SMB_ENOACK;     /* or SMB_ETIMEOUT? */
548                         goto finished;
549                 }
550
551                 /* Check for byte completion in block transfer */
552                 if (status & ICH_HST_STA_BYTE_DONE_STS) {
553                         if (sc->block_write) {
554                                 if (sc->block_index < sc->block_count) {
555
556                                         /* Write next byte */
557                                         bus_space_write_1(sc->io_bst,
558                                             sc->io_bsh, ICH_BLOCK_DB,
559                                             sc->block_data[sc->block_index++]);
560                                 }
561                         } else {
562
563                                 /* First interrupt, get the count also */
564                                 if (sc->block_index == 0) {
565                                         sc->block_count = bus_space_read_1(
566                                             sc->io_bst, sc->io_bsh, ICH_D0);
567                                 }
568
569                                 /* Get next byte, if any */
570                                 if (sc->block_index < sc->block_count) {
571
572                                         /* Read next byte */
573                                         sc->block_data[sc->block_index++] =
574                                             bus_space_read_1(sc->io_bst,
575                                               sc->io_bsh, ICH_BLOCK_DB);
576
577                                         /* Set "LAST_BYTE" bit before reading
578                                            the last byte of block data */
579                                         if (sc->block_index
580                                             >= sc->block_count - 1) {
581                                                 bus_space_write_1(sc->io_bst,
582                                                     sc->io_bsh, ICH_HST_CNT,
583                                                     ICH_HST_CNT_LAST_BYTE
584                                                         | ICH_HST_CNT_INTREN
585                                                         | sc->ich_cmd);
586                                         }
587                                 }
588                         }
589                 }
590
591                 /* Check command completion */
592                 if (status & ICH_HST_STA_INTR) {
593                         sc->smb_error = SMB_ENOERR;
594 finished:
595                         sc->ich_cmd = -1;
596                         bus_space_write_1(sc->io_bst, sc->io_bsh,
597                             ICH_HST_STA, status);
598                         wakeup(sc);
599                         break;
600                 }
601
602                 /* Clear status bits and try again */
603                 bus_space_write_1(sc->io_bst, sc->io_bsh, ICH_HST_STA, status);
604         }
605         mtx_unlock(&sc->mutex);
606
607         /* Too many loops? */
608         if (count == maxloops) {
609                 log(LOG_ERR, "%s: interrupt loop, status=0x%02x\n",
610                     device_get_nameunit(dev),
611                     bus_space_read_1(sc->io_bst, sc->io_bsh, ICH_HST_STA));
612         }
613 }
614
615 /*
616  * Wait for command completion. Assumes mutex is held.
617  * Returns an SMB_* error code.
618  */
619 static int
620 ichsmb_wait(sc_p sc)
621 {
622         const device_t dev = sc->dev;
623         int error, smb_error;
624
625         KASSERT(sc->ich_cmd != -1,
626             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
627         mtx_assert(&sc->mutex, MA_OWNED);
628         error = msleep(sc, &sc->mutex, PZERO, "ichsmb", hz / 4);
629         DBG("msleep -> %d\n", error);
630         switch (error) {
631         case 0:
632                 smb_error = sc->smb_error;
633                 break;
634         case EWOULDBLOCK:
635                 log(LOG_ERR, "%s: device timeout, status=0x%02x\n",
636                     device_get_nameunit(dev),
637                     bus_space_read_1(sc->io_bst, sc->io_bsh, ICH_HST_STA));
638                 sc->ich_cmd = -1;
639                 smb_error = SMB_ETIMEOUT;
640                 break;
641         default:
642                 smb_error = SMB_EABORT;
643                 break;
644         }
645         return (smb_error);
646 }
647
648 /*
649  * Release resources associated with device.
650  */
651 void
652 ichsmb_release_resources(sc_p sc)
653 {
654         const device_t dev = sc->dev;
655
656         if (sc->irq_handle != NULL) {
657                 bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
658                 sc->irq_handle = NULL;
659         }
660         if (sc->irq_res != NULL) {
661                 bus_release_resource(dev,
662                     SYS_RES_IRQ, sc->irq_rid, sc->irq_res);
663                 sc->irq_res = NULL;
664         }
665         if (sc->io_res != NULL) {
666                 bus_release_resource(dev,
667                     SYS_RES_IOPORT, sc->io_rid, sc->io_res);
668                 sc->io_res = NULL;
669         }
670 }
671
672 int ichsmb_detach(device_t dev)
673 {
674         const sc_p sc = device_get_softc(dev);
675
676         mtx_destroy(&sc->mutex);
677         bus_generic_detach(dev);
678         device_delete_child(dev, sc->smb);
679         ichsmb_release_resources(sc);
680         
681         return 0;
682 }