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