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