143166Snsouch/*-
243166Snsouch * Copyright (c) 1998, 1999 Takanori Watanabe
343166Snsouch * All rights reserved.
443166Snsouch *
543166Snsouch * Redistribution and use in source and binary forms, with or without
643166Snsouch * modification, are permitted provided that the following conditions
743166Snsouch * are met:
843166Snsouch * 1. Redistributions of source code must retain the above copyright
943166Snsouch *        notice, this list of conditions and the following disclaimer.
1043166Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1143166Snsouch *        notice, this list of conditions and the following disclaimer in the
1243166Snsouch *        documentation and/or other materials provided with the distribution.
1343166Snsouch *
1443166Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1543166Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1643166Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1743166Snsouch * ARE DISCLAIMED.    IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1843166Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1943166Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2043166Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2143166Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2243166Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2343166Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2443166Snsouch * SUCH DAMAGE.
2543166Snsouch */
2643166Snsouch
27116192Sobrien#include <sys/cdefs.h>
28116192Sobrien__FBSDID("$FreeBSD$");
29116192Sobrien
3043166Snsouch#include <sys/param.h>
3143166Snsouch#include <sys/systm.h>
32165951Sjhb#include <sys/bus.h>
3343166Snsouch#include <sys/kernel.h>
34165951Sjhb#include <sys/lock.h>
3543166Snsouch#include <sys/module.h>
36165951Sjhb#include <sys/mutex.h>
3746651Speter#include <sys/rman.h>
38165951Sjhb#include <machine/bus.h>
3943166Snsouch#include <dev/smbus/smbconf.h>
4043166Snsouch
4143166Snsouch#include "smbus_if.h"
4243166Snsouch
43119288Simp#include <dev/pci/pcireg.h>
44119288Simp#include <dev/pci/pcivar.h>
4543166Snsouch#include <pci/intpmreg.h>
4643166Snsouch
4743166Snsouch#include "opt_intpm.h"
4843166Snsouch
49165951Sjhbstruct intsmb_softc {
50165951Sjhb	device_t		dev;
51165951Sjhb	struct resource		*io_res;
52165951Sjhb	struct resource		*irq_res;
53165951Sjhb	void			*irq_hand;
54165951Sjhb	device_t		smbus;
55165951Sjhb	int			isbusy;
56197128Savg	int			cfg_irq9;
57197128Savg	int			poll;
58165951Sjhb	struct mtx		lock;
5943166Snsouch};
60162289Sjhb
61165951Sjhb#define	INTSMB_LOCK(sc)		mtx_lock(&(sc)->lock)
62165951Sjhb#define	INTSMB_UNLOCK(sc)	mtx_unlock(&(sc)->lock)
63165951Sjhb#define	INTSMB_LOCK_ASSERT(sc)	mtx_assert(&(sc)->lock, MA_OWNED)
64165951Sjhb
6543166Snsouchstatic int intsmb_probe(device_t);
6643166Snsouchstatic int intsmb_attach(device_t);
67165951Sjhbstatic int intsmb_detach(device_t);
68165951Sjhbstatic int intsmb_intr(struct intsmb_softc *sc);
69165951Sjhbstatic int intsmb_slvintr(struct intsmb_softc *sc);
70165951Sjhbstatic void intsmb_alrintr(struct intsmb_softc *sc);
71162234Sjhbstatic int intsmb_callback(device_t dev, int index, void *data);
7243166Snsouchstatic int intsmb_quick(device_t dev, u_char slave, int how);
7343166Snsouchstatic int intsmb_sendb(device_t dev, u_char slave, char byte);
7443166Snsouchstatic int intsmb_recvb(device_t dev, u_char slave, char *byte);
7543166Snsouchstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
7643166Snsouchstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
7743166Snsouchstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
7843166Snsouchstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
7943166Snsouchstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
8043166Snsouchstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
81162234Sjhbstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
82165951Sjhbstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr);
83165951Sjhbstatic int intsmb_stop(struct intsmb_softc *sc);
84165951Sjhbstatic int intsmb_stop_poll(struct intsmb_softc *sc);
85165951Sjhbstatic int intsmb_free(struct intsmb_softc *sc);
86165951Sjhbstatic void intsmb_rawintr(void *arg);
87162289Sjhb
88165951Sjhbstatic int
89165951Sjhbintsmb_probe(device_t dev)
90165951Sjhb{
9143166Snsouch
92165951Sjhb	switch (pci_get_devid(dev)) {
93165951Sjhb	case 0x71138086:	/* Intel 82371AB */
94165951Sjhb	case 0x719b8086:	/* Intel 82443MX */
95165951Sjhb#if 0
96165951Sjhb	/* Not a good idea yet, this stops isab0 functioning */
97165951Sjhb	case 0x02001166:	/* ServerWorks OSB4 */
98165951Sjhb#endif
99165951Sjhb		device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
100165951Sjhb		break;
101234677Savg	case 0x43721002:
102234677Savg		device_set_desc(dev, "ATI IXP400 SMBus Controller");
103234677Savg		break;
104197128Savg	case 0x43851002:
105234213Savg		/* SB800 and newer can not be configured in a compatible way. */
106234213Savg		if (pci_get_revid(dev) >= 0x40)
107234213Savg			return (ENXIO);
108197128Savg		device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller");
109197128Savg		/* XXX Maybe force polling right here? */
110197128Savg		break;
111165951Sjhb	default:
112165951Sjhb		return (ENXIO);
113165951Sjhb	}
11443166Snsouch
115165951Sjhb	return (BUS_PROBE_DEFAULT);
116165951Sjhb}
117162289Sjhb
118165951Sjhbstatic int
119165951Sjhbintsmb_attach(device_t dev)
120165951Sjhb{
121165951Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
122165951Sjhb	int error, rid, value;
123197128Savg	int intr;
124165951Sjhb	char *str;
125162289Sjhb
126178972Sjhb	sc->dev = dev;
127178972Sjhb
128165951Sjhb	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
12943166Snsouch
130197128Savg	sc->cfg_irq9 = 0;
131197128Savg#ifndef NO_CHANGE_PCICONF
132197128Savg	switch (pci_get_devid(dev)) {
133197128Savg	case 0x71138086:	/* Intel 82371AB */
134197128Savg	case 0x719b8086:	/* Intel 82443MX */
135197128Savg		/* Changing configuration is allowed. */
136197128Savg		sc->cfg_irq9 = 1;
137197128Savg		break;
138197128Savg	}
139197128Savg#endif
140197128Savg
141165951Sjhb	rid = PCI_BASE_ADDR_SMB;
142165951Sjhb	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
143165951Sjhb	    RF_ACTIVE);
144165951Sjhb	if (sc->io_res == NULL) {
145165951Sjhb		device_printf(dev, "Could not allocate I/O space\n");
146165951Sjhb		error = ENXIO;
147165951Sjhb		goto fail;
148165951Sjhb	}
14943166Snsouch
150197128Savg	if (sc->cfg_irq9) {
151197128Savg		pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
152197128Savg		pci_write_config(dev, PCI_HST_CFG_SMB,
153197128Savg		    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
154197128Savg	}
155165951Sjhb	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
156197128Savg	sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0;
157197128Savg	intr = value & PCI_INTR_SMB_MASK;
158197128Savg	switch (intr) {
159165951Sjhb	case PCI_INTR_SMB_SMI:
160165951Sjhb		str = "SMI";
161165951Sjhb		break;
162165951Sjhb	case PCI_INTR_SMB_IRQ9:
163165951Sjhb		str = "IRQ 9";
164165951Sjhb		break;
165197128Savg	case PCI_INTR_SMB_IRQ_PCI:
166197128Savg		str = "PCI IRQ";
167197128Savg		break;
168165951Sjhb	default:
169165951Sjhb		str = "BOGUS";
170165951Sjhb	}
171197128Savg
172165951Sjhb	device_printf(dev, "intr %s %s ", str,
173197128Savg	    sc->poll == 0 ? "enabled" : "disabled");
174168870Sjhb	printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
17543166Snsouch
176197325Savg	if (!sc->poll && intr == PCI_INTR_SMB_SMI) {
177197325Savg		device_printf(dev,
178197325Savg		    "using polling mode when configured interrupt is SMI\n");
179197325Savg		sc->poll = 1;
180197325Savg	}
181197325Savg
182197128Savg	if (sc->poll)
183197128Savg	    goto no_intr;
184197128Savg
185197128Savg	if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) {
186165951Sjhb		device_printf(dev, "Unsupported interrupt mode\n");
187165951Sjhb		error = ENXIO;
188165951Sjhb		goto fail;
189165951Sjhb	}
19046651Speter
191165951Sjhb	/* Force IRQ 9. */
192165951Sjhb	rid = 0;
193197128Savg	if (sc->cfg_irq9)
194197128Savg		bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
195197128Savg
196165951Sjhb	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
197165951Sjhb	    RF_SHAREABLE | RF_ACTIVE);
198165951Sjhb	if (sc->irq_res == NULL) {
199165951Sjhb		device_printf(dev, "Could not allocate irq\n");
200165951Sjhb		error = ENXIO;
201165951Sjhb		goto fail;
202165951Sjhb	}
20343166Snsouch
204179622Sjhb	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
205179622Sjhb	    NULL, intsmb_rawintr, sc, &sc->irq_hand);
206165951Sjhb	if (error) {
207165951Sjhb		device_printf(dev, "Failed to map intr\n");
208165951Sjhb		goto fail;
209165951Sjhb	}
210162289Sjhb
211197128Savgno_intr:
212165951Sjhb	sc->isbusy = 0;
213165951Sjhb	sc->smbus = device_add_child(dev, "smbus", -1);
214165951Sjhb	if (sc->smbus == NULL) {
215165951Sjhb		error = ENXIO;
216165951Sjhb		goto fail;
217165951Sjhb	}
218165951Sjhb	error = device_probe_and_attach(sc->smbus);
219165951Sjhb	if (error)
220165951Sjhb		goto fail;
221162289Sjhb
222165951Sjhb#ifdef ENABLE_ALART
223165951Sjhb	/* Enable Arart */
224165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
225165951Sjhb#endif
226165951Sjhb	return (0);
22743166Snsouch
228165951Sjhbfail:
229165951Sjhb	intsmb_detach(dev);
230165951Sjhb	return (error);
231165951Sjhb}
232165951Sjhb
233162289Sjhbstatic int
234165951Sjhbintsmb_detach(device_t dev)
23543166Snsouch{
236162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
237165951Sjhb	int error;
238162289Sjhb
239165951Sjhb	error = bus_generic_detach(dev);
240165951Sjhb	if (error)
241165951Sjhb		return (error);
242162289Sjhb
243165951Sjhb	if (sc->smbus)
244165951Sjhb		device_delete_child(dev, sc->smbus);
245165951Sjhb	if (sc->irq_hand)
246165951Sjhb		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
247165951Sjhb	if (sc->irq_res)
248165951Sjhb		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
249165951Sjhb	if (sc->io_res)
250165951Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
251165951Sjhb		    sc->io_res);
252165951Sjhb	mtx_destroy(&sc->lock);
253165951Sjhb	return (0);
25443166Snsouch}
255165951Sjhb
256165951Sjhbstatic void
257165951Sjhbintsmb_rawintr(void *arg)
25843166Snsouch{
259165951Sjhb	struct intsmb_softc *sc = arg;
260162289Sjhb
261165951Sjhb	INTSMB_LOCK(sc);
262165951Sjhb	intsmb_intr(sc);
263165951Sjhb	intsmb_slvintr(sc);
264165951Sjhb	INTSMB_UNLOCK(sc);
26543166Snsouch}
26643166Snsouch
267162289Sjhbstatic int
268162234Sjhbintsmb_callback(device_t dev, int index, void *data)
26943166Snsouch{
27043166Snsouch	int error = 0;
271162289Sjhb
27243166Snsouch	switch (index) {
27343166Snsouch	case SMB_REQUEST_BUS:
27443166Snsouch		break;
27543166Snsouch	case SMB_RELEASE_BUS:
27643166Snsouch		break;
27743166Snsouch	default:
278234215Savg		error = SMB_EINVAL;
27943166Snsouch	}
280162289Sjhb
28143166Snsouch	return (error);
28243166Snsouch}
283162289Sjhb
284162289Sjhb/* Counterpart of smbtx_smb_free(). */
285162289Sjhbstatic int
286165951Sjhbintsmb_free(struct intsmb_softc *sc)
287162289Sjhb{
288162289Sjhb
289165951Sjhb	INTSMB_LOCK_ASSERT(sc);
290165951Sjhb	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
29143166Snsouch#ifdef ENABLE_ALART
292165951Sjhb	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
29343166Snsouch#endif
294162289Sjhb	    sc->isbusy)
295165951Sjhb		return (SMB_EBUSY);
296165951Sjhb
297162289Sjhb	sc->isbusy = 1;
298162289Sjhb	/* Disable Interrupt in slave part. */
29943166Snsouch#ifndef ENABLE_ALART
300165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
30143166Snsouch#endif
302162289Sjhb	/* Reset INTR Flag to prepare INTR. */
303165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
304165951Sjhb	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
305165951Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
306162289Sjhb	return (0);
30743166Snsouch}
30843166Snsouch
30943166Snsouchstatic int
310165951Sjhbintsmb_intr(struct intsmb_softc *sc)
31143166Snsouch{
312165951Sjhb	int status, tmp;
313162289Sjhb
314165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
315162289Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSY)
316162289Sjhb		return (1);
317162289Sjhb
318162289Sjhb	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
319162289Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
320162289Sjhb
321165951Sjhb		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
322165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
323162289Sjhb		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
324162289Sjhb		if (sc->isbusy) {
325162289Sjhb			sc->isbusy = 0;
326162289Sjhb			wakeup(sc);
32749064Snsouch		}
328162289Sjhb		return (0);
32943166Snsouch	}
330162289Sjhb	return (1); /* Not Completed */
33143166Snsouch}
332162289Sjhb
33343166Snsouchstatic int
334165951Sjhbintsmb_slvintr(struct intsmb_softc *sc)
33543166Snsouch{
336165951Sjhb	int status;
337162289Sjhb
338165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
339162289Sjhb	if (status & PIIX4_SMBSLVSTS_BUSY)
340165951Sjhb		return (1);
341165951Sjhb	if (status & PIIX4_SMBSLVSTS_ALART)
342165951Sjhb		intsmb_alrintr(sc);
343165951Sjhb	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
344162289Sjhb		| PIIX4_SMBSLVSTS_SDW1)) {
34543166Snsouch	}
346162289Sjhb
347162289Sjhb	/* Reset Status Register */
348165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
349162289Sjhb	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
350162289Sjhb	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
351165951Sjhb	return (0);
35243166Snsouch}
35343166Snsouch
354162289Sjhbstatic void
355165951Sjhbintsmb_alrintr(struct intsmb_softc *sc)
35643166Snsouch{
35743166Snsouch	int slvcnt;
35843288Sdillon#ifdef ENABLE_ALART
35943288Sdillon	int error;
360165951Sjhb	uint8_t addr;
36143288Sdillon#endif
36243288Sdillon
363162289Sjhb	/* Stop generating INTR from ALART. */
364165951Sjhb	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
36543166Snsouch#ifdef ENABLE_ALART
366165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
367162289Sjhb	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
36843166Snsouch#endif
36943166Snsouch	DELAY(5);
370162289Sjhb
371162289Sjhb	/* Ask bus who asserted it and then ask it what's the matter. */
37243166Snsouch#ifdef ENABLE_ALART
373165951Sjhb	error = intsmb_free(sc);
374165951Sjhb	if (error)
375165951Sjhb		return;
376162289Sjhb
377165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
378165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
379165951Sjhb	error = intsmb_stop_poll(sc);
380165951Sjhb	if (error)
381165951Sjhb		device_printf(sc->dev, "ALART: ERROR\n");
382165951Sjhb	else {
383165951Sjhb		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
384165951Sjhb		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
385165951Sjhb	}
38643166Snsouch
387162289Sjhb	/* Re-enable INTR from ALART. */
388165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
389162289Sjhb	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
39043166Snsouch	DELAY(5);
39143166Snsouch#endif
392162289Sjhb}
39343166Snsouch
39443166Snsouchstatic void
395165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
39643166Snsouch{
39743166Snsouch	unsigned char tmp;
398162289Sjhb
399165951Sjhb	INTSMB_LOCK_ASSERT(sc);
400165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
401162289Sjhb	tmp &= 0xe0;
40243166Snsouch	tmp |= cmd;
403162289Sjhb	tmp |= PIIX4_SMBHSTCNT_START;
404162289Sjhb
405162289Sjhb	/* While not in autoconfiguration enable interrupts. */
406197128Savg	if (!sc->poll && !cold && !nointr)
407162289Sjhb		tmp |= PIIX4_SMBHSTCNT_INTREN;
408165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
40943166Snsouch}
41043166Snsouch
411165951Sjhbstatic int
412189882Savgintsmb_error(device_t dev, int status)
413165951Sjhb{
414165951Sjhb	int error = 0;
415165951Sjhb
416165951Sjhb	if (status & PIIX4_SMBHSTSTAT_ERR)
417165951Sjhb		error |= SMB_EBUSERR;
418165951Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSC)
419165951Sjhb		error |= SMB_ECOLLI;
420165951Sjhb	if (status & PIIX4_SMBHSTSTAT_FAIL)
421165951Sjhb		error |= SMB_ENOACK;
422189882Savg
423189882Savg	if (error != 0 && bootverbose)
424189882Savg		device_printf(dev, "error = %d, status = %#x\n", error, status);
425189882Savg
426165951Sjhb	return (error);
427165951Sjhb}
428165951Sjhb
429162289Sjhb/*
430162289Sjhb * Polling Code.
431162289Sjhb *
432162289Sjhb * Polling is not encouraged because it requires waiting for the
433162289Sjhb * device if it is busy.
434162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
435162289Sjhb * polling code then.
43643166Snsouch */
437162289Sjhbstatic int
438165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc)
439162289Sjhb{
440165951Sjhb	int error, i, status, tmp;
44143166Snsouch
442165951Sjhb	INTSMB_LOCK_ASSERT(sc);
443165951Sjhb
444165951Sjhb	/* First, wait for busy to be set. */
445162289Sjhb	for (i = 0; i < 0x7fff; i++)
446165951Sjhb		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
447162289Sjhb		    PIIX4_SMBHSTSTAT_BUSY)
448162289Sjhb			break;
449162289Sjhb
450165951Sjhb	/* Wait for busy to clear. */
451162289Sjhb	for (i = 0; i < 0x7fff; i++) {
452165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
453162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
454162289Sjhb			sc->isbusy = 0;
455189882Savg			error = intsmb_error(sc->dev, status);
456162289Sjhb			return (error);
45743166Snsouch		}
45843166Snsouch	}
459162289Sjhb
460165951Sjhb	/* Timed out waiting for busy to clear. */
461162289Sjhb	sc->isbusy = 0;
462165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
463165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
464165951Sjhb	return (SMB_ETIMEOUT);
46543166Snsouch}
466162289Sjhb
46743166Snsouch/*
468162289Sjhb * Wait for completion and return result.
46943166Snsouch */
470162289Sjhbstatic int
471165951Sjhbintsmb_stop(struct intsmb_softc *sc)
472162289Sjhb{
473165951Sjhb	int error, status;
474162289Sjhb
475165951Sjhb	INTSMB_LOCK_ASSERT(sc);
476165951Sjhb
477197128Savg	if (sc->poll || cold)
478162289Sjhb		/* So that it can use device during device probe on SMBus. */
479165951Sjhb		return (intsmb_stop_poll(sc));
480162289Sjhb
481172667Sjhb	error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8);
482165951Sjhb	if (error == 0) {
483165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
484162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
485189882Savg			error = intsmb_error(sc->dev, status);
486162289Sjhb			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
487165951Sjhb				device_printf(sc->dev, "unknown cause why?\n");
48843166Snsouch#ifdef ENABLE_ALART
489165951Sjhb			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
490162289Sjhb			    PIIX4_SMBSLVCNT_ALTEN);
49143166Snsouch#endif
492162289Sjhb			return (error);
49343166Snsouch		}
49443166Snsouch	}
495162289Sjhb
496162289Sjhb	/* Timeout Procedure. */
497162289Sjhb	sc->isbusy = 0;
498162289Sjhb
499162289Sjhb	/* Re-enable supressed interrupt from slave part. */
500165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
501165951Sjhb	if (error == EWOULDBLOCK)
502165951Sjhb		return (SMB_ETIMEOUT);
503165951Sjhb	else
504165951Sjhb		return (SMB_EABORT);
50543166Snsouch}
50643166Snsouch
50743166Snsouchstatic int
50843166Snsouchintsmb_quick(device_t dev, u_char slave, int how)
50943166Snsouch{
510162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
511165951Sjhb	int error;
512162289Sjhb	u_char data;
513162289Sjhb
514162289Sjhb	data = slave;
515162289Sjhb
516162289Sjhb	/* Quick command is part of Address, I think. */
517162289Sjhb	switch(how) {
518162289Sjhb	case SMB_QWRITE:
519162289Sjhb		data &= ~LSB;
52043166Snsouch		break;
521162289Sjhb	case SMB_QREAD:
522162289Sjhb		data |= LSB;
523162289Sjhb		break;
524162289Sjhb	default:
525234215Savg		return (SMB_EINVAL);
526162289Sjhb	}
527165951Sjhb
528165951Sjhb	INTSMB_LOCK(sc);
529165951Sjhb	error = intsmb_free(sc);
530165951Sjhb	if (error) {
531165951Sjhb		INTSMB_UNLOCK(sc);
532165951Sjhb		return (error);
533162289Sjhb	}
534165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
535165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
536165951Sjhb	error = intsmb_stop(sc);
537165951Sjhb	INTSMB_UNLOCK(sc);
538162289Sjhb	return (error);
53943166Snsouch}
54043166Snsouch
54143166Snsouchstatic int
54243166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte)
54343166Snsouch{
544162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
545162289Sjhb	int error;
546162289Sjhb
547165951Sjhb	INTSMB_LOCK(sc);
548165951Sjhb	error = intsmb_free(sc);
549165951Sjhb	if (error) {
550165951Sjhb		INTSMB_UNLOCK(sc);
551165951Sjhb		return (error);
552162289Sjhb	}
553165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
554165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
555165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
556165951Sjhb	error = intsmb_stop(sc);
557165951Sjhb	INTSMB_UNLOCK(sc);
558162289Sjhb	return (error);
55943166Snsouch}
560162289Sjhb
56143166Snsouchstatic int
56243166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte)
56343166Snsouch{
564162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
565162289Sjhb	int error;
566162289Sjhb
567165951Sjhb	INTSMB_LOCK(sc);
568165951Sjhb	error = intsmb_free(sc);
569165951Sjhb	if (error) {
570165951Sjhb		INTSMB_UNLOCK(sc);
571165951Sjhb		return (error);
572165951Sjhb	}
573165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
574165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
575165951Sjhb	error = intsmb_stop(sc);
576165951Sjhb	if (error == 0) {
57743166Snsouch#ifdef RECV_IS_IN_CMD
578165951Sjhb		/*
579165951Sjhb		 * Linux SMBus stuff also troubles
580165951Sjhb		 * Because Intel's datasheet does not make clear.
581165951Sjhb		 */
582165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
58343166Snsouch#else
584165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
58543166Snsouch#endif
586162289Sjhb	}
587165951Sjhb	INTSMB_UNLOCK(sc);
588162289Sjhb	return (error);
58943166Snsouch}
590162289Sjhb
59143166Snsouchstatic int
59243166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
59343166Snsouch{
594162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
595162289Sjhb	int error;
596162289Sjhb
597165951Sjhb	INTSMB_LOCK(sc);
598165951Sjhb	error = intsmb_free(sc);
599165951Sjhb	if (error) {
600165951Sjhb		INTSMB_UNLOCK(sc);
601165951Sjhb		return (error);
602162289Sjhb	}
603165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
604165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
605165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
606165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
607165951Sjhb	error = intsmb_stop(sc);
608165951Sjhb	INTSMB_UNLOCK(sc);
609162289Sjhb	return (error);
61043166Snsouch}
611162289Sjhb
61243166Snsouchstatic int
61343166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word)
61443166Snsouch{
615162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
616162289Sjhb	int error;
617162289Sjhb
618165951Sjhb	INTSMB_LOCK(sc);
619165951Sjhb	error = intsmb_free(sc);
620165951Sjhb	if (error) {
621165951Sjhb		INTSMB_UNLOCK(sc);
622165951Sjhb		return (error);
623162289Sjhb	}
624165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
625165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
626165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
627165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
628165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
629165951Sjhb	error = intsmb_stop(sc);
630165951Sjhb	INTSMB_UNLOCK(sc);
631162289Sjhb	return (error);
63243166Snsouch}
63343166Snsouch
63443166Snsouchstatic int
63543166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
63643166Snsouch{
637162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
638162289Sjhb	int error;
639162289Sjhb
640165951Sjhb	INTSMB_LOCK(sc);
641165951Sjhb	error = intsmb_free(sc);
642165951Sjhb	if (error) {
643165951Sjhb		INTSMB_UNLOCK(sc);
644165951Sjhb		return (error);
645162289Sjhb	}
646165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
647165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
648165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
649165951Sjhb	error = intsmb_stop(sc);
650165951Sjhb	if (error == 0)
651165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
652165951Sjhb	INTSMB_UNLOCK(sc);
653162289Sjhb	return (error);
65443166Snsouch}
655165951Sjhb
65643166Snsouchstatic int
65743166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
65843166Snsouch{
659162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
660162289Sjhb	int error;
661162289Sjhb
662165951Sjhb	INTSMB_LOCK(sc);
663165951Sjhb	error = intsmb_free(sc);
664165951Sjhb	if (error) {
665165951Sjhb		INTSMB_UNLOCK(sc);
666165951Sjhb		return (error);
667162289Sjhb	}
668165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
669165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
670165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
671165951Sjhb	error = intsmb_stop(sc);
672165951Sjhb	if (error == 0) {
673165951Sjhb		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
674165951Sjhb		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
675165951Sjhb	}
676165951Sjhb	INTSMB_UNLOCK(sc);
677162289Sjhb	return (error);
67843166Snsouch}
679162289Sjhb
68043166Snsouch/*
68143166Snsouch * Data sheet claims that it implements all function, but also claims
68243166Snsouch * that it implements 7 function and not mention PCALL. So I don't know
68343166Snsouch * whether it will work.
68443166Snsouch */
68543166Snsouchstatic int
68643166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
68743166Snsouch{
68843166Snsouch#ifdef PROCCALL_TEST
689162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
690162289Sjhb	int error;
691162289Sjhb
692165951Sjhb	INTSMB_LOCK(sc);
693165951Sjhb	error = intsmb_free(sc);
694165951Sjhb	if (error) {
695165951Sjhb		INTSMB_UNLOCK(sc);
696165951Sjhb		return (error);
697162289Sjhb	}
698165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
699165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
700165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
701165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
702165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
703165951Sjhb	error = intsmb_stop(sc);
704165951Sjhb	if (error == 0) {
705165951Sjhb		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
706165951Sjhb		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
707162289Sjhb	}
708165951Sjhb	INTSMB_UNLOCK(sc);
709162289Sjhb	return (error);
71043166Snsouch#else
711165951Sjhb	return (SMB_ENOTSUPP);
71243166Snsouch#endif
71343166Snsouch}
714162289Sjhb
71543166Snsouchstatic int
71643166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
71743166Snsouch{
718162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
719162289Sjhb	int error, i;
720162289Sjhb
721162289Sjhb	if (count > SMBBLOCKTRANS_MAX || count == 0)
722165951Sjhb		return (SMB_EINVAL);
723162289Sjhb
724165951Sjhb	INTSMB_LOCK(sc);
725165951Sjhb	error = intsmb_free(sc);
726165951Sjhb	if (error) {
727165951Sjhb		INTSMB_UNLOCK(sc);
728165951Sjhb		return (error);
729162289Sjhb	}
730165951Sjhb
731165951Sjhb	/* Reset internal array index. */
732165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
733165951Sjhb
734165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
735165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
736165951Sjhb	for (i = 0; i < count; i++)
737165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
738165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
739165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
740165951Sjhb	error = intsmb_stop(sc);
741165951Sjhb	INTSMB_UNLOCK(sc);
742162289Sjhb	return (error);
74343166Snsouch}
74443166Snsouch
74543166Snsouchstatic int
746162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
74743166Snsouch{
748162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
749162289Sjhb	int error, i;
750162234Sjhb	u_char data, nread;
751162289Sjhb
752162289Sjhb	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
753165951Sjhb		return (SMB_EINVAL);
754162289Sjhb
755165951Sjhb	INTSMB_LOCK(sc);
756165951Sjhb	error = intsmb_free(sc);
757165951Sjhb	if (error) {
758165951Sjhb		INTSMB_UNLOCK(sc);
759165951Sjhb		return (error);
760165951Sjhb	}
761165951Sjhb
762165951Sjhb	/* Reset internal array index. */
763165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
764165951Sjhb
765165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
766165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
767165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
768165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
769165951Sjhb	error = intsmb_stop(sc);
770165951Sjhb	if (error == 0) {
771165951Sjhb		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
772165951Sjhb		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
773165951Sjhb			for (i = 0; i < nread; i++) {
774165951Sjhb				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
775165951Sjhb				if (i < *count)
776165951Sjhb					buf[i] = data;
77743166Snsouch			}
778165951Sjhb			*count = nread;
779165951Sjhb		} else
780234215Savg			error = SMB_EBUSERR;
78143166Snsouch	}
782165951Sjhb	INTSMB_UNLOCK(sc);
783162289Sjhb	return (error);
78443166Snsouch}
78543166Snsouch
786165951Sjhbstatic devclass_t intsmb_devclass;
78743166Snsouch
788165951Sjhbstatic device_method_t intsmb_methods[] = {
789165951Sjhb	/* Device interface */
790165951Sjhb	DEVMETHOD(device_probe,		intsmb_probe),
791165951Sjhb	DEVMETHOD(device_attach,	intsmb_attach),
792165951Sjhb	DEVMETHOD(device_detach,	intsmb_detach),
79346651Speter
794165951Sjhb	/* SMBus interface */
795165951Sjhb	DEVMETHOD(smbus_callback,	intsmb_callback),
796165951Sjhb	DEVMETHOD(smbus_quick,		intsmb_quick),
797165951Sjhb	DEVMETHOD(smbus_sendb,		intsmb_sendb),
798165951Sjhb	DEVMETHOD(smbus_recvb,		intsmb_recvb),
799165951Sjhb	DEVMETHOD(smbus_writeb,		intsmb_writeb),
800165951Sjhb	DEVMETHOD(smbus_writew,		intsmb_writew),
801165951Sjhb	DEVMETHOD(smbus_readb,		intsmb_readb),
802165951Sjhb	DEVMETHOD(smbus_readw,		intsmb_readw),
803165951Sjhb	DEVMETHOD(smbus_pcall,		intsmb_pcall),
804165951Sjhb	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
805165951Sjhb	DEVMETHOD(smbus_bread,		intsmb_bread),
806162289Sjhb
807229093Shselasky	DEVMETHOD_END
808165951Sjhb};
80943166Snsouch
810165951Sjhbstatic driver_t intsmb_driver = {
811165951Sjhb	"intsmb",
812165951Sjhb	intsmb_methods,
813165951Sjhb	sizeof(struct intsmb_softc),
814165951Sjhb};
815162289Sjhb
816165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
817162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
818165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
819165951SjhbMODULE_VERSION(intsmb, 1);
820