xref: /dflybsd-src/sys/dev/powermng/intpm/intpm.c (revision a9656fbcd49c376aba5e04370d8b0f1fa96e063c)
1*a9656fbcSSascha Wildner /*-
2*a9656fbcSSascha Wildner  * Copyright (c) 1998, 1999 Takanori Watanabe
3*a9656fbcSSascha Wildner  * All rights reserved.
4*a9656fbcSSascha Wildner  *
5*a9656fbcSSascha Wildner  * Redistribution and use in source and binary forms, with or without
6*a9656fbcSSascha Wildner  * modification, are permitted provided that the following conditions
7*a9656fbcSSascha Wildner  * are met:
8*a9656fbcSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
9*a9656fbcSSascha Wildner  *        notice, this list of conditions and the following disclaimer.
10*a9656fbcSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
11*a9656fbcSSascha Wildner  *        notice, this list of conditions and the following disclaimer in the
12*a9656fbcSSascha Wildner  *        documentation and/or other materials provided with the distribution.
13*a9656fbcSSascha Wildner  *
14*a9656fbcSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*a9656fbcSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*a9656fbcSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*a9656fbcSSascha Wildner  * ARE DISCLAIMED.    IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*a9656fbcSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*a9656fbcSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*a9656fbcSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*a9656fbcSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*a9656fbcSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*a9656fbcSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*a9656fbcSSascha Wildner  * SUCH DAMAGE.
25*a9656fbcSSascha Wildner  *
26*a9656fbcSSascha Wildner  * $FreeBSD: src/sys/pci/intpm.c,v 1.45 2009/09/19 08:56:28 avg Exp $
27*a9656fbcSSascha Wildner  */
28*a9656fbcSSascha Wildner 
29*a9656fbcSSascha Wildner #include <sys/param.h>
30*a9656fbcSSascha Wildner #include <sys/systm.h>
31*a9656fbcSSascha Wildner #include <sys/bus.h>
32*a9656fbcSSascha Wildner #include <sys/globaldata.h>
33*a9656fbcSSascha Wildner #include <sys/kernel.h>
34*a9656fbcSSascha Wildner #include <sys/lock.h>
35*a9656fbcSSascha Wildner #include <sys/module.h>
36*a9656fbcSSascha Wildner #include <sys/mutex.h>
37*a9656fbcSSascha Wildner #include <sys/rman.h>
38*a9656fbcSSascha Wildner #include <bus/smbus/smbconf.h>
39*a9656fbcSSascha Wildner 
40*a9656fbcSSascha Wildner #include "smbus_if.h"
41*a9656fbcSSascha Wildner 
42*a9656fbcSSascha Wildner #include <bus/pci/pcireg.h>
43*a9656fbcSSascha Wildner #include <bus/pci/pcivar.h>
44*a9656fbcSSascha Wildner #include <dev/powermng/intpm/intpmreg.h>
45*a9656fbcSSascha Wildner 
46*a9656fbcSSascha Wildner #include "opt_intpm.h"
47*a9656fbcSSascha Wildner 
48*a9656fbcSSascha Wildner struct intsmb_softc {
49*a9656fbcSSascha Wildner 	device_t		dev;
50*a9656fbcSSascha Wildner 	struct resource		*io_res;
51*a9656fbcSSascha Wildner 	struct resource		*irq_res;
52*a9656fbcSSascha Wildner 	void			*irq_hand;
53*a9656fbcSSascha Wildner 	device_t		smbus;
54*a9656fbcSSascha Wildner 	int			isbusy;
55*a9656fbcSSascha Wildner 	int			cfg_irq9;
56*a9656fbcSSascha Wildner 	int			poll;
57*a9656fbcSSascha Wildner 	struct lock		lock;
58*a9656fbcSSascha Wildner };
59*a9656fbcSSascha Wildner 
60*a9656fbcSSascha Wildner #define	INTSMB_LOCK(sc)		lockmgr(&(sc)->lock, LK_EXCLUSIVE)
61*a9656fbcSSascha Wildner #define	INTSMB_UNLOCK(sc)	lockmgr(&(sc)->lock, LK_RELEASE)
62*a9656fbcSSascha Wildner #define	INTSMB_LOCK_ASSERT(sc)	KKASSERT(lockstatus(&(sc)->lock, curthread) != 0)
63*a9656fbcSSascha Wildner 
64*a9656fbcSSascha Wildner static int intsmb_probe(device_t);
65*a9656fbcSSascha Wildner static int intsmb_attach(device_t);
66*a9656fbcSSascha Wildner static int intsmb_detach(device_t);
67*a9656fbcSSascha Wildner static int intsmb_intr(struct intsmb_softc *sc);
68*a9656fbcSSascha Wildner static int intsmb_slvintr(struct intsmb_softc *sc);
69*a9656fbcSSascha Wildner static void intsmb_alrintr(struct intsmb_softc *sc);
70*a9656fbcSSascha Wildner static int intsmb_callback(device_t dev, int index, void *data);
71*a9656fbcSSascha Wildner static int intsmb_quick(device_t dev, u_char slave, int how);
72*a9656fbcSSascha Wildner static int intsmb_sendb(device_t dev, u_char slave, char byte);
73*a9656fbcSSascha Wildner static int intsmb_recvb(device_t dev, u_char slave, char *byte);
74*a9656fbcSSascha Wildner static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
75*a9656fbcSSascha Wildner static int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
76*a9656fbcSSascha Wildner static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
77*a9656fbcSSascha Wildner static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
78*a9656fbcSSascha Wildner static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
79*a9656fbcSSascha Wildner static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
80*a9656fbcSSascha Wildner static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
81*a9656fbcSSascha Wildner static void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr);
82*a9656fbcSSascha Wildner static int intsmb_stop(struct intsmb_softc *sc);
83*a9656fbcSSascha Wildner static int intsmb_stop_poll(struct intsmb_softc *sc);
84*a9656fbcSSascha Wildner static int intsmb_free(struct intsmb_softc *sc);
85*a9656fbcSSascha Wildner static void intsmb_rawintr(void *arg);
86*a9656fbcSSascha Wildner 
87*a9656fbcSSascha Wildner static int
88*a9656fbcSSascha Wildner intsmb_probe(device_t dev)
89*a9656fbcSSascha Wildner {
90*a9656fbcSSascha Wildner 
91*a9656fbcSSascha Wildner 	switch (pci_get_devid(dev)) {
92*a9656fbcSSascha Wildner 	case 0x71138086:	/* Intel 82371AB */
93*a9656fbcSSascha Wildner 	case 0x719b8086:	/* Intel 82443MX */
94*a9656fbcSSascha Wildner #if 0
95*a9656fbcSSascha Wildner 	/* Not a good idea yet, this stops isab0 functioning */
96*a9656fbcSSascha Wildner 	case 0x02001166:	/* ServerWorks OSB4 */
97*a9656fbcSSascha Wildner #endif
98*a9656fbcSSascha Wildner 		device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
99*a9656fbcSSascha Wildner 		break;
100*a9656fbcSSascha Wildner 	case 0x43851002:
101*a9656fbcSSascha Wildner 		device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller");
102*a9656fbcSSascha Wildner 		/* XXX Maybe force polling right here? */
103*a9656fbcSSascha Wildner 		break;
104*a9656fbcSSascha Wildner 	default:
105*a9656fbcSSascha Wildner 		return (ENXIO);
106*a9656fbcSSascha Wildner 	}
107*a9656fbcSSascha Wildner 
108*a9656fbcSSascha Wildner 	return (BUS_PROBE_DEFAULT);
109*a9656fbcSSascha Wildner }
110*a9656fbcSSascha Wildner 
111*a9656fbcSSascha Wildner static int
112*a9656fbcSSascha Wildner intsmb_attach(device_t dev)
113*a9656fbcSSascha Wildner {
114*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
115*a9656fbcSSascha Wildner 	int error, rid, value;
116*a9656fbcSSascha Wildner 	int intr;
117*a9656fbcSSascha Wildner 	char *str;
118*a9656fbcSSascha Wildner 
119*a9656fbcSSascha Wildner 	sc->dev = dev;
120*a9656fbcSSascha Wildner 
121*a9656fbcSSascha Wildner 	lockinit(&sc->lock, "intsmb", 0, LK_CANRECURSE);
122*a9656fbcSSascha Wildner 
123*a9656fbcSSascha Wildner 	sc->cfg_irq9 = 0;
124*a9656fbcSSascha Wildner #ifndef NO_CHANGE_PCICONF
125*a9656fbcSSascha Wildner 	switch (pci_get_devid(dev)) {
126*a9656fbcSSascha Wildner 	case 0x71138086:	/* Intel 82371AB */
127*a9656fbcSSascha Wildner 	case 0x719b8086:	/* Intel 82443MX */
128*a9656fbcSSascha Wildner 		/* Changing configuration is allowed. */
129*a9656fbcSSascha Wildner 		sc->cfg_irq9 = 1;
130*a9656fbcSSascha Wildner 		break;
131*a9656fbcSSascha Wildner 	}
132*a9656fbcSSascha Wildner #endif
133*a9656fbcSSascha Wildner 
134*a9656fbcSSascha Wildner 	rid = PCI_BASE_ADDR_SMB;
135*a9656fbcSSascha Wildner 	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
136*a9656fbcSSascha Wildner 	    RF_ACTIVE);
137*a9656fbcSSascha Wildner 	if (sc->io_res == NULL) {
138*a9656fbcSSascha Wildner 		device_printf(dev, "Could not allocate I/O space\n");
139*a9656fbcSSascha Wildner 		error = ENXIO;
140*a9656fbcSSascha Wildner 		goto fail;
141*a9656fbcSSascha Wildner 	}
142*a9656fbcSSascha Wildner 
143*a9656fbcSSascha Wildner 	if (sc->cfg_irq9) {
144*a9656fbcSSascha Wildner 		pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
145*a9656fbcSSascha Wildner 		pci_write_config(dev, PCI_HST_CFG_SMB,
146*a9656fbcSSascha Wildner 		    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
147*a9656fbcSSascha Wildner 	}
148*a9656fbcSSascha Wildner 	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
149*a9656fbcSSascha Wildner 	sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0;
150*a9656fbcSSascha Wildner 	intr = value & PCI_INTR_SMB_MASK;
151*a9656fbcSSascha Wildner 	switch (intr) {
152*a9656fbcSSascha Wildner 	case PCI_INTR_SMB_SMI:
153*a9656fbcSSascha Wildner 		str = "SMI";
154*a9656fbcSSascha Wildner 		break;
155*a9656fbcSSascha Wildner 	case PCI_INTR_SMB_IRQ9:
156*a9656fbcSSascha Wildner 		str = "IRQ 9";
157*a9656fbcSSascha Wildner 		break;
158*a9656fbcSSascha Wildner 	case PCI_INTR_SMB_IRQ_PCI:
159*a9656fbcSSascha Wildner 		str = "PCI IRQ";
160*a9656fbcSSascha Wildner 		break;
161*a9656fbcSSascha Wildner 	default:
162*a9656fbcSSascha Wildner 		str = "BOGUS";
163*a9656fbcSSascha Wildner 	}
164*a9656fbcSSascha Wildner 
165*a9656fbcSSascha Wildner 	device_printf(dev, "intr %s %s ", str,
166*a9656fbcSSascha Wildner 	    sc->poll == 0 ? "enabled" : "disabled");
167*a9656fbcSSascha Wildner 	kprintf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
168*a9656fbcSSascha Wildner 
169*a9656fbcSSascha Wildner 	if (!sc->poll && intr == PCI_INTR_SMB_SMI) {
170*a9656fbcSSascha Wildner 		device_printf(dev,
171*a9656fbcSSascha Wildner 		    "using polling mode when configured interrupt is SMI\n");
172*a9656fbcSSascha Wildner 		sc->poll = 1;
173*a9656fbcSSascha Wildner 	}
174*a9656fbcSSascha Wildner 
175*a9656fbcSSascha Wildner 	if (sc->poll)
176*a9656fbcSSascha Wildner 	    goto no_intr;
177*a9656fbcSSascha Wildner 
178*a9656fbcSSascha Wildner 	if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) {
179*a9656fbcSSascha Wildner 		device_printf(dev, "Unsupported interrupt mode\n");
180*a9656fbcSSascha Wildner 		error = ENXIO;
181*a9656fbcSSascha Wildner 		goto fail;
182*a9656fbcSSascha Wildner 	}
183*a9656fbcSSascha Wildner 
184*a9656fbcSSascha Wildner 	/* Force IRQ 9. */
185*a9656fbcSSascha Wildner 	rid = 0;
186*a9656fbcSSascha Wildner 	if (sc->cfg_irq9)
187*a9656fbcSSascha Wildner 		bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
188*a9656fbcSSascha Wildner 
189*a9656fbcSSascha Wildner 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
190*a9656fbcSSascha Wildner 	    RF_SHAREABLE | RF_ACTIVE);
191*a9656fbcSSascha Wildner 	if (sc->irq_res == NULL) {
192*a9656fbcSSascha Wildner 		device_printf(dev, "Could not allocate irq\n");
193*a9656fbcSSascha Wildner 		error = ENXIO;
194*a9656fbcSSascha Wildner 		goto fail;
195*a9656fbcSSascha Wildner 	}
196*a9656fbcSSascha Wildner 
197*a9656fbcSSascha Wildner 	error = bus_setup_intr(dev, sc->irq_res, 0,
198*a9656fbcSSascha Wildner 	    intsmb_rawintr, sc, &sc->irq_hand, NULL);
199*a9656fbcSSascha Wildner 	if (error) {
200*a9656fbcSSascha Wildner 		device_printf(dev, "Failed to map intr\n");
201*a9656fbcSSascha Wildner 		goto fail;
202*a9656fbcSSascha Wildner 	}
203*a9656fbcSSascha Wildner 
204*a9656fbcSSascha Wildner no_intr:
205*a9656fbcSSascha Wildner 	sc->isbusy = 0;
206*a9656fbcSSascha Wildner 	sc->smbus = device_add_child(dev, "smbus", -1);
207*a9656fbcSSascha Wildner 	if (sc->smbus == NULL) {
208*a9656fbcSSascha Wildner 		error = ENXIO;
209*a9656fbcSSascha Wildner 		goto fail;
210*a9656fbcSSascha Wildner 	}
211*a9656fbcSSascha Wildner 	error = device_probe_and_attach(sc->smbus);
212*a9656fbcSSascha Wildner 	if (error)
213*a9656fbcSSascha Wildner 		goto fail;
214*a9656fbcSSascha Wildner 
215*a9656fbcSSascha Wildner #ifdef ENABLE_ALART
216*a9656fbcSSascha Wildner 	/* Enable Arart */
217*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
218*a9656fbcSSascha Wildner #endif
219*a9656fbcSSascha Wildner 	return (0);
220*a9656fbcSSascha Wildner 
221*a9656fbcSSascha Wildner fail:
222*a9656fbcSSascha Wildner 	intsmb_detach(dev);
223*a9656fbcSSascha Wildner 	return (error);
224*a9656fbcSSascha Wildner }
225*a9656fbcSSascha Wildner 
226*a9656fbcSSascha Wildner static int
227*a9656fbcSSascha Wildner intsmb_detach(device_t dev)
228*a9656fbcSSascha Wildner {
229*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
230*a9656fbcSSascha Wildner 	int error;
231*a9656fbcSSascha Wildner 
232*a9656fbcSSascha Wildner 	error = bus_generic_detach(dev);
233*a9656fbcSSascha Wildner 	if (error)
234*a9656fbcSSascha Wildner 		return (error);
235*a9656fbcSSascha Wildner 
236*a9656fbcSSascha Wildner 	if (sc->smbus)
237*a9656fbcSSascha Wildner 		device_delete_child(dev, sc->smbus);
238*a9656fbcSSascha Wildner 	if (sc->irq_hand)
239*a9656fbcSSascha Wildner 		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
240*a9656fbcSSascha Wildner 	if (sc->irq_res)
241*a9656fbcSSascha Wildner 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
242*a9656fbcSSascha Wildner 	if (sc->io_res)
243*a9656fbcSSascha Wildner 		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
244*a9656fbcSSascha Wildner 		    sc->io_res);
245*a9656fbcSSascha Wildner 	lockuninit(&sc->lock);
246*a9656fbcSSascha Wildner 	return (0);
247*a9656fbcSSascha Wildner }
248*a9656fbcSSascha Wildner 
249*a9656fbcSSascha Wildner static void
250*a9656fbcSSascha Wildner intsmb_rawintr(void *arg)
251*a9656fbcSSascha Wildner {
252*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = arg;
253*a9656fbcSSascha Wildner 
254*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
255*a9656fbcSSascha Wildner 	intsmb_intr(sc);
256*a9656fbcSSascha Wildner 	intsmb_slvintr(sc);
257*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
258*a9656fbcSSascha Wildner }
259*a9656fbcSSascha Wildner 
260*a9656fbcSSascha Wildner static int
261*a9656fbcSSascha Wildner intsmb_callback(device_t dev, int index, void *data)
262*a9656fbcSSascha Wildner {
263*a9656fbcSSascha Wildner 	int error = 0;
264*a9656fbcSSascha Wildner 
265*a9656fbcSSascha Wildner 	switch (index) {
266*a9656fbcSSascha Wildner 	case SMB_REQUEST_BUS:
267*a9656fbcSSascha Wildner 		break;
268*a9656fbcSSascha Wildner 	case SMB_RELEASE_BUS:
269*a9656fbcSSascha Wildner 		break;
270*a9656fbcSSascha Wildner 	default:
271*a9656fbcSSascha Wildner 		error = EINVAL;
272*a9656fbcSSascha Wildner 	}
273*a9656fbcSSascha Wildner 
274*a9656fbcSSascha Wildner 	return (error);
275*a9656fbcSSascha Wildner }
276*a9656fbcSSascha Wildner 
277*a9656fbcSSascha Wildner /* Counterpart of smbtx_smb_free(). */
278*a9656fbcSSascha Wildner static int
279*a9656fbcSSascha Wildner intsmb_free(struct intsmb_softc *sc)
280*a9656fbcSSascha Wildner {
281*a9656fbcSSascha Wildner 
282*a9656fbcSSascha Wildner 	INTSMB_LOCK_ASSERT(sc);
283*a9656fbcSSascha Wildner 	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
284*a9656fbcSSascha Wildner #ifdef ENABLE_ALART
285*a9656fbcSSascha Wildner 	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
286*a9656fbcSSascha Wildner #endif
287*a9656fbcSSascha Wildner 	    sc->isbusy)
288*a9656fbcSSascha Wildner 		return (SMB_EBUSY);
289*a9656fbcSSascha Wildner 
290*a9656fbcSSascha Wildner 	sc->isbusy = 1;
291*a9656fbcSSascha Wildner 	/* Disable Interrupt in slave part. */
292*a9656fbcSSascha Wildner #ifndef ENABLE_ALART
293*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
294*a9656fbcSSascha Wildner #endif
295*a9656fbcSSascha Wildner 	/* Reset INTR Flag to prepare INTR. */
296*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
297*a9656fbcSSascha Wildner 	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
298*a9656fbcSSascha Wildner 	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
299*a9656fbcSSascha Wildner 	return (0);
300*a9656fbcSSascha Wildner }
301*a9656fbcSSascha Wildner 
302*a9656fbcSSascha Wildner static int
303*a9656fbcSSascha Wildner intsmb_intr(struct intsmb_softc *sc)
304*a9656fbcSSascha Wildner {
305*a9656fbcSSascha Wildner 	int status, tmp;
306*a9656fbcSSascha Wildner 
307*a9656fbcSSascha Wildner 	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
308*a9656fbcSSascha Wildner 	if (status & PIIX4_SMBHSTSTAT_BUSY)
309*a9656fbcSSascha Wildner 		return (1);
310*a9656fbcSSascha Wildner 
311*a9656fbcSSascha Wildner 	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
312*a9656fbcSSascha Wildner 	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
313*a9656fbcSSascha Wildner 
314*a9656fbcSSascha Wildner 		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
315*a9656fbcSSascha Wildner 		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
316*a9656fbcSSascha Wildner 		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
317*a9656fbcSSascha Wildner 		if (sc->isbusy) {
318*a9656fbcSSascha Wildner 			sc->isbusy = 0;
319*a9656fbcSSascha Wildner 			wakeup(sc);
320*a9656fbcSSascha Wildner 		}
321*a9656fbcSSascha Wildner 		return (0);
322*a9656fbcSSascha Wildner 	}
323*a9656fbcSSascha Wildner 	return (1); /* Not Completed */
324*a9656fbcSSascha Wildner }
325*a9656fbcSSascha Wildner 
326*a9656fbcSSascha Wildner static int
327*a9656fbcSSascha Wildner intsmb_slvintr(struct intsmb_softc *sc)
328*a9656fbcSSascha Wildner {
329*a9656fbcSSascha Wildner 	int status;
330*a9656fbcSSascha Wildner 
331*a9656fbcSSascha Wildner 	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
332*a9656fbcSSascha Wildner 	if (status & PIIX4_SMBSLVSTS_BUSY)
333*a9656fbcSSascha Wildner 		return (1);
334*a9656fbcSSascha Wildner 	if (status & PIIX4_SMBSLVSTS_ALART)
335*a9656fbcSSascha Wildner 		intsmb_alrintr(sc);
336*a9656fbcSSascha Wildner 	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
337*a9656fbcSSascha Wildner 		| PIIX4_SMBSLVSTS_SDW1)) {
338*a9656fbcSSascha Wildner 	}
339*a9656fbcSSascha Wildner 
340*a9656fbcSSascha Wildner 	/* Reset Status Register */
341*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
342*a9656fbcSSascha Wildner 	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
343*a9656fbcSSascha Wildner 	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
344*a9656fbcSSascha Wildner 	return (0);
345*a9656fbcSSascha Wildner }
346*a9656fbcSSascha Wildner 
347*a9656fbcSSascha Wildner static void
348*a9656fbcSSascha Wildner intsmb_alrintr(struct intsmb_softc *sc)
349*a9656fbcSSascha Wildner {
350*a9656fbcSSascha Wildner 	int slvcnt;
351*a9656fbcSSascha Wildner #ifdef ENABLE_ALART
352*a9656fbcSSascha Wildner 	int error;
353*a9656fbcSSascha Wildner 	uint8_t addr;
354*a9656fbcSSascha Wildner #endif
355*a9656fbcSSascha Wildner 
356*a9656fbcSSascha Wildner 	/* Stop generating INTR from ALART. */
357*a9656fbcSSascha Wildner 	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
358*a9656fbcSSascha Wildner #ifdef ENABLE_ALART
359*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
360*a9656fbcSSascha Wildner 	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
361*a9656fbcSSascha Wildner #endif
362*a9656fbcSSascha Wildner 	DELAY(5);
363*a9656fbcSSascha Wildner 
364*a9656fbcSSascha Wildner 	/* Ask bus who asserted it and then ask it what's the matter. */
365*a9656fbcSSascha Wildner #ifdef ENABLE_ALART
366*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
367*a9656fbcSSascha Wildner 	if (error)
368*a9656fbcSSascha Wildner 		return;
369*a9656fbcSSascha Wildner 
370*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
371*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
372*a9656fbcSSascha Wildner 	error = intsmb_stop_poll(sc);
373*a9656fbcSSascha Wildner 	if (error)
374*a9656fbcSSascha Wildner 		device_printf(sc->dev, "ALART: ERROR\n");
375*a9656fbcSSascha Wildner 	else {
376*a9656fbcSSascha Wildner 		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
377*a9656fbcSSascha Wildner 		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
378*a9656fbcSSascha Wildner 	}
379*a9656fbcSSascha Wildner 
380*a9656fbcSSascha Wildner 	/* Re-enable INTR from ALART. */
381*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
382*a9656fbcSSascha Wildner 	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
383*a9656fbcSSascha Wildner 	DELAY(5);
384*a9656fbcSSascha Wildner #endif
385*a9656fbcSSascha Wildner }
386*a9656fbcSSascha Wildner 
387*a9656fbcSSascha Wildner static void
388*a9656fbcSSascha Wildner intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
389*a9656fbcSSascha Wildner {
390*a9656fbcSSascha Wildner 	unsigned char tmp;
391*a9656fbcSSascha Wildner 
392*a9656fbcSSascha Wildner 	INTSMB_LOCK_ASSERT(sc);
393*a9656fbcSSascha Wildner 	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
394*a9656fbcSSascha Wildner 	tmp &= 0xe0;
395*a9656fbcSSascha Wildner 	tmp |= cmd;
396*a9656fbcSSascha Wildner 	tmp |= PIIX4_SMBHSTCNT_START;
397*a9656fbcSSascha Wildner 
398*a9656fbcSSascha Wildner 	/* While not in autoconfiguration enable interrupts. */
399*a9656fbcSSascha Wildner 	if (!sc->poll && !cold && !nointr)
400*a9656fbcSSascha Wildner 		tmp |= PIIX4_SMBHSTCNT_INTREN;
401*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
402*a9656fbcSSascha Wildner }
403*a9656fbcSSascha Wildner 
404*a9656fbcSSascha Wildner static int
405*a9656fbcSSascha Wildner intsmb_error(device_t dev, int status)
406*a9656fbcSSascha Wildner {
407*a9656fbcSSascha Wildner 	int error = 0;
408*a9656fbcSSascha Wildner 
409*a9656fbcSSascha Wildner 	if (status & PIIX4_SMBHSTSTAT_ERR)
410*a9656fbcSSascha Wildner 		error |= SMB_EBUSERR;
411*a9656fbcSSascha Wildner 	if (status & PIIX4_SMBHSTSTAT_BUSC)
412*a9656fbcSSascha Wildner 		error |= SMB_ECOLLI;
413*a9656fbcSSascha Wildner 	if (status & PIIX4_SMBHSTSTAT_FAIL)
414*a9656fbcSSascha Wildner 		error |= SMB_ENOACK;
415*a9656fbcSSascha Wildner 
416*a9656fbcSSascha Wildner 	if (error != 0 && bootverbose)
417*a9656fbcSSascha Wildner 		device_printf(dev, "error = %d, status = %#x\n", error, status);
418*a9656fbcSSascha Wildner 
419*a9656fbcSSascha Wildner 	return (error);
420*a9656fbcSSascha Wildner }
421*a9656fbcSSascha Wildner 
422*a9656fbcSSascha Wildner /*
423*a9656fbcSSascha Wildner  * Polling Code.
424*a9656fbcSSascha Wildner  *
425*a9656fbcSSascha Wildner  * Polling is not encouraged because it requires waiting for the
426*a9656fbcSSascha Wildner  * device if it is busy.
427*a9656fbcSSascha Wildner  * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
428*a9656fbcSSascha Wildner  * polling code then.
429*a9656fbcSSascha Wildner  */
430*a9656fbcSSascha Wildner static int
431*a9656fbcSSascha Wildner intsmb_stop_poll(struct intsmb_softc *sc)
432*a9656fbcSSascha Wildner {
433*a9656fbcSSascha Wildner 	int error, i, status, tmp;
434*a9656fbcSSascha Wildner 
435*a9656fbcSSascha Wildner 	INTSMB_LOCK_ASSERT(sc);
436*a9656fbcSSascha Wildner 
437*a9656fbcSSascha Wildner 	/* First, wait for busy to be set. */
438*a9656fbcSSascha Wildner 	for (i = 0; i < 0x7fff; i++)
439*a9656fbcSSascha Wildner 		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
440*a9656fbcSSascha Wildner 		    PIIX4_SMBHSTSTAT_BUSY)
441*a9656fbcSSascha Wildner 			break;
442*a9656fbcSSascha Wildner 
443*a9656fbcSSascha Wildner 	/* Wait for busy to clear. */
444*a9656fbcSSascha Wildner 	for (i = 0; i < 0x7fff; i++) {
445*a9656fbcSSascha Wildner 		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
446*a9656fbcSSascha Wildner 		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
447*a9656fbcSSascha Wildner 			sc->isbusy = 0;
448*a9656fbcSSascha Wildner 			error = intsmb_error(sc->dev, status);
449*a9656fbcSSascha Wildner 			return (error);
450*a9656fbcSSascha Wildner 		}
451*a9656fbcSSascha Wildner 	}
452*a9656fbcSSascha Wildner 
453*a9656fbcSSascha Wildner 	/* Timed out waiting for busy to clear. */
454*a9656fbcSSascha Wildner 	sc->isbusy = 0;
455*a9656fbcSSascha Wildner 	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
456*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
457*a9656fbcSSascha Wildner 	return (SMB_ETIMEOUT);
458*a9656fbcSSascha Wildner }
459*a9656fbcSSascha Wildner 
460*a9656fbcSSascha Wildner /*
461*a9656fbcSSascha Wildner  * Wait for completion and return result.
462*a9656fbcSSascha Wildner  */
463*a9656fbcSSascha Wildner static int
464*a9656fbcSSascha Wildner intsmb_stop(struct intsmb_softc *sc)
465*a9656fbcSSascha Wildner {
466*a9656fbcSSascha Wildner 	int error, status;
467*a9656fbcSSascha Wildner 
468*a9656fbcSSascha Wildner 	INTSMB_LOCK_ASSERT(sc);
469*a9656fbcSSascha Wildner 
470*a9656fbcSSascha Wildner 	if (sc->poll || cold)
471*a9656fbcSSascha Wildner 		/* So that it can use device during device probe on SMBus. */
472*a9656fbcSSascha Wildner 		return (intsmb_stop_poll(sc));
473*a9656fbcSSascha Wildner 
474*a9656fbcSSascha Wildner 	error = lksleep(sc, &sc->lock, PCATCH, "SMBWAI", hz / 8);
475*a9656fbcSSascha Wildner 	if (error == 0) {
476*a9656fbcSSascha Wildner 		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
477*a9656fbcSSascha Wildner 		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
478*a9656fbcSSascha Wildner 			error = intsmb_error(sc->dev, status);
479*a9656fbcSSascha Wildner 			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
480*a9656fbcSSascha Wildner 				device_printf(sc->dev, "unknown cause why?\n");
481*a9656fbcSSascha Wildner #ifdef ENABLE_ALART
482*a9656fbcSSascha Wildner 			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
483*a9656fbcSSascha Wildner 			    PIIX4_SMBSLVCNT_ALTEN);
484*a9656fbcSSascha Wildner #endif
485*a9656fbcSSascha Wildner 			return (error);
486*a9656fbcSSascha Wildner 		}
487*a9656fbcSSascha Wildner 	}
488*a9656fbcSSascha Wildner 
489*a9656fbcSSascha Wildner 	/* Timeout Procedure. */
490*a9656fbcSSascha Wildner 	sc->isbusy = 0;
491*a9656fbcSSascha Wildner 
492*a9656fbcSSascha Wildner 	/* Re-enable supressed interrupt from slave part. */
493*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
494*a9656fbcSSascha Wildner 	if (error == EWOULDBLOCK)
495*a9656fbcSSascha Wildner 		return (SMB_ETIMEOUT);
496*a9656fbcSSascha Wildner 	else
497*a9656fbcSSascha Wildner 		return (SMB_EABORT);
498*a9656fbcSSascha Wildner }
499*a9656fbcSSascha Wildner 
500*a9656fbcSSascha Wildner static int
501*a9656fbcSSascha Wildner intsmb_quick(device_t dev, u_char slave, int how)
502*a9656fbcSSascha Wildner {
503*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
504*a9656fbcSSascha Wildner 	int error;
505*a9656fbcSSascha Wildner 	u_char data;
506*a9656fbcSSascha Wildner 
507*a9656fbcSSascha Wildner 	data = slave;
508*a9656fbcSSascha Wildner 
509*a9656fbcSSascha Wildner 	/* Quick command is part of Address, I think. */
510*a9656fbcSSascha Wildner 	switch(how) {
511*a9656fbcSSascha Wildner 	case SMB_QWRITE:
512*a9656fbcSSascha Wildner 		data &= ~LSB;
513*a9656fbcSSascha Wildner 		break;
514*a9656fbcSSascha Wildner 	case SMB_QREAD:
515*a9656fbcSSascha Wildner 		data |= LSB;
516*a9656fbcSSascha Wildner 		break;
517*a9656fbcSSascha Wildner 	default:
518*a9656fbcSSascha Wildner 		return (EINVAL);
519*a9656fbcSSascha Wildner 	}
520*a9656fbcSSascha Wildner 
521*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
522*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
523*a9656fbcSSascha Wildner 	if (error) {
524*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
525*a9656fbcSSascha Wildner 		return (error);
526*a9656fbcSSascha Wildner 	}
527*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
528*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
529*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
530*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
531*a9656fbcSSascha Wildner 	return (error);
532*a9656fbcSSascha Wildner }
533*a9656fbcSSascha Wildner 
534*a9656fbcSSascha Wildner static int
535*a9656fbcSSascha Wildner intsmb_sendb(device_t dev, u_char slave, char byte)
536*a9656fbcSSascha Wildner {
537*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
538*a9656fbcSSascha Wildner 	int error;
539*a9656fbcSSascha Wildner 
540*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
541*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
542*a9656fbcSSascha Wildner 	if (error) {
543*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
544*a9656fbcSSascha Wildner 		return (error);
545*a9656fbcSSascha Wildner 	}
546*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
547*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
548*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
549*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
550*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
551*a9656fbcSSascha Wildner 	return (error);
552*a9656fbcSSascha Wildner }
553*a9656fbcSSascha Wildner 
554*a9656fbcSSascha Wildner static int
555*a9656fbcSSascha Wildner intsmb_recvb(device_t dev, u_char slave, char *byte)
556*a9656fbcSSascha Wildner {
557*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
558*a9656fbcSSascha Wildner 	int error;
559*a9656fbcSSascha Wildner 
560*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
561*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
562*a9656fbcSSascha Wildner 	if (error) {
563*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
564*a9656fbcSSascha Wildner 		return (error);
565*a9656fbcSSascha Wildner 	}
566*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
567*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
568*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
569*a9656fbcSSascha Wildner 	if (error == 0) {
570*a9656fbcSSascha Wildner #ifdef RECV_IS_IN_CMD
571*a9656fbcSSascha Wildner 		/*
572*a9656fbcSSascha Wildner 		 * Linux SMBus stuff also troubles
573*a9656fbcSSascha Wildner 		 * Because Intel's datasheet does not make clear.
574*a9656fbcSSascha Wildner 		 */
575*a9656fbcSSascha Wildner 		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
576*a9656fbcSSascha Wildner #else
577*a9656fbcSSascha Wildner 		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
578*a9656fbcSSascha Wildner #endif
579*a9656fbcSSascha Wildner 	}
580*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
581*a9656fbcSSascha Wildner 	return (error);
582*a9656fbcSSascha Wildner }
583*a9656fbcSSascha Wildner 
584*a9656fbcSSascha Wildner static int
585*a9656fbcSSascha Wildner intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
586*a9656fbcSSascha Wildner {
587*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
588*a9656fbcSSascha Wildner 	int error;
589*a9656fbcSSascha Wildner 
590*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
591*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
592*a9656fbcSSascha Wildner 	if (error) {
593*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
594*a9656fbcSSascha Wildner 		return (error);
595*a9656fbcSSascha Wildner 	}
596*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
597*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
598*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
599*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
600*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
601*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
602*a9656fbcSSascha Wildner 	return (error);
603*a9656fbcSSascha Wildner }
604*a9656fbcSSascha Wildner 
605*a9656fbcSSascha Wildner static int
606*a9656fbcSSascha Wildner intsmb_writew(device_t dev, u_char slave, char cmd, short word)
607*a9656fbcSSascha Wildner {
608*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
609*a9656fbcSSascha Wildner 	int error;
610*a9656fbcSSascha Wildner 
611*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
612*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
613*a9656fbcSSascha Wildner 	if (error) {
614*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
615*a9656fbcSSascha Wildner 		return (error);
616*a9656fbcSSascha Wildner 	}
617*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
618*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
619*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
620*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
621*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
622*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
623*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
624*a9656fbcSSascha Wildner 	return (error);
625*a9656fbcSSascha Wildner }
626*a9656fbcSSascha Wildner 
627*a9656fbcSSascha Wildner static int
628*a9656fbcSSascha Wildner intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
629*a9656fbcSSascha Wildner {
630*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
631*a9656fbcSSascha Wildner 	int error;
632*a9656fbcSSascha Wildner 
633*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
634*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
635*a9656fbcSSascha Wildner 	if (error) {
636*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
637*a9656fbcSSascha Wildner 		return (error);
638*a9656fbcSSascha Wildner 	}
639*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
640*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
641*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
642*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
643*a9656fbcSSascha Wildner 	if (error == 0)
644*a9656fbcSSascha Wildner 		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
645*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
646*a9656fbcSSascha Wildner 	return (error);
647*a9656fbcSSascha Wildner }
648*a9656fbcSSascha Wildner 
649*a9656fbcSSascha Wildner static int
650*a9656fbcSSascha Wildner intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
651*a9656fbcSSascha Wildner {
652*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
653*a9656fbcSSascha Wildner 	int error;
654*a9656fbcSSascha Wildner 
655*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
656*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
657*a9656fbcSSascha Wildner 	if (error) {
658*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
659*a9656fbcSSascha Wildner 		return (error);
660*a9656fbcSSascha Wildner 	}
661*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
662*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
663*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
664*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
665*a9656fbcSSascha Wildner 	if (error == 0) {
666*a9656fbcSSascha Wildner 		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
667*a9656fbcSSascha Wildner 		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
668*a9656fbcSSascha Wildner 	}
669*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
670*a9656fbcSSascha Wildner 	return (error);
671*a9656fbcSSascha Wildner }
672*a9656fbcSSascha Wildner 
673*a9656fbcSSascha Wildner /*
674*a9656fbcSSascha Wildner  * Data sheet claims that it implements all function, but also claims
675*a9656fbcSSascha Wildner  * that it implements 7 function and not mention PCALL. So I don't know
676*a9656fbcSSascha Wildner  * whether it will work.
677*a9656fbcSSascha Wildner  */
678*a9656fbcSSascha Wildner static int
679*a9656fbcSSascha Wildner intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
680*a9656fbcSSascha Wildner {
681*a9656fbcSSascha Wildner #ifdef PROCCALL_TEST
682*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
683*a9656fbcSSascha Wildner 	int error;
684*a9656fbcSSascha Wildner 
685*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
686*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
687*a9656fbcSSascha Wildner 	if (error) {
688*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
689*a9656fbcSSascha Wildner 		return (error);
690*a9656fbcSSascha Wildner 	}
691*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
692*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
693*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
694*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
695*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
696*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
697*a9656fbcSSascha Wildner 	if (error == 0) {
698*a9656fbcSSascha Wildner 		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
699*a9656fbcSSascha Wildner 		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
700*a9656fbcSSascha Wildner 	}
701*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
702*a9656fbcSSascha Wildner 	return (error);
703*a9656fbcSSascha Wildner #else
704*a9656fbcSSascha Wildner 	return (SMB_ENOTSUPP);
705*a9656fbcSSascha Wildner #endif
706*a9656fbcSSascha Wildner }
707*a9656fbcSSascha Wildner 
708*a9656fbcSSascha Wildner static int
709*a9656fbcSSascha Wildner intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
710*a9656fbcSSascha Wildner {
711*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
712*a9656fbcSSascha Wildner 	int error, i;
713*a9656fbcSSascha Wildner 
714*a9656fbcSSascha Wildner 	if (count > SMBBLOCKTRANS_MAX || count == 0)
715*a9656fbcSSascha Wildner 		return (SMB_EINVAL);
716*a9656fbcSSascha Wildner 
717*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
718*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
719*a9656fbcSSascha Wildner 	if (error) {
720*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
721*a9656fbcSSascha Wildner 		return (error);
722*a9656fbcSSascha Wildner 	}
723*a9656fbcSSascha Wildner 
724*a9656fbcSSascha Wildner 	/* Reset internal array index. */
725*a9656fbcSSascha Wildner 	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
726*a9656fbcSSascha Wildner 
727*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
728*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
729*a9656fbcSSascha Wildner 	for (i = 0; i < count; i++)
730*a9656fbcSSascha Wildner 		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
731*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
732*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
733*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
734*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
735*a9656fbcSSascha Wildner 	return (error);
736*a9656fbcSSascha Wildner }
737*a9656fbcSSascha Wildner 
738*a9656fbcSSascha Wildner static int
739*a9656fbcSSascha Wildner intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
740*a9656fbcSSascha Wildner {
741*a9656fbcSSascha Wildner 	struct intsmb_softc *sc = device_get_softc(dev);
742*a9656fbcSSascha Wildner 	int error, i;
743*a9656fbcSSascha Wildner 	u_char data, nread;
744*a9656fbcSSascha Wildner 
745*a9656fbcSSascha Wildner 	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
746*a9656fbcSSascha Wildner 		return (SMB_EINVAL);
747*a9656fbcSSascha Wildner 
748*a9656fbcSSascha Wildner 	INTSMB_LOCK(sc);
749*a9656fbcSSascha Wildner 	error = intsmb_free(sc);
750*a9656fbcSSascha Wildner 	if (error) {
751*a9656fbcSSascha Wildner 		INTSMB_UNLOCK(sc);
752*a9656fbcSSascha Wildner 		return (error);
753*a9656fbcSSascha Wildner 	}
754*a9656fbcSSascha Wildner 
755*a9656fbcSSascha Wildner 	/* Reset internal array index. */
756*a9656fbcSSascha Wildner 	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
757*a9656fbcSSascha Wildner 
758*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
759*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
760*a9656fbcSSascha Wildner 	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
761*a9656fbcSSascha Wildner 	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
762*a9656fbcSSascha Wildner 	error = intsmb_stop(sc);
763*a9656fbcSSascha Wildner 	if (error == 0) {
764*a9656fbcSSascha Wildner 		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
765*a9656fbcSSascha Wildner 		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
766*a9656fbcSSascha Wildner 			for (i = 0; i < nread; i++) {
767*a9656fbcSSascha Wildner 				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
768*a9656fbcSSascha Wildner 				if (i < *count)
769*a9656fbcSSascha Wildner 					buf[i] = data;
770*a9656fbcSSascha Wildner 			}
771*a9656fbcSSascha Wildner 			*count = nread;
772*a9656fbcSSascha Wildner 		} else
773*a9656fbcSSascha Wildner 			error = EIO;
774*a9656fbcSSascha Wildner 	}
775*a9656fbcSSascha Wildner 	INTSMB_UNLOCK(sc);
776*a9656fbcSSascha Wildner 	return (error);
777*a9656fbcSSascha Wildner }
778*a9656fbcSSascha Wildner 
779*a9656fbcSSascha Wildner static devclass_t intsmb_devclass;
780*a9656fbcSSascha Wildner 
781*a9656fbcSSascha Wildner static device_method_t intsmb_methods[] = {
782*a9656fbcSSascha Wildner 	/* Device interface */
783*a9656fbcSSascha Wildner 	DEVMETHOD(device_probe,		intsmb_probe),
784*a9656fbcSSascha Wildner 	DEVMETHOD(device_attach,	intsmb_attach),
785*a9656fbcSSascha Wildner 	DEVMETHOD(device_detach,	intsmb_detach),
786*a9656fbcSSascha Wildner 
787*a9656fbcSSascha Wildner 	/* Bus interface */
788*a9656fbcSSascha Wildner 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
789*a9656fbcSSascha Wildner 
790*a9656fbcSSascha Wildner 	/* SMBus interface */
791*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_callback,	intsmb_callback),
792*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_quick,		intsmb_quick),
793*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_sendb,		intsmb_sendb),
794*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_recvb,		intsmb_recvb),
795*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_writeb,		intsmb_writeb),
796*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_writew,		intsmb_writew),
797*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_readb,		intsmb_readb),
798*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_readw,		intsmb_readw),
799*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_pcall,		intsmb_pcall),
800*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
801*a9656fbcSSascha Wildner 	DEVMETHOD(smbus_bread,		intsmb_bread),
802*a9656fbcSSascha Wildner 
803*a9656fbcSSascha Wildner 	{ 0, 0 }
804*a9656fbcSSascha Wildner };
805*a9656fbcSSascha Wildner 
806*a9656fbcSSascha Wildner static driver_t intsmb_driver = {
807*a9656fbcSSascha Wildner 	"intsmb",
808*a9656fbcSSascha Wildner 	intsmb_methods,
809*a9656fbcSSascha Wildner 	sizeof(struct intsmb_softc),
810*a9656fbcSSascha Wildner };
811*a9656fbcSSascha Wildner 
812*a9656fbcSSascha Wildner DRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
813*a9656fbcSSascha Wildner DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
814*a9656fbcSSascha Wildner MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
815*a9656fbcSSascha Wildner MODULE_VERSION(intsmb, 1);
816