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