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