]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ichsmb/ichsmb.c
dts: Update our copy to be in sync with Linux 5.7
[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/module.h>
55 #include <sys/mutex.h>
56 #include <sys/syslog.h>
57 #include <sys/bus.h>
58
59 #include <machine/bus.h>
60 #include <sys/rman.h>
61 #include <machine/resource.h>
62
63 #include <dev/smbus/smbconf.h>
64
65 #include <dev/ichsmb/ichsmb_var.h>
66 #include <dev/ichsmb/ichsmb_reg.h>
67
68 /*
69  * Enable debugging by defining ICHSMB_DEBUG to a non-zero value.
70  */
71 #define ICHSMB_DEBUG    0
72 #if ICHSMB_DEBUG != 0 && defined(__CC_SUPPORTS___FUNC__)
73 #define DBG(fmt, args...)       \
74         do { printf("%s: " fmt, __func__ , ## args); } while (0)
75 #else
76 #define DBG(fmt, args...)       do { } while (0)
77 #endif
78
79 /*
80  * Our child device driver name
81  */
82 #define DRIVER_SMBUS    "smbus"
83
84 /*
85  * Internal functions
86  */
87 static int ichsmb_wait(sc_p sc);
88
89 /********************************************************************
90                 BUS-INDEPENDENT BUS METHODS
91 ********************************************************************/
92
93 /*
94  * Handle probe-time duties that are independent of the bus
95  * our device lives on.
96  */
97 int
98 ichsmb_probe(device_t dev)
99 {
100         return (BUS_PROBE_DEFAULT);
101 }
102
103 /*
104  * Handle attach-time duties that are independent of the bus
105  * our device lives on.
106  */
107 int
108 ichsmb_attach(device_t dev)
109 {
110         const sc_p sc = device_get_softc(dev);
111         int error;
112
113         /* Create mutex */
114         mtx_init(&sc->mutex, device_get_nameunit(dev), "ichsmb", MTX_DEF);
115
116         /* Add child: an instance of the "smbus" device */
117         if ((sc->smb = device_add_child(dev, DRIVER_SMBUS, -1)) == NULL) {
118                 device_printf(dev, "no \"%s\" child found\n", DRIVER_SMBUS);
119                 error = ENXIO;
120                 goto fail;
121         }
122
123         /* Clear interrupt conditions */
124         bus_write_1(sc->io_res, ICH_HST_STA, 0xff);
125
126         /* Set up interrupt handler */
127         error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
128             NULL, ichsmb_device_intr, sc, &sc->irq_handle);
129         if (error != 0) {
130                 device_printf(dev, "can't setup irq\n");
131                 goto fail;
132         }
133
134         /* Attach children when interrupts are available */
135         return (bus_delayed_attach_children(dev));
136 fail:
137         mtx_destroy(&sc->mutex);
138         return (error);
139 }
140
141 /********************************************************************
142                         SMBUS METHODS
143 ********************************************************************/
144
145 int 
146 ichsmb_callback(device_t dev, int index, void *data)
147 {
148         int smb_error = 0;
149
150         DBG("index=%d how=%d\n", index, data ? *(int *)data : -1);
151         switch (index) {
152         case SMB_REQUEST_BUS:
153                 break;
154         case SMB_RELEASE_BUS:
155                 break;
156         default:
157                 smb_error = SMB_EABORT; /* XXX */
158                 break;
159         }
160         DBG("smb_error=%d\n", smb_error);
161         return (smb_error);
162 }
163
164 int
165 ichsmb_quick(device_t dev, u_char slave, int how)
166 {
167         const sc_p sc = device_get_softc(dev);
168         int smb_error;
169
170         DBG("slave=0x%02x how=%d\n", slave, how);
171         KASSERT(sc->ich_cmd == -1,
172             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
173         switch (how) {
174         case SMB_QREAD:
175         case SMB_QWRITE:
176                 mtx_lock(&sc->mutex);
177                 sc->ich_cmd = ICH_HST_CNT_SMB_CMD_QUICK;
178                 bus_write_1(sc->io_res, ICH_XMIT_SLVA,
179                     slave | (how == SMB_QREAD ?
180                         ICH_XMIT_SLVA_READ : ICH_XMIT_SLVA_WRITE));
181                 bus_write_1(sc->io_res, ICH_HST_CNT,
182                     ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
183                 smb_error = ichsmb_wait(sc);
184                 mtx_unlock(&sc->mutex);
185                 break;
186         default:
187                 smb_error = SMB_ENOTSUPP;
188         }
189         DBG("smb_error=%d\n", smb_error);
190         return (smb_error);
191 }
192
193 int
194 ichsmb_sendb(device_t dev, u_char slave, char byte)
195 {
196         const sc_p sc = device_get_softc(dev);
197         int smb_error;
198
199         DBG("slave=0x%02x byte=0x%02x\n", slave, (u_char)byte);
200         KASSERT(sc->ich_cmd == -1,
201             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
202         mtx_lock(&sc->mutex);
203         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
204         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
205             slave | ICH_XMIT_SLVA_WRITE);
206         bus_write_1(sc->io_res, ICH_HST_CMD, byte);
207         bus_write_1(sc->io_res, ICH_HST_CNT,
208             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
209         smb_error = ichsmb_wait(sc);
210         mtx_unlock(&sc->mutex);
211         DBG("smb_error=%d\n", smb_error);
212         return (smb_error);
213 }
214
215 int
216 ichsmb_recvb(device_t dev, u_char slave, char *byte)
217 {
218         const sc_p sc = device_get_softc(dev);
219         int smb_error;
220
221         DBG("slave=0x%02x\n", slave);
222         KASSERT(sc->ich_cmd == -1,
223             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
224         mtx_lock(&sc->mutex);
225         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
226         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
227             slave | ICH_XMIT_SLVA_READ);
228         bus_write_1(sc->io_res, ICH_HST_CNT,
229             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
230         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
231                 *byte = bus_read_1(sc->io_res, ICH_D0);
232         mtx_unlock(&sc->mutex);
233         DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
234         return (smb_error);
235 }
236
237 int
238 ichsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
239 {
240         const sc_p sc = device_get_softc(dev);
241         int smb_error;
242
243         DBG("slave=0x%02x cmd=0x%02x byte=0x%02x\n",
244             slave, (u_char)cmd, (u_char)byte);
245         KASSERT(sc->ich_cmd == -1,
246             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
247         mtx_lock(&sc->mutex);
248         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
249         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
250             slave | ICH_XMIT_SLVA_WRITE);
251         bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
252         bus_write_1(sc->io_res, ICH_D0, byte);
253         bus_write_1(sc->io_res, ICH_HST_CNT,
254             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
255         smb_error = ichsmb_wait(sc);
256         mtx_unlock(&sc->mutex);
257         DBG("smb_error=%d\n", smb_error);
258         return (smb_error);
259 }
260
261 int
262 ichsmb_writew(device_t dev, u_char slave, char cmd, short word)
263 {
264         const sc_p sc = device_get_softc(dev);
265         int smb_error;
266
267         DBG("slave=0x%02x cmd=0x%02x word=0x%04x\n",
268             slave, (u_char)cmd, (u_int16_t)word);
269         KASSERT(sc->ich_cmd == -1,
270             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
271         mtx_lock(&sc->mutex);
272         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
273         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
274             slave | ICH_XMIT_SLVA_WRITE);
275         bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
276         bus_write_1(sc->io_res, ICH_D0, word & 0xff);
277         bus_write_1(sc->io_res, ICH_D1, word >> 8);
278         bus_write_1(sc->io_res, ICH_HST_CNT,
279             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
280         smb_error = ichsmb_wait(sc);
281         mtx_unlock(&sc->mutex);
282         DBG("smb_error=%d\n", smb_error);
283         return (smb_error);
284 }
285
286 int
287 ichsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
288 {
289         const sc_p sc = device_get_softc(dev);
290         int smb_error;
291
292         DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
293         KASSERT(sc->ich_cmd == -1,
294             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
295         mtx_lock(&sc->mutex);
296         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
297         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
298             slave | ICH_XMIT_SLVA_READ);
299         bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
300         bus_write_1(sc->io_res, ICH_HST_CNT,
301             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
302         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
303                 *byte = bus_read_1(sc->io_res, ICH_D0);
304         mtx_unlock(&sc->mutex);
305         DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
306         return (smb_error);
307 }
308
309 int
310 ichsmb_readw(device_t dev, u_char slave, char cmd, short *word)
311 {
312         const sc_p sc = device_get_softc(dev);
313         int smb_error;
314
315         DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
316         KASSERT(sc->ich_cmd == -1,
317             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
318         mtx_lock(&sc->mutex);
319         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
320         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
321             slave | ICH_XMIT_SLVA_READ);
322         bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
323         bus_write_1(sc->io_res, ICH_HST_CNT,
324             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
325         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
326                 *word = (bus_read_1(sc->io_res,
327                         ICH_D0) & 0xff)
328                   | (bus_read_1(sc->io_res,
329                         ICH_D1) << 8);
330         }
331         mtx_unlock(&sc->mutex);
332         DBG("smb_error=%d word=0x%04x\n", smb_error, (u_int16_t)*word);
333         return (smb_error);
334 }
335
336 int
337 ichsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
338 {
339         const sc_p sc = device_get_softc(dev);
340         int smb_error;
341
342         DBG("slave=0x%02x cmd=0x%02x sdata=0x%04x\n",
343             slave, (u_char)cmd, (u_int16_t)sdata);
344         KASSERT(sc->ich_cmd == -1,
345             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
346         mtx_lock(&sc->mutex);
347         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_PROC_CALL;
348         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
349             slave | ICH_XMIT_SLVA_WRITE);
350         bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
351         bus_write_1(sc->io_res, ICH_D0, sdata & 0xff);
352         bus_write_1(sc->io_res, ICH_D1, sdata >> 8);
353         bus_write_1(sc->io_res, ICH_HST_CNT,
354             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
355         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
356                 *rdata = (bus_read_1(sc->io_res,
357                         ICH_D0) & 0xff)
358                   | (bus_read_1(sc->io_res,
359                         ICH_D1) << 8);
360         }
361         mtx_unlock(&sc->mutex);
362         DBG("smb_error=%d rdata=0x%04x\n", smb_error, (u_int16_t)*rdata);
363         return (smb_error);
364 }
365
366 int
367 ichsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
368 {
369         const sc_p sc = device_get_softc(dev);
370         int smb_error;
371
372         DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
373 #if ICHSMB_DEBUG
374 #define DISP(ch)        (((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
375         {
376             u_char *p;
377
378             for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
379                 DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
380                     "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
381                     p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
382                     DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]), 
383                     DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
384             }
385         }
386 #undef DISP
387 #endif
388         KASSERT(sc->ich_cmd == -1,
389             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
390         if (count < 1 || count > 32)
391                 return (SMB_EINVAL);
392         bcopy(buf, sc->block_data, count);
393         sc->block_count = count;
394         sc->block_index = 1;
395         sc->block_write = 1;
396
397         mtx_lock(&sc->mutex);
398         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
399         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
400             slave | ICH_XMIT_SLVA_WRITE);
401         bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
402         bus_write_1(sc->io_res, ICH_D0, count);
403         bus_write_1(sc->io_res, ICH_BLOCK_DB, buf[0]);
404         bus_write_1(sc->io_res, ICH_HST_CNT,
405             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
406         smb_error = ichsmb_wait(sc);
407         mtx_unlock(&sc->mutex);
408         DBG("smb_error=%d\n", smb_error);
409         return (smb_error);
410 }
411
412 int
413 ichsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
414 {
415         const sc_p sc = device_get_softc(dev);
416         int smb_error;
417
418         DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
419         KASSERT(sc->ich_cmd == -1,
420             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
421         if (*count < 1 || *count > 32)
422                 return (SMB_EINVAL);
423         bzero(sc->block_data, sizeof(sc->block_data));
424         sc->block_count = 0;
425         sc->block_index = 0;
426         sc->block_write = 0;
427
428         mtx_lock(&sc->mutex);
429         sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
430         bus_write_1(sc->io_res, ICH_XMIT_SLVA,
431             slave | ICH_XMIT_SLVA_READ);
432         bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
433         bus_write_1(sc->io_res, ICH_D0, *count); /* XXX? */
434         bus_write_1(sc->io_res, ICH_HST_CNT,
435             ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
436         if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
437                 bcopy(sc->block_data, buf, min(sc->block_count, *count));
438                 *count = sc->block_count;
439         }
440         mtx_unlock(&sc->mutex);
441         DBG("smb_error=%d\n", smb_error);
442 #if ICHSMB_DEBUG
443 #define DISP(ch)        (((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
444         {
445             u_char *p;
446
447             for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
448                 DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
449                     "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
450                     p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
451                     DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]), 
452                     DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
453             }
454         }
455 #undef DISP
456 #endif
457         return (smb_error);
458 }
459
460 /********************************************************************
461                         OTHER FUNCTIONS
462 ********************************************************************/
463
464 /*
465  * This table describes what interrupts we should ever expect to
466  * see after each ICH command, not including the SMBALERT interrupt.
467  */
468 static const u_int8_t ichsmb_state_irqs[] = {
469         /* quick */
470         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
471         /* byte */
472         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
473         /* byte data */
474         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
475         /* word data */
476         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
477         /* process call */
478         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
479         /* block */
480         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
481             | ICH_HST_STA_BYTE_DONE_STS),
482         /* i2c read (not used) */
483         (ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
484             | ICH_HST_STA_BYTE_DONE_STS)
485 };
486
487 /*
488  * Interrupt handler. This handler is bus-independent. Note that our
489  * interrupt may be shared, so we must handle "false" interrupts.
490  */
491 void
492 ichsmb_device_intr(void *cookie)
493 {
494         const sc_p sc = cookie;
495         const device_t dev = sc->dev;
496         const int maxloops = 16;
497         u_int8_t status;
498         u_int8_t ok_bits;
499         int cmd_index;
500         int count;
501
502         mtx_lock(&sc->mutex);
503         for (count = 0; count < maxloops; count++) {
504
505                 /* Get and reset status bits */
506                 status = bus_read_1(sc->io_res, ICH_HST_STA);
507 #if ICHSMB_DEBUG
508                 if ((status & ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY))
509                     || count > 0) {
510                         DBG("%d stat=0x%02x\n", count, status);
511                 }
512 #endif
513                 status &= ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY);
514                 if (status == 0)
515                         break;
516
517                 /* Check for unexpected interrupt */
518                 ok_bits = ICH_HST_STA_SMBALERT_STS;
519                 cmd_index = sc->ich_cmd >> 2;
520                 if (sc->ich_cmd != -1) {
521                         KASSERT(cmd_index < sizeof(ichsmb_state_irqs),
522                             ("%s: ich_cmd=%d", device_get_nameunit(dev),
523                             sc->ich_cmd));
524                         ok_bits |= ichsmb_state_irqs[cmd_index];
525                 }
526                 if ((status & ~ok_bits) != 0) {
527                         device_printf(dev, "irq 0x%02x during %d\n", status,
528                             cmd_index);
529                         bus_write_1(sc->io_res,
530                             ICH_HST_STA, (status & ~ok_bits));
531                         continue;
532                 }
533
534                 /* Handle SMBALERT interrupt */
535                 if (status & ICH_HST_STA_SMBALERT_STS) {
536                         static int smbalert_count = 16;
537                         if (smbalert_count > 0) {
538                                 device_printf(dev, "SMBALERT# rec'd\n");
539                                 if (--smbalert_count == 0) {
540                                         device_printf(dev,
541                                             "not logging anymore\n");
542                                 }
543                         }
544                 }
545
546                 /* Check for bus error */
547                 if (status & ICH_HST_STA_BUS_ERR) {
548                         sc->smb_error = SMB_ECOLLI;     /* XXX SMB_EBUSERR? */
549                         goto finished;
550                 }
551
552                 /* Check for device error */
553                 if (status & ICH_HST_STA_DEV_ERR) {
554                         sc->smb_error = SMB_ENOACK;     /* or SMB_ETIMEOUT? */
555                         goto finished;
556                 }
557
558                 /* Check for byte completion in block transfer */
559                 if (status & ICH_HST_STA_BYTE_DONE_STS) {
560                         if (sc->block_write) {
561                                 if (sc->block_index < sc->block_count) {
562
563                                         /* Write next byte */
564                                         bus_write_1(sc->io_res,
565                                             ICH_BLOCK_DB,
566                                             sc->block_data[sc->block_index++]);
567                                 }
568                         } else {
569
570                                 /* First interrupt, get the count also */
571                                 if (sc->block_index == 0) {
572                                         sc->block_count = bus_read_1(
573                                             sc->io_res, ICH_D0);
574                                 }
575
576                                 /* Get next byte, if any */
577                                 if (sc->block_index < sc->block_count) {
578
579                                         /* Read next byte */
580                                         sc->block_data[sc->block_index++] =
581                                             bus_read_1(sc->io_res,
582                                               ICH_BLOCK_DB);
583
584                                         /* Set "LAST_BYTE" bit before reading
585                                            the last byte of block data */
586                                         if (sc->block_index
587                                             >= sc->block_count - 1) {
588                                                 bus_write_1(sc->io_res,
589                                                     ICH_HST_CNT,
590                                                     ICH_HST_CNT_LAST_BYTE
591                                                         | ICH_HST_CNT_INTREN
592                                                         | sc->ich_cmd);
593                                         }
594                                 }
595                         }
596                 }
597
598                 /* Check command completion */
599                 if (status & ICH_HST_STA_INTR) {
600                         sc->smb_error = SMB_ENOERR;
601 finished:
602                         sc->ich_cmd = -1;
603                         bus_write_1(sc->io_res,
604                             ICH_HST_STA, status);
605                         wakeup(sc);
606                         break;
607                 }
608
609                 /* Clear status bits and try again */
610                 bus_write_1(sc->io_res, ICH_HST_STA, status);
611         }
612         mtx_unlock(&sc->mutex);
613
614         /* Too many loops? */
615         if (count == maxloops) {
616                 device_printf(dev, "interrupt loop, status=0x%02x\n",
617                     bus_read_1(sc->io_res, ICH_HST_STA));
618         }
619 }
620
621 /*
622  * Wait for command completion. Assumes mutex is held.
623  * Returns an SMB_* error code.
624  */
625 static int
626 ichsmb_wait(sc_p sc)
627 {
628         const device_t dev = sc->dev;
629         int error, smb_error;
630
631         KASSERT(sc->ich_cmd != -1,
632             ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
633         mtx_assert(&sc->mutex, MA_OWNED);
634         error = msleep(sc, &sc->mutex, PZERO, "ichsmb", hz / 4);
635         DBG("msleep -> %d\n", error);
636         switch (error) {
637         case 0:
638                 smb_error = sc->smb_error;
639                 break;
640         case EWOULDBLOCK:
641                 device_printf(dev, "device timeout, status=0x%02x\n",
642                     bus_read_1(sc->io_res, ICH_HST_STA));
643                 sc->ich_cmd = -1;
644                 smb_error = SMB_ETIMEOUT;
645                 break;
646         default:
647                 smb_error = SMB_EABORT;
648                 break;
649         }
650         return (smb_error);
651 }
652
653 /*
654  * Release resources associated with device.
655  */
656 void
657 ichsmb_release_resources(sc_p sc)
658 {
659         const device_t dev = sc->dev;
660
661         if (sc->irq_handle != NULL) {
662                 bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
663                 sc->irq_handle = NULL;
664         }
665         if (sc->irq_res != NULL) {
666                 bus_release_resource(dev,
667                     SYS_RES_IRQ, sc->irq_rid, sc->irq_res);
668                 sc->irq_res = NULL;
669         }
670         if (sc->io_res != NULL) {
671                 bus_release_resource(dev,
672                     SYS_RES_IOPORT, sc->io_rid, sc->io_res);
673                 sc->io_res = NULL;
674         }
675 }
676
677 int
678 ichsmb_detach(device_t dev)
679 {
680         const sc_p sc = device_get_softc(dev);
681         int error;
682
683         error = bus_generic_detach(dev);
684         if (error)
685                 return (error);
686         device_delete_child(dev, sc->smb);
687         ichsmb_release_resources(sc);
688         mtx_destroy(&sc->mutex);
689         
690         return 0;
691 }
692
693 DRIVER_MODULE(smbus, ichsmb, smbus_driver, smbus_devclass, 0, 0);