xref: /dragonfly/sys/bus/smbus/ichsmb/ichsmb.c (revision 6fbea5ba)
1335b94f8SMatthew Dillon /*-
2335b94f8SMatthew Dillon  * ichsmb.c
3335b94f8SMatthew Dillon  *
4335b94f8SMatthew Dillon  * Author: Archie Cobbs <archie@freebsd.org>
5335b94f8SMatthew Dillon  * Copyright (c) 2000 Whistle Communications, Inc.
6335b94f8SMatthew Dillon  * All rights reserved.
7335b94f8SMatthew Dillon  *
8335b94f8SMatthew Dillon  * Subject to the following obligations and disclaimer of warranty, use and
9335b94f8SMatthew Dillon  * redistribution of this software, in source or object code forms, with or
10335b94f8SMatthew Dillon  * without modifications are expressly permitted by Whistle Communications;
11335b94f8SMatthew Dillon  * provided, however, that:
12335b94f8SMatthew Dillon  * 1. Any and all reproductions of the source or object code must include the
13335b94f8SMatthew Dillon  *    copyright notice above and the following disclaimer of warranties; and
14335b94f8SMatthew Dillon  * 2. No rights are granted, in any manner or form, to use Whistle
15335b94f8SMatthew Dillon  *    Communications, Inc. trademarks, including the mark "WHISTLE
16335b94f8SMatthew Dillon  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17335b94f8SMatthew Dillon  *    such appears in the above copyright notice or in the software.
18335b94f8SMatthew Dillon  *
19335b94f8SMatthew Dillon  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20335b94f8SMatthew Dillon  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21335b94f8SMatthew Dillon  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22335b94f8SMatthew Dillon  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23335b94f8SMatthew Dillon  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24335b94f8SMatthew Dillon  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25335b94f8SMatthew Dillon  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26335b94f8SMatthew Dillon  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27335b94f8SMatthew Dillon  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28335b94f8SMatthew Dillon  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29335b94f8SMatthew Dillon  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30335b94f8SMatthew Dillon  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31335b94f8SMatthew Dillon  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32335b94f8SMatthew Dillon  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33335b94f8SMatthew Dillon  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34335b94f8SMatthew Dillon  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35335b94f8SMatthew Dillon  * OF SUCH DAMAGE.
36335b94f8SMatthew Dillon  *
37335b94f8SMatthew Dillon  * $FreeBSD: src/sys/dev/ichsmb/ichsmb.c,v 1.20 2009/02/03 16:14:37 jhb Exp $
38335b94f8SMatthew Dillon  */
39335b94f8SMatthew Dillon 
40335b94f8SMatthew Dillon /*
41335b94f8SMatthew Dillon  * Support for the SMBus controller logical device which is part of the
42335b94f8SMatthew Dillon  * Intel 81801AA (ICH) and 81801AB (ICH0) I/O controller hub chips.
43335b94f8SMatthew Dillon  *
44335b94f8SMatthew Dillon  * This driver assumes that the generic SMBus code will ensure that
45335b94f8SMatthew Dillon  * at most one process at a time calls into the SMBus methods below.
46335b94f8SMatthew Dillon  */
47335b94f8SMatthew Dillon 
48335b94f8SMatthew Dillon #include <sys/param.h>
49335b94f8SMatthew Dillon #include <sys/systm.h>
50335b94f8SMatthew Dillon #include <sys/kernel.h>
51335b94f8SMatthew Dillon #include <sys/errno.h>
52335b94f8SMatthew Dillon #include <sys/globaldata.h>
53335b94f8SMatthew Dillon #include <sys/lock.h>
54335b94f8SMatthew Dillon #include <sys/module.h>
55335b94f8SMatthew Dillon #include <sys/syslog.h>
56335b94f8SMatthew Dillon #include <sys/bus.h>
57335b94f8SMatthew Dillon 
58335b94f8SMatthew Dillon #include <sys/rman.h>
59335b94f8SMatthew Dillon 
60335b94f8SMatthew Dillon #include <bus/smbus/smbconf.h>
61335b94f8SMatthew Dillon 
62*6fbea5baSSascha Wildner #include <bus/smbus/ichsmb/ichsmb_var.h>
63*6fbea5baSSascha Wildner #include <bus/smbus/ichsmb/ichsmb_reg.h>
64335b94f8SMatthew Dillon 
65335b94f8SMatthew Dillon /*
66335b94f8SMatthew Dillon  * Enable debugging by defining ICHSMB_DEBUG to a non-zero value.
67335b94f8SMatthew Dillon  */
68335b94f8SMatthew Dillon #define ICHSMB_DEBUG	0
69a35a3f48SSascha Wildner #if ICHSMB_DEBUG != 0
70335b94f8SMatthew Dillon #define DBG(fmt, args...)	\
71335b94f8SMatthew Dillon 	do { kprintf("%s: " fmt, __func__ , ## args); } while (0)
72335b94f8SMatthew Dillon #else
73335b94f8SMatthew Dillon #define DBG(fmt, args...)	do { } while (0)
74335b94f8SMatthew Dillon #endif
75335b94f8SMatthew Dillon 
76335b94f8SMatthew Dillon /*
77335b94f8SMatthew Dillon  * Our child device driver name
78335b94f8SMatthew Dillon  */
79335b94f8SMatthew Dillon #define DRIVER_SMBUS	"smbus"
80335b94f8SMatthew Dillon 
81335b94f8SMatthew Dillon /*
82335b94f8SMatthew Dillon  * Internal functions
83335b94f8SMatthew Dillon  */
84335b94f8SMatthew Dillon static int ichsmb_wait(sc_p sc);
85335b94f8SMatthew Dillon 
86335b94f8SMatthew Dillon /********************************************************************
87335b94f8SMatthew Dillon 		BUS-INDEPENDENT BUS METHODS
88335b94f8SMatthew Dillon ********************************************************************/
89335b94f8SMatthew Dillon 
90335b94f8SMatthew Dillon /*
91335b94f8SMatthew Dillon  * Handle probe-time duties that are independent of the bus
92335b94f8SMatthew Dillon  * our device lives on.
93335b94f8SMatthew Dillon  */
94335b94f8SMatthew Dillon int
ichsmb_probe(device_t dev)95335b94f8SMatthew Dillon ichsmb_probe(device_t dev)
96335b94f8SMatthew Dillon {
97335b94f8SMatthew Dillon 	return (BUS_PROBE_DEFAULT);
98335b94f8SMatthew Dillon }
99335b94f8SMatthew Dillon 
100335b94f8SMatthew Dillon /*
101335b94f8SMatthew Dillon  * Handle attach-time duties that are independent of the bus
102335b94f8SMatthew Dillon  * our device lives on.
103335b94f8SMatthew Dillon  */
104335b94f8SMatthew Dillon int
ichsmb_attach(device_t dev)105335b94f8SMatthew Dillon ichsmb_attach(device_t dev)
106335b94f8SMatthew Dillon {
107335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
108335b94f8SMatthew Dillon 	int error;
109335b94f8SMatthew Dillon 
110335b94f8SMatthew Dillon 	/* Create mutex */
111335b94f8SMatthew Dillon 	lockinit(&sc->mutex, "ichsmb", 0, LK_CANRECURSE);
112335b94f8SMatthew Dillon 
113335b94f8SMatthew Dillon 	/* Add child: an instance of the "smbus" device */
114335b94f8SMatthew Dillon 	if ((sc->smb = device_add_child(dev, DRIVER_SMBUS, -1)) == NULL) {
115335b94f8SMatthew Dillon 		device_printf(dev, "no \"%s\" child found\n", DRIVER_SMBUS);
116335b94f8SMatthew Dillon 		error = ENXIO;
117335b94f8SMatthew Dillon 		goto fail;
118335b94f8SMatthew Dillon 	}
119335b94f8SMatthew Dillon 
120335b94f8SMatthew Dillon 	/* Clear interrupt conditions */
121335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_STA, 0xff);
122335b94f8SMatthew Dillon 
123335b94f8SMatthew Dillon 	/* Set up interrupt handler */
124*6fbea5baSSascha Wildner 	error = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE,
125335b94f8SMatthew Dillon 	    ichsmb_device_intr, sc, &sc->irq_handle, NULL);
126335b94f8SMatthew Dillon 	if (error != 0) {
127335b94f8SMatthew Dillon 		device_printf(dev, "can't setup irq\n");
128335b94f8SMatthew Dillon 		goto fail;
129335b94f8SMatthew Dillon 	}
130335b94f8SMatthew Dillon 
131335b94f8SMatthew Dillon 	/* Attach "smbus" child */
132335b94f8SMatthew Dillon 	if ((error = bus_generic_attach(dev)) != 0) {
133335b94f8SMatthew Dillon 		device_printf(dev, "failed to attach child: %d\n", error);
134335b94f8SMatthew Dillon 		goto fail;
135335b94f8SMatthew Dillon 	}
136335b94f8SMatthew Dillon 
137335b94f8SMatthew Dillon 	return (0);
138335b94f8SMatthew Dillon 
139335b94f8SMatthew Dillon fail:
140335b94f8SMatthew Dillon 	lockuninit(&sc->mutex);
141335b94f8SMatthew Dillon 	return (error);
142335b94f8SMatthew Dillon }
143335b94f8SMatthew Dillon 
144335b94f8SMatthew Dillon /********************************************************************
145335b94f8SMatthew Dillon 			SMBUS METHODS
146335b94f8SMatthew Dillon ********************************************************************/
147335b94f8SMatthew Dillon 
148335b94f8SMatthew Dillon int
ichsmb_callback(device_t dev,int index,void * data)149335b94f8SMatthew Dillon ichsmb_callback(device_t dev, int index, void *data)
150335b94f8SMatthew Dillon {
151335b94f8SMatthew Dillon 	int smb_error = 0;
152335b94f8SMatthew Dillon 
153335b94f8SMatthew Dillon 	DBG("index=%d how=%d\n", index, data ? *(int *)data : -1);
154335b94f8SMatthew Dillon 	switch (index) {
155335b94f8SMatthew Dillon 	case SMB_REQUEST_BUS:
156335b94f8SMatthew Dillon 		break;
157335b94f8SMatthew Dillon 	case SMB_RELEASE_BUS:
158335b94f8SMatthew Dillon 		break;
159335b94f8SMatthew Dillon 	default:
160335b94f8SMatthew Dillon 		smb_error = SMB_EABORT;	/* XXX */
161335b94f8SMatthew Dillon 		break;
162335b94f8SMatthew Dillon 	}
163335b94f8SMatthew Dillon 	DBG("smb_error=%d\n", smb_error);
164335b94f8SMatthew Dillon 	return (smb_error);
165335b94f8SMatthew Dillon }
166335b94f8SMatthew Dillon 
167335b94f8SMatthew Dillon int
ichsmb_quick(device_t dev,u_char slave,int how)168335b94f8SMatthew Dillon ichsmb_quick(device_t dev, u_char slave, int how)
169335b94f8SMatthew Dillon {
170335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
171335b94f8SMatthew Dillon 	int smb_error;
172335b94f8SMatthew Dillon 
173335b94f8SMatthew Dillon 	DBG("slave=0x%02x how=%d\n", slave, how);
174335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
175335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
176335b94f8SMatthew Dillon 	switch (how) {
177335b94f8SMatthew Dillon 	case SMB_QREAD:
178335b94f8SMatthew Dillon 	case SMB_QWRITE:
179335b94f8SMatthew Dillon 		lockmgr(&sc->mutex, LK_EXCLUSIVE);
180335b94f8SMatthew Dillon 		sc->ich_cmd = ICH_HST_CNT_SMB_CMD_QUICK;
181335b94f8SMatthew Dillon 		bus_write_1(sc->io_res, ICH_XMIT_SLVA,
182335b94f8SMatthew Dillon 		    slave | (how == SMB_QREAD ?
183335b94f8SMatthew Dillon 			ICH_XMIT_SLVA_READ : ICH_XMIT_SLVA_WRITE));
184335b94f8SMatthew Dillon 		bus_write_1(sc->io_res, ICH_HST_CNT,
185335b94f8SMatthew Dillon 		    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
186335b94f8SMatthew Dillon 		smb_error = ichsmb_wait(sc);
187335b94f8SMatthew Dillon 		lockmgr(&sc->mutex, LK_RELEASE);
188335b94f8SMatthew Dillon 		break;
189335b94f8SMatthew Dillon 	default:
190335b94f8SMatthew Dillon 		smb_error = SMB_ENOTSUPP;
191335b94f8SMatthew Dillon 	}
192335b94f8SMatthew Dillon 	DBG("smb_error=%d\n", smb_error);
193335b94f8SMatthew Dillon 	return (smb_error);
194335b94f8SMatthew Dillon }
195335b94f8SMatthew Dillon 
196335b94f8SMatthew Dillon int
ichsmb_sendb(device_t dev,u_char slave,char byte)197335b94f8SMatthew Dillon ichsmb_sendb(device_t dev, u_char slave, char byte)
198335b94f8SMatthew Dillon {
199335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
200335b94f8SMatthew Dillon 	int smb_error;
201335b94f8SMatthew Dillon 
202335b94f8SMatthew Dillon 	DBG("slave=0x%02x byte=0x%02x\n", slave, (u_char)byte);
203335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
204335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
205335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
206335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
207335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
208335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_WRITE);
209335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, byte);
210335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
211335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
212335b94f8SMatthew Dillon 	smb_error = ichsmb_wait(sc);
213335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
214335b94f8SMatthew Dillon 	DBG("smb_error=%d\n", smb_error);
215335b94f8SMatthew Dillon 	return (smb_error);
216335b94f8SMatthew Dillon }
217335b94f8SMatthew Dillon 
218335b94f8SMatthew Dillon int
ichsmb_recvb(device_t dev,u_char slave,char * byte)219335b94f8SMatthew Dillon ichsmb_recvb(device_t dev, u_char slave, char *byte)
220335b94f8SMatthew Dillon {
221335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
222335b94f8SMatthew Dillon 	int smb_error;
223335b94f8SMatthew Dillon 
224335b94f8SMatthew Dillon 	DBG("slave=0x%02x\n", slave);
225335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
226335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
227335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
228335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
229335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
230335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_READ);
231335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
232335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
233335b94f8SMatthew Dillon 	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
234335b94f8SMatthew Dillon 		*byte = bus_read_1(sc->io_res, ICH_D0);
235335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
236335b94f8SMatthew Dillon 	DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
237335b94f8SMatthew Dillon 	return (smb_error);
238335b94f8SMatthew Dillon }
239335b94f8SMatthew Dillon 
240335b94f8SMatthew Dillon int
ichsmb_writeb(device_t dev,u_char slave,char cmd,char byte)241335b94f8SMatthew Dillon ichsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
242335b94f8SMatthew Dillon {
243335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
244335b94f8SMatthew Dillon 	int smb_error;
245335b94f8SMatthew Dillon 
246335b94f8SMatthew Dillon 	DBG("slave=0x%02x cmd=0x%02x byte=0x%02x\n",
247335b94f8SMatthew Dillon 	    slave, (u_char)cmd, (u_char)byte);
248335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
249335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
250335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
251335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
252335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
253335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_WRITE);
254335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
255335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_D0, byte);
256335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
257335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
258335b94f8SMatthew Dillon 	smb_error = ichsmb_wait(sc);
259335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
260335b94f8SMatthew Dillon 	DBG("smb_error=%d\n", smb_error);
261335b94f8SMatthew Dillon 	return (smb_error);
262335b94f8SMatthew Dillon }
263335b94f8SMatthew Dillon 
264335b94f8SMatthew Dillon int
ichsmb_writew(device_t dev,u_char slave,char cmd,short word)265335b94f8SMatthew Dillon ichsmb_writew(device_t dev, u_char slave, char cmd, short word)
266335b94f8SMatthew Dillon {
267335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
268335b94f8SMatthew Dillon 	int smb_error;
269335b94f8SMatthew Dillon 
270335b94f8SMatthew Dillon 	DBG("slave=0x%02x cmd=0x%02x word=0x%04x\n",
271335b94f8SMatthew Dillon 	    slave, (u_char)cmd, (u_int16_t)word);
272335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
273335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
274335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
275335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
276335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
277335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_WRITE);
278335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
279335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_D0, word & 0xff);
280335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_D1, word >> 8);
281335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
282335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
283335b94f8SMatthew Dillon 	smb_error = ichsmb_wait(sc);
284335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
285335b94f8SMatthew Dillon 	DBG("smb_error=%d\n", smb_error);
286335b94f8SMatthew Dillon 	return (smb_error);
287335b94f8SMatthew Dillon }
288335b94f8SMatthew Dillon 
289335b94f8SMatthew Dillon int
ichsmb_readb(device_t dev,u_char slave,char cmd,char * byte)290335b94f8SMatthew Dillon ichsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
291335b94f8SMatthew Dillon {
292335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
293335b94f8SMatthew Dillon 	int smb_error;
294335b94f8SMatthew Dillon 
295335b94f8SMatthew Dillon 	DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
296335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
297335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
298335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
299335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
300335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
301335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_READ);
302335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
303335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
304335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
305335b94f8SMatthew Dillon 	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
306335b94f8SMatthew Dillon 		*byte = bus_read_1(sc->io_res, ICH_D0);
307335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
308335b94f8SMatthew Dillon 	DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
309335b94f8SMatthew Dillon 	return (smb_error);
310335b94f8SMatthew Dillon }
311335b94f8SMatthew Dillon 
312335b94f8SMatthew Dillon int
ichsmb_readw(device_t dev,u_char slave,char cmd,short * word)313335b94f8SMatthew Dillon ichsmb_readw(device_t dev, u_char slave, char cmd, short *word)
314335b94f8SMatthew Dillon {
315335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
316335b94f8SMatthew Dillon 	int smb_error;
317335b94f8SMatthew Dillon 
318335b94f8SMatthew Dillon 	DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
319335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
320335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
321335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
322335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
323335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
324335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_READ);
325335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
326335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
327335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
328335b94f8SMatthew Dillon 	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
329335b94f8SMatthew Dillon 		*word = (bus_read_1(sc->io_res,
330335b94f8SMatthew Dillon 			ICH_D0) & 0xff)
331335b94f8SMatthew Dillon 		  | (bus_read_1(sc->io_res,
332335b94f8SMatthew Dillon 			ICH_D1) << 8);
333335b94f8SMatthew Dillon 	}
334335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
335335b94f8SMatthew Dillon 	DBG("smb_error=%d word=0x%04x\n", smb_error, (u_int16_t)*word);
336335b94f8SMatthew Dillon 	return (smb_error);
337335b94f8SMatthew Dillon }
338335b94f8SMatthew Dillon 
339335b94f8SMatthew Dillon int
ichsmb_pcall(device_t dev,u_char slave,char cmd,short sdata,short * rdata)340335b94f8SMatthew Dillon ichsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
341335b94f8SMatthew Dillon {
342335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
343335b94f8SMatthew Dillon 	int smb_error;
344335b94f8SMatthew Dillon 
345335b94f8SMatthew Dillon 	DBG("slave=0x%02x cmd=0x%02x sdata=0x%04x\n",
346335b94f8SMatthew Dillon 	    slave, (u_char)cmd, (u_int16_t)sdata);
347335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
348335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
349335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
350335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_PROC_CALL;
351335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
352335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_WRITE);
353335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
354335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_D0, sdata & 0xff);
355335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_D1, sdata >> 8);
356335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
357335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
358335b94f8SMatthew Dillon 	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
359335b94f8SMatthew Dillon 		*rdata = (bus_read_1(sc->io_res,
360335b94f8SMatthew Dillon 			ICH_D0) & 0xff)
361335b94f8SMatthew Dillon 		  | (bus_read_1(sc->io_res,
362335b94f8SMatthew Dillon 			ICH_D1) << 8);
363335b94f8SMatthew Dillon 	}
364335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
365335b94f8SMatthew Dillon 	DBG("smb_error=%d rdata=0x%04x\n", smb_error, (u_int16_t)*rdata);
366335b94f8SMatthew Dillon 	return (smb_error);
367335b94f8SMatthew Dillon }
368335b94f8SMatthew Dillon 
369335b94f8SMatthew Dillon int
ichsmb_bwrite(device_t dev,u_char slave,char cmd,u_char count,char * buf)370335b94f8SMatthew Dillon ichsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
371335b94f8SMatthew Dillon {
372335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
373335b94f8SMatthew Dillon 	int smb_error;
374335b94f8SMatthew Dillon 
375335b94f8SMatthew Dillon 	DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
376335b94f8SMatthew Dillon #if ICHSMB_DEBUG
377335b94f8SMatthew Dillon #define DISP(ch)	(((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
378335b94f8SMatthew Dillon 	{
379335b94f8SMatthew Dillon 	    u_char *p;
380335b94f8SMatthew Dillon 
381335b94f8SMatthew Dillon 	    for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
382335b94f8SMatthew Dillon 		DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
383335b94f8SMatthew Dillon 		    "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
384335b94f8SMatthew Dillon 		    p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
385335b94f8SMatthew Dillon 		    DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]),
386335b94f8SMatthew Dillon 		    DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
387335b94f8SMatthew Dillon 	    }
388335b94f8SMatthew Dillon 	}
389335b94f8SMatthew Dillon #undef DISP
390335b94f8SMatthew Dillon #endif
391335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
392335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
393335b94f8SMatthew Dillon 	if (count < 1 || count > 32)
394335b94f8SMatthew Dillon 		return (SMB_EINVAL);
395335b94f8SMatthew Dillon 	bcopy(buf, sc->block_data, count);
396335b94f8SMatthew Dillon 	sc->block_count = count;
397335b94f8SMatthew Dillon 	sc->block_index = 1;
398335b94f8SMatthew Dillon 	sc->block_write = 1;
399335b94f8SMatthew Dillon 
400335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
401335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
402335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
403335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_WRITE);
404335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
405335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_D0, count);
406335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_BLOCK_DB, buf[0]);
407335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
408335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
409335b94f8SMatthew Dillon 	smb_error = ichsmb_wait(sc);
410335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
411335b94f8SMatthew Dillon 	DBG("smb_error=%d\n", smb_error);
412335b94f8SMatthew Dillon 	return (smb_error);
413335b94f8SMatthew Dillon }
414335b94f8SMatthew Dillon 
415335b94f8SMatthew Dillon int
ichsmb_bread(device_t dev,u_char slave,char cmd,u_char * count,char * buf)416335b94f8SMatthew Dillon ichsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
417335b94f8SMatthew Dillon {
418335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
419335b94f8SMatthew Dillon 	int smb_error;
420335b94f8SMatthew Dillon 
421335b94f8SMatthew Dillon 	DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
422335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd == -1,
423335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
424335b94f8SMatthew Dillon 	if (*count < 1 || *count > 32)
425335b94f8SMatthew Dillon 		return (SMB_EINVAL);
426335b94f8SMatthew Dillon 	bzero(sc->block_data, sizeof(sc->block_data));
427335b94f8SMatthew Dillon 	sc->block_count = 0;
428335b94f8SMatthew Dillon 	sc->block_index = 0;
429335b94f8SMatthew Dillon 	sc->block_write = 0;
430335b94f8SMatthew Dillon 
431335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
432335b94f8SMatthew Dillon 	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
433335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
434335b94f8SMatthew Dillon 	    slave | ICH_XMIT_SLVA_READ);
435335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
436335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_D0, *count); /* XXX? */
437335b94f8SMatthew Dillon 	bus_write_1(sc->io_res, ICH_HST_CNT,
438335b94f8SMatthew Dillon 	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
439335b94f8SMatthew Dillon 	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
440335b94f8SMatthew Dillon 		bcopy(sc->block_data, buf, min(sc->block_count, *count));
441335b94f8SMatthew Dillon 		*count = sc->block_count;
442335b94f8SMatthew Dillon 	}
443335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
444335b94f8SMatthew Dillon 	DBG("smb_error=%d\n", smb_error);
445335b94f8SMatthew Dillon #if ICHSMB_DEBUG
446335b94f8SMatthew Dillon #define DISP(ch)	(((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
447335b94f8SMatthew Dillon 	{
448335b94f8SMatthew Dillon 	    u_char *p;
449335b94f8SMatthew Dillon 
450335b94f8SMatthew Dillon 	    for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
451335b94f8SMatthew Dillon 		DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
452335b94f8SMatthew Dillon 		    "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
453335b94f8SMatthew Dillon 		    p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
454335b94f8SMatthew Dillon 		    DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]),
455335b94f8SMatthew Dillon 		    DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
456335b94f8SMatthew Dillon 	    }
457335b94f8SMatthew Dillon 	}
458335b94f8SMatthew Dillon #undef DISP
459335b94f8SMatthew Dillon #endif
460335b94f8SMatthew Dillon 	return (smb_error);
461335b94f8SMatthew Dillon }
462335b94f8SMatthew Dillon 
463335b94f8SMatthew Dillon /********************************************************************
464335b94f8SMatthew Dillon 			OTHER FUNCTIONS
465335b94f8SMatthew Dillon ********************************************************************/
466335b94f8SMatthew Dillon 
467335b94f8SMatthew Dillon /*
468335b94f8SMatthew Dillon  * This table describes what interrupts we should ever expect to
469335b94f8SMatthew Dillon  * see after each ICH command, not including the SMBALERT interrupt.
470335b94f8SMatthew Dillon  */
471335b94f8SMatthew Dillon static const u_int8_t ichsmb_state_irqs[] = {
472335b94f8SMatthew Dillon 	/* quick */
473335b94f8SMatthew Dillon 	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
474335b94f8SMatthew Dillon 	/* byte */
475335b94f8SMatthew Dillon 	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
476335b94f8SMatthew Dillon 	/* byte data */
477335b94f8SMatthew Dillon 	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
478335b94f8SMatthew Dillon 	/* word data */
479335b94f8SMatthew Dillon 	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
480335b94f8SMatthew Dillon 	/* process call */
481335b94f8SMatthew Dillon 	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
482335b94f8SMatthew Dillon 	/* block */
483335b94f8SMatthew Dillon 	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
484335b94f8SMatthew Dillon 	    | ICH_HST_STA_BYTE_DONE_STS),
485335b94f8SMatthew Dillon 	/* i2c read (not used) */
486335b94f8SMatthew Dillon 	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
487335b94f8SMatthew Dillon 	    | ICH_HST_STA_BYTE_DONE_STS)
488335b94f8SMatthew Dillon };
489335b94f8SMatthew Dillon 
490335b94f8SMatthew Dillon /*
491335b94f8SMatthew Dillon  * Interrupt handler. This handler is bus-independent. Note that our
492335b94f8SMatthew Dillon  * interrupt may be shared, so we must handle "false" interrupts.
493335b94f8SMatthew Dillon  */
494335b94f8SMatthew Dillon void
ichsmb_device_intr(void * cookie)495335b94f8SMatthew Dillon ichsmb_device_intr(void *cookie)
496335b94f8SMatthew Dillon {
497335b94f8SMatthew Dillon 	const sc_p sc = cookie;
498335b94f8SMatthew Dillon 	const device_t dev = sc->dev;
499335b94f8SMatthew Dillon 	const int maxloops = 16;
500335b94f8SMatthew Dillon 	u_int8_t status;
501335b94f8SMatthew Dillon 	u_int8_t ok_bits;
502335b94f8SMatthew Dillon 	int cmd_index;
503335b94f8SMatthew Dillon         int count;
504335b94f8SMatthew Dillon 
505335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_EXCLUSIVE);
506335b94f8SMatthew Dillon 	for (count = 0; count < maxloops; count++) {
507335b94f8SMatthew Dillon 
508335b94f8SMatthew Dillon 		/* Get and reset status bits */
509335b94f8SMatthew Dillon 		status = bus_read_1(sc->io_res, ICH_HST_STA);
510335b94f8SMatthew Dillon #if ICHSMB_DEBUG
511335b94f8SMatthew Dillon 		if ((status & ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY))
512335b94f8SMatthew Dillon 		    || count > 0) {
513335b94f8SMatthew Dillon 			DBG("%d stat=0x%02x\n", count, status);
514335b94f8SMatthew Dillon 		}
515335b94f8SMatthew Dillon #endif
516335b94f8SMatthew Dillon 		status &= ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY);
517335b94f8SMatthew Dillon 		if (status == 0)
518335b94f8SMatthew Dillon 			break;
519335b94f8SMatthew Dillon 
520335b94f8SMatthew Dillon 		/* Check for unexpected interrupt */
521335b94f8SMatthew Dillon 		ok_bits = ICH_HST_STA_SMBALERT_STS;
522335b94f8SMatthew Dillon 		cmd_index = sc->ich_cmd >> 2;
523335b94f8SMatthew Dillon 		if (sc->ich_cmd != -1) {
524335b94f8SMatthew Dillon 			KASSERT(cmd_index < sizeof(ichsmb_state_irqs),
525335b94f8SMatthew Dillon 			    ("%s: ich_cmd=%d", device_get_nameunit(dev),
526335b94f8SMatthew Dillon 			    sc->ich_cmd));
527335b94f8SMatthew Dillon 			ok_bits |= ichsmb_state_irqs[cmd_index];
528335b94f8SMatthew Dillon 		}
529335b94f8SMatthew Dillon 		if ((status & ~ok_bits) != 0) {
530335b94f8SMatthew Dillon 			device_printf(dev, "irq 0x%02x during %d\n", status,
531335b94f8SMatthew Dillon 			    cmd_index);
532335b94f8SMatthew Dillon 			bus_write_1(sc->io_res,
533335b94f8SMatthew Dillon 			    ICH_HST_STA, (status & ~ok_bits));
534335b94f8SMatthew Dillon 			continue;
535335b94f8SMatthew Dillon 		}
536335b94f8SMatthew Dillon 
537335b94f8SMatthew Dillon 		/* Handle SMBALERT interrupt */
538335b94f8SMatthew Dillon 		if (status & ICH_HST_STA_SMBALERT_STS) {
539335b94f8SMatthew Dillon 			static int smbalert_count = 16;
540335b94f8SMatthew Dillon 			if (smbalert_count > 0) {
541335b94f8SMatthew Dillon 				device_printf(dev, "SMBALERT# rec'd\n");
542335b94f8SMatthew Dillon 				if (--smbalert_count == 0) {
543335b94f8SMatthew Dillon 					device_printf(dev,
544335b94f8SMatthew Dillon 					    "not logging anymore\n");
545335b94f8SMatthew Dillon 				}
546335b94f8SMatthew Dillon 			}
547335b94f8SMatthew Dillon 		}
548335b94f8SMatthew Dillon 
549335b94f8SMatthew Dillon 		/* Check for bus error */
550335b94f8SMatthew Dillon 		if (status & ICH_HST_STA_BUS_ERR) {
551335b94f8SMatthew Dillon 			sc->smb_error = SMB_ECOLLI;	/* XXX SMB_EBUSERR? */
552335b94f8SMatthew Dillon 			goto finished;
553335b94f8SMatthew Dillon 		}
554335b94f8SMatthew Dillon 
555335b94f8SMatthew Dillon 		/* Check for device error */
556335b94f8SMatthew Dillon 		if (status & ICH_HST_STA_DEV_ERR) {
557335b94f8SMatthew Dillon 			sc->smb_error = SMB_ENOACK;	/* or SMB_ETIMEOUT? */
558335b94f8SMatthew Dillon 			goto finished;
559335b94f8SMatthew Dillon 		}
560335b94f8SMatthew Dillon 
561335b94f8SMatthew Dillon 		/* Check for byte completion in block transfer */
562335b94f8SMatthew Dillon 		if (status & ICH_HST_STA_BYTE_DONE_STS) {
563335b94f8SMatthew Dillon 			if (sc->block_write) {
564335b94f8SMatthew Dillon 				if (sc->block_index < sc->block_count) {
565335b94f8SMatthew Dillon 
566335b94f8SMatthew Dillon 					/* Write next byte */
567335b94f8SMatthew Dillon 					bus_write_1(sc->io_res,
568335b94f8SMatthew Dillon 					    ICH_BLOCK_DB,
569335b94f8SMatthew Dillon 					    sc->block_data[sc->block_index++]);
570335b94f8SMatthew Dillon 				}
571335b94f8SMatthew Dillon 			} else {
572335b94f8SMatthew Dillon 
573335b94f8SMatthew Dillon 				/* First interrupt, get the count also */
574335b94f8SMatthew Dillon 				if (sc->block_index == 0) {
575335b94f8SMatthew Dillon 					sc->block_count = bus_read_1(
576335b94f8SMatthew Dillon 					    sc->io_res, ICH_D0);
577335b94f8SMatthew Dillon 				}
578335b94f8SMatthew Dillon 
579335b94f8SMatthew Dillon 				/* Get next byte, if any */
580335b94f8SMatthew Dillon 				if (sc->block_index < sc->block_count) {
581335b94f8SMatthew Dillon 
582335b94f8SMatthew Dillon 					/* Read next byte */
583335b94f8SMatthew Dillon 					sc->block_data[sc->block_index++] =
584335b94f8SMatthew Dillon 					    bus_read_1(sc->io_res,
585335b94f8SMatthew Dillon 					      ICH_BLOCK_DB);
586335b94f8SMatthew Dillon 
587335b94f8SMatthew Dillon 					/* Set "LAST_BYTE" bit before reading
588335b94f8SMatthew Dillon 					   the last byte of block data */
589335b94f8SMatthew Dillon 					if (sc->block_index
590335b94f8SMatthew Dillon 					    >= sc->block_count - 1) {
591335b94f8SMatthew Dillon 						bus_write_1(sc->io_res,
592335b94f8SMatthew Dillon 						    ICH_HST_CNT,
593335b94f8SMatthew Dillon 						    ICH_HST_CNT_LAST_BYTE
594335b94f8SMatthew Dillon 							| ICH_HST_CNT_INTREN
595335b94f8SMatthew Dillon 							| sc->ich_cmd);
596335b94f8SMatthew Dillon 					}
597335b94f8SMatthew Dillon 				}
598335b94f8SMatthew Dillon 			}
599335b94f8SMatthew Dillon 		}
600335b94f8SMatthew Dillon 
601335b94f8SMatthew Dillon 		/* Check command completion */
602335b94f8SMatthew Dillon 		if (status & ICH_HST_STA_INTR) {
603335b94f8SMatthew Dillon 			sc->smb_error = SMB_ENOERR;
604335b94f8SMatthew Dillon finished:
605335b94f8SMatthew Dillon 			sc->ich_cmd = -1;
606335b94f8SMatthew Dillon 			bus_write_1(sc->io_res,
607335b94f8SMatthew Dillon 			    ICH_HST_STA, status);
608335b94f8SMatthew Dillon 			wakeup(sc);
609335b94f8SMatthew Dillon 			break;
610335b94f8SMatthew Dillon 		}
611335b94f8SMatthew Dillon 
612335b94f8SMatthew Dillon 		/* Clear status bits and try again */
613335b94f8SMatthew Dillon 		bus_write_1(sc->io_res, ICH_HST_STA, status);
614335b94f8SMatthew Dillon 	}
615335b94f8SMatthew Dillon 	lockmgr(&sc->mutex, LK_RELEASE);
616335b94f8SMatthew Dillon 
617335b94f8SMatthew Dillon 	/* Too many loops? */
618335b94f8SMatthew Dillon 	if (count == maxloops) {
619335b94f8SMatthew Dillon 		device_printf(dev, "interrupt loop, status=0x%02x\n",
620335b94f8SMatthew Dillon 		    bus_read_1(sc->io_res, ICH_HST_STA));
621335b94f8SMatthew Dillon 	}
622335b94f8SMatthew Dillon }
623335b94f8SMatthew Dillon 
624335b94f8SMatthew Dillon /*
625335b94f8SMatthew Dillon  * Wait for command completion. Assumes mutex is held.
626335b94f8SMatthew Dillon  * Returns an SMB_* error code.
627335b94f8SMatthew Dillon  */
628335b94f8SMatthew Dillon static int
ichsmb_wait(sc_p sc)629335b94f8SMatthew Dillon ichsmb_wait(sc_p sc)
630335b94f8SMatthew Dillon {
631335b94f8SMatthew Dillon 	const device_t dev = sc->dev;
632335b94f8SMatthew Dillon 	int error, smb_error;
633335b94f8SMatthew Dillon 
634335b94f8SMatthew Dillon 	KASSERT(sc->ich_cmd != -1,
635335b94f8SMatthew Dillon 	    ("%s: ich_cmd=%d", __func__ , sc->ich_cmd));
636335b94f8SMatthew Dillon 	KKASSERT(lockstatus(&sc->mutex, curthread) != 0);
637335b94f8SMatthew Dillon 	error = lksleep(sc, &sc->mutex, 0, "ichsmb", hz / 4);
638335b94f8SMatthew Dillon 	DBG("msleep -> %d\n", error);
639335b94f8SMatthew Dillon 	switch (error) {
640335b94f8SMatthew Dillon 	case 0:
641335b94f8SMatthew Dillon 		smb_error = sc->smb_error;
642335b94f8SMatthew Dillon 		break;
643335b94f8SMatthew Dillon 	case EWOULDBLOCK:
644335b94f8SMatthew Dillon 		device_printf(dev, "device timeout, status=0x%02x\n",
645335b94f8SMatthew Dillon 		    bus_read_1(sc->io_res, ICH_HST_STA));
646335b94f8SMatthew Dillon 		sc->ich_cmd = -1;
647335b94f8SMatthew Dillon 		smb_error = SMB_ETIMEOUT;
648335b94f8SMatthew Dillon 		break;
649335b94f8SMatthew Dillon 	default:
650335b94f8SMatthew Dillon 		smb_error = SMB_EABORT;
651335b94f8SMatthew Dillon 		break;
652335b94f8SMatthew Dillon 	}
653335b94f8SMatthew Dillon 	return (smb_error);
654335b94f8SMatthew Dillon }
655335b94f8SMatthew Dillon 
656335b94f8SMatthew Dillon /*
657335b94f8SMatthew Dillon  * Release resources associated with device.
658335b94f8SMatthew Dillon  */
659335b94f8SMatthew Dillon void
ichsmb_release_resources(sc_p sc)660335b94f8SMatthew Dillon ichsmb_release_resources(sc_p sc)
661335b94f8SMatthew Dillon {
662335b94f8SMatthew Dillon 	const device_t dev = sc->dev;
663335b94f8SMatthew Dillon 
664335b94f8SMatthew Dillon 	if (sc->irq_handle != NULL) {
665335b94f8SMatthew Dillon 		bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
666335b94f8SMatthew Dillon 		sc->irq_handle = NULL;
667335b94f8SMatthew Dillon 	}
668335b94f8SMatthew Dillon 	if (sc->irq_res != NULL) {
669335b94f8SMatthew Dillon 		bus_release_resource(dev,
670335b94f8SMatthew Dillon 		    SYS_RES_IRQ, sc->irq_rid, sc->irq_res);
671335b94f8SMatthew Dillon 		sc->irq_res = NULL;
672335b94f8SMatthew Dillon 	}
673335b94f8SMatthew Dillon 	if (sc->io_res != NULL) {
674335b94f8SMatthew Dillon 		bus_release_resource(dev,
675335b94f8SMatthew Dillon 		    SYS_RES_IOPORT, sc->io_rid, sc->io_res);
676335b94f8SMatthew Dillon 		sc->io_res = NULL;
677335b94f8SMatthew Dillon 	}
678335b94f8SMatthew Dillon }
679335b94f8SMatthew Dillon 
680335b94f8SMatthew Dillon int
ichsmb_detach(device_t dev)681335b94f8SMatthew Dillon ichsmb_detach(device_t dev)
682335b94f8SMatthew Dillon {
683335b94f8SMatthew Dillon 	const sc_p sc = device_get_softc(dev);
684335b94f8SMatthew Dillon 	int error;
685335b94f8SMatthew Dillon 
686335b94f8SMatthew Dillon 	error = bus_generic_detach(dev);
687335b94f8SMatthew Dillon 	if (error)
688335b94f8SMatthew Dillon 		return (error);
689335b94f8SMatthew Dillon 	device_delete_child(dev, sc->smb);
690335b94f8SMatthew Dillon 	ichsmb_release_resources(sc);
691335b94f8SMatthew Dillon 	lockuninit(&sc->mutex);
692335b94f8SMatthew Dillon 
693335b94f8SMatthew Dillon 	return 0;
694335b94f8SMatthew Dillon }
695335b94f8SMatthew Dillon 
696335b94f8SMatthew Dillon DRIVER_MODULE(smbus, ichsmb, smbus_driver, smbus_devclass, NULL, NULL);
697