intpm.c revision 197325
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: head/sys/pci/intpm.c 197325 2009-09-19 08:56:28Z avg $");
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;
101197128Savg	case 0x43851002:
102197128Savg		device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller");
103197128Savg		/* XXX Maybe force polling right here? */
104197128Savg		break;
105165951Sjhb	default:
106165951Sjhb		return (ENXIO);
107165951Sjhb	}
10843166Snsouch
109165951Sjhb	return (BUS_PROBE_DEFAULT);
110165951Sjhb}
111162289Sjhb
112165951Sjhbstatic int
113165951Sjhbintsmb_attach(device_t dev)
114165951Sjhb{
115165951Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
116165951Sjhb	int error, rid, value;
117197128Savg	int intr;
118165951Sjhb	char *str;
119162289Sjhb
120178972Sjhb	sc->dev = dev;
121178972Sjhb
122165951Sjhb	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
12343166Snsouch
124197128Savg	sc->cfg_irq9 = 0;
125197128Savg#ifndef NO_CHANGE_PCICONF
126197128Savg	switch (pci_get_devid(dev)) {
127197128Savg	case 0x71138086:	/* Intel 82371AB */
128197128Savg	case 0x719b8086:	/* Intel 82443MX */
129197128Savg		/* Changing configuration is allowed. */
130197128Savg		sc->cfg_irq9 = 1;
131197128Savg		break;
132197128Savg	}
133197128Savg#endif
134197128Savg
135165951Sjhb	rid = PCI_BASE_ADDR_SMB;
136165951Sjhb	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
137165951Sjhb	    RF_ACTIVE);
138165951Sjhb	if (sc->io_res == NULL) {
139165951Sjhb		device_printf(dev, "Could not allocate I/O space\n");
140165951Sjhb		error = ENXIO;
141165951Sjhb		goto fail;
142165951Sjhb	}
14343166Snsouch
144197128Savg	if (sc->cfg_irq9) {
145197128Savg		pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
146197128Savg		pci_write_config(dev, PCI_HST_CFG_SMB,
147197128Savg		    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
148197128Savg	}
149165951Sjhb	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
150197128Savg	sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0;
151197128Savg	intr = value & PCI_INTR_SMB_MASK;
152197128Savg	switch (intr) {
153165951Sjhb	case PCI_INTR_SMB_SMI:
154165951Sjhb		str = "SMI";
155165951Sjhb		break;
156165951Sjhb	case PCI_INTR_SMB_IRQ9:
157165951Sjhb		str = "IRQ 9";
158165951Sjhb		break;
159197128Savg	case PCI_INTR_SMB_IRQ_PCI:
160197128Savg		str = "PCI IRQ";
161197128Savg		break;
162165951Sjhb	default:
163165951Sjhb		str = "BOGUS";
164165951Sjhb	}
165197128Savg
166165951Sjhb	device_printf(dev, "intr %s %s ", str,
167197128Savg	    sc->poll == 0 ? "enabled" : "disabled");
168168870Sjhb	printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
16943166Snsouch
170197325Savg	if (!sc->poll && intr == PCI_INTR_SMB_SMI) {
171197325Savg		device_printf(dev,
172197325Savg		    "using polling mode when configured interrupt is SMI\n");
173197325Savg		sc->poll = 1;
174197325Savg	}
175197325Savg
176197128Savg	if (sc->poll)
177197128Savg	    goto no_intr;
178197128Savg
179197128Savg	if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) {
180165951Sjhb		device_printf(dev, "Unsupported interrupt mode\n");
181165951Sjhb		error = ENXIO;
182165951Sjhb		goto fail;
183165951Sjhb	}
18446651Speter
185165951Sjhb	/* Force IRQ 9. */
186165951Sjhb	rid = 0;
187197128Savg	if (sc->cfg_irq9)
188197128Savg		bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
189197128Savg
190165951Sjhb	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
191165951Sjhb	    RF_SHAREABLE | RF_ACTIVE);
192165951Sjhb	if (sc->irq_res == NULL) {
193165951Sjhb		device_printf(dev, "Could not allocate irq\n");
194165951Sjhb		error = ENXIO;
195165951Sjhb		goto fail;
196165951Sjhb	}
19743166Snsouch
198179622Sjhb	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
199179622Sjhb	    NULL, intsmb_rawintr, sc, &sc->irq_hand);
200165951Sjhb	if (error) {
201165951Sjhb		device_printf(dev, "Failed to map intr\n");
202165951Sjhb		goto fail;
203165951Sjhb	}
204162289Sjhb
205197128Savgno_intr:
206165951Sjhb	sc->isbusy = 0;
207165951Sjhb	sc->smbus = device_add_child(dev, "smbus", -1);
208165951Sjhb	if (sc->smbus == NULL) {
209165951Sjhb		error = ENXIO;
210165951Sjhb		goto fail;
211165951Sjhb	}
212165951Sjhb	error = device_probe_and_attach(sc->smbus);
213165951Sjhb	if (error)
214165951Sjhb		goto fail;
215162289Sjhb
216165951Sjhb#ifdef ENABLE_ALART
217165951Sjhb	/* Enable Arart */
218165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
219165951Sjhb#endif
220165951Sjhb	return (0);
22143166Snsouch
222165951Sjhbfail:
223165951Sjhb	intsmb_detach(dev);
224165951Sjhb	return (error);
225165951Sjhb}
226165951Sjhb
227162289Sjhbstatic int
228165951Sjhbintsmb_detach(device_t dev)
22943166Snsouch{
230162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
231165951Sjhb	int error;
232162289Sjhb
233165951Sjhb	error = bus_generic_detach(dev);
234165951Sjhb	if (error)
235165951Sjhb		return (error);
236162289Sjhb
237165951Sjhb	if (sc->smbus)
238165951Sjhb		device_delete_child(dev, sc->smbus);
239165951Sjhb	if (sc->irq_hand)
240165951Sjhb		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
241165951Sjhb	if (sc->irq_res)
242165951Sjhb		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
243165951Sjhb	if (sc->io_res)
244165951Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
245165951Sjhb		    sc->io_res);
246165951Sjhb	mtx_destroy(&sc->lock);
247165951Sjhb	return (0);
24843166Snsouch}
249165951Sjhb
250165951Sjhbstatic void
251165951Sjhbintsmb_rawintr(void *arg)
25243166Snsouch{
253165951Sjhb	struct intsmb_softc *sc = arg;
254162289Sjhb
255165951Sjhb	INTSMB_LOCK(sc);
256165951Sjhb	intsmb_intr(sc);
257165951Sjhb	intsmb_slvintr(sc);
258165951Sjhb	INTSMB_UNLOCK(sc);
25943166Snsouch}
26043166Snsouch
261162289Sjhbstatic int
262162234Sjhbintsmb_callback(device_t dev, int index, void *data)
26343166Snsouch{
26443166Snsouch	int error = 0;
265162289Sjhb
26643166Snsouch	switch (index) {
26743166Snsouch	case SMB_REQUEST_BUS:
26843166Snsouch		break;
26943166Snsouch	case SMB_RELEASE_BUS:
27043166Snsouch		break;
27143166Snsouch	default:
27243166Snsouch		error = EINVAL;
27343166Snsouch	}
274162289Sjhb
27543166Snsouch	return (error);
27643166Snsouch}
277162289Sjhb
278162289Sjhb/* Counterpart of smbtx_smb_free(). */
279162289Sjhbstatic int
280165951Sjhbintsmb_free(struct intsmb_softc *sc)
281162289Sjhb{
282162289Sjhb
283165951Sjhb	INTSMB_LOCK_ASSERT(sc);
284165951Sjhb	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
28543166Snsouch#ifdef ENABLE_ALART
286165951Sjhb	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
28743166Snsouch#endif
288162289Sjhb	    sc->isbusy)
289165951Sjhb		return (SMB_EBUSY);
290165951Sjhb
291162289Sjhb	sc->isbusy = 1;
292162289Sjhb	/* Disable Interrupt in slave part. */
29343166Snsouch#ifndef ENABLE_ALART
294165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
29543166Snsouch#endif
296162289Sjhb	/* Reset INTR Flag to prepare INTR. */
297165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
298165951Sjhb	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
299165951Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
300162289Sjhb	return (0);
30143166Snsouch}
30243166Snsouch
30343166Snsouchstatic int
304165951Sjhbintsmb_intr(struct intsmb_softc *sc)
30543166Snsouch{
306165951Sjhb	int status, tmp;
307162289Sjhb
308165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
309162289Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSY)
310162289Sjhb		return (1);
311162289Sjhb
312162289Sjhb	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
313162289Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
314162289Sjhb
315165951Sjhb		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
316165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
317162289Sjhb		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
318162289Sjhb		if (sc->isbusy) {
319162289Sjhb			sc->isbusy = 0;
320162289Sjhb			wakeup(sc);
32149064Snsouch		}
322162289Sjhb		return (0);
32343166Snsouch	}
324162289Sjhb	return (1); /* Not Completed */
32543166Snsouch}
326162289Sjhb
32743166Snsouchstatic int
328165951Sjhbintsmb_slvintr(struct intsmb_softc *sc)
32943166Snsouch{
330165951Sjhb	int status;
331162289Sjhb
332165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
333162289Sjhb	if (status & PIIX4_SMBSLVSTS_BUSY)
334165951Sjhb		return (1);
335165951Sjhb	if (status & PIIX4_SMBSLVSTS_ALART)
336165951Sjhb		intsmb_alrintr(sc);
337165951Sjhb	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
338162289Sjhb		| PIIX4_SMBSLVSTS_SDW1)) {
33943166Snsouch	}
340162289Sjhb
341162289Sjhb	/* Reset Status Register */
342165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
343162289Sjhb	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
344162289Sjhb	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
345165951Sjhb	return (0);
34643166Snsouch}
34743166Snsouch
348162289Sjhbstatic void
349165951Sjhbintsmb_alrintr(struct intsmb_softc *sc)
35043166Snsouch{
35143166Snsouch	int slvcnt;
35243288Sdillon#ifdef ENABLE_ALART
35343288Sdillon	int error;
354165951Sjhb	uint8_t addr;
35543288Sdillon#endif
35643288Sdillon
357162289Sjhb	/* Stop generating INTR from ALART. */
358165951Sjhb	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
35943166Snsouch#ifdef ENABLE_ALART
360165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
361162289Sjhb	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
36243166Snsouch#endif
36343166Snsouch	DELAY(5);
364162289Sjhb
365162289Sjhb	/* Ask bus who asserted it and then ask it what's the matter. */
36643166Snsouch#ifdef ENABLE_ALART
367165951Sjhb	error = intsmb_free(sc);
368165951Sjhb	if (error)
369165951Sjhb		return;
370162289Sjhb
371165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
372165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
373165951Sjhb	error = intsmb_stop_poll(sc);
374165951Sjhb	if (error)
375165951Sjhb		device_printf(sc->dev, "ALART: ERROR\n");
376165951Sjhb	else {
377165951Sjhb		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
378165951Sjhb		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
379165951Sjhb	}
38043166Snsouch
381162289Sjhb	/* Re-enable INTR from ALART. */
382165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
383162289Sjhb	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
38443166Snsouch	DELAY(5);
38543166Snsouch#endif
386162289Sjhb}
38743166Snsouch
38843166Snsouchstatic void
389165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
39043166Snsouch{
39143166Snsouch	unsigned char tmp;
392162289Sjhb
393165951Sjhb	INTSMB_LOCK_ASSERT(sc);
394165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
395162289Sjhb	tmp &= 0xe0;
39643166Snsouch	tmp |= cmd;
397162289Sjhb	tmp |= PIIX4_SMBHSTCNT_START;
398162289Sjhb
399162289Sjhb	/* While not in autoconfiguration enable interrupts. */
400197128Savg	if (!sc->poll && !cold && !nointr)
401162289Sjhb		tmp |= PIIX4_SMBHSTCNT_INTREN;
402165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
40343166Snsouch}
40443166Snsouch
405165951Sjhbstatic int
406189882Savgintsmb_error(device_t dev, int status)
407165951Sjhb{
408165951Sjhb	int error = 0;
409165951Sjhb
410165951Sjhb	if (status & PIIX4_SMBHSTSTAT_ERR)
411165951Sjhb		error |= SMB_EBUSERR;
412165951Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSC)
413165951Sjhb		error |= SMB_ECOLLI;
414165951Sjhb	if (status & PIIX4_SMBHSTSTAT_FAIL)
415165951Sjhb		error |= SMB_ENOACK;
416189882Savg
417189882Savg	if (error != 0 && bootverbose)
418189882Savg		device_printf(dev, "error = %d, status = %#x\n", error, status);
419189882Savg
420165951Sjhb	return (error);
421165951Sjhb}
422165951Sjhb
423162289Sjhb/*
424162289Sjhb * Polling Code.
425162289Sjhb *
426162289Sjhb * Polling is not encouraged because it requires waiting for the
427162289Sjhb * device if it is busy.
428162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
429162289Sjhb * polling code then.
43043166Snsouch */
431162289Sjhbstatic int
432165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc)
433162289Sjhb{
434165951Sjhb	int error, i, status, tmp;
43543166Snsouch
436165951Sjhb	INTSMB_LOCK_ASSERT(sc);
437165951Sjhb
438165951Sjhb	/* First, wait for busy to be set. */
439162289Sjhb	for (i = 0; i < 0x7fff; i++)
440165951Sjhb		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
441162289Sjhb		    PIIX4_SMBHSTSTAT_BUSY)
442162289Sjhb			break;
443162289Sjhb
444165951Sjhb	/* Wait for busy to clear. */
445162289Sjhb	for (i = 0; i < 0x7fff; i++) {
446165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
447162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
448162289Sjhb			sc->isbusy = 0;
449189882Savg			error = intsmb_error(sc->dev, status);
450162289Sjhb			return (error);
45143166Snsouch		}
45243166Snsouch	}
453162289Sjhb
454165951Sjhb	/* Timed out waiting for busy to clear. */
455162289Sjhb	sc->isbusy = 0;
456165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
457165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
458165951Sjhb	return (SMB_ETIMEOUT);
45943166Snsouch}
460162289Sjhb
46143166Snsouch/*
462162289Sjhb * Wait for completion and return result.
46343166Snsouch */
464162289Sjhbstatic int
465165951Sjhbintsmb_stop(struct intsmb_softc *sc)
466162289Sjhb{
467165951Sjhb	int error, status;
468162289Sjhb
469165951Sjhb	INTSMB_LOCK_ASSERT(sc);
470165951Sjhb
471197128Savg	if (sc->poll || cold)
472162289Sjhb		/* So that it can use device during device probe on SMBus. */
473165951Sjhb		return (intsmb_stop_poll(sc));
474162289Sjhb
475172667Sjhb	error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8);
476165951Sjhb	if (error == 0) {
477165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
478162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
479189882Savg			error = intsmb_error(sc->dev, status);
480162289Sjhb			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
481165951Sjhb				device_printf(sc->dev, "unknown cause why?\n");
48243166Snsouch#ifdef ENABLE_ALART
483165951Sjhb			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
484162289Sjhb			    PIIX4_SMBSLVCNT_ALTEN);
48543166Snsouch#endif
486162289Sjhb			return (error);
48743166Snsouch		}
48843166Snsouch	}
489162289Sjhb
490162289Sjhb	/* Timeout Procedure. */
491162289Sjhb	sc->isbusy = 0;
492162289Sjhb
493162289Sjhb	/* Re-enable supressed interrupt from slave part. */
494165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
495165951Sjhb	if (error == EWOULDBLOCK)
496165951Sjhb		return (SMB_ETIMEOUT);
497165951Sjhb	else
498165951Sjhb		return (SMB_EABORT);
49943166Snsouch}
50043166Snsouch
50143166Snsouchstatic int
50243166Snsouchintsmb_quick(device_t dev, u_char slave, int how)
50343166Snsouch{
504162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
505165951Sjhb	int error;
506162289Sjhb	u_char data;
507162289Sjhb
508162289Sjhb	data = slave;
509162289Sjhb
510162289Sjhb	/* Quick command is part of Address, I think. */
511162289Sjhb	switch(how) {
512162289Sjhb	case SMB_QWRITE:
513162289Sjhb		data &= ~LSB;
51443166Snsouch		break;
515162289Sjhb	case SMB_QREAD:
516162289Sjhb		data |= LSB;
517162289Sjhb		break;
518162289Sjhb	default:
519165951Sjhb		return (EINVAL);
520162289Sjhb	}
521165951Sjhb
522165951Sjhb	INTSMB_LOCK(sc);
523165951Sjhb	error = intsmb_free(sc);
524165951Sjhb	if (error) {
525165951Sjhb		INTSMB_UNLOCK(sc);
526165951Sjhb		return (error);
527162289Sjhb	}
528165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
529165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
530165951Sjhb	error = intsmb_stop(sc);
531165951Sjhb	INTSMB_UNLOCK(sc);
532162289Sjhb	return (error);
53343166Snsouch}
53443166Snsouch
53543166Snsouchstatic int
53643166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte)
53743166Snsouch{
538162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
539162289Sjhb	int error;
540162289Sjhb
541165951Sjhb	INTSMB_LOCK(sc);
542165951Sjhb	error = intsmb_free(sc);
543165951Sjhb	if (error) {
544165951Sjhb		INTSMB_UNLOCK(sc);
545165951Sjhb		return (error);
546162289Sjhb	}
547165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
548165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
549165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
550165951Sjhb	error = intsmb_stop(sc);
551165951Sjhb	INTSMB_UNLOCK(sc);
552162289Sjhb	return (error);
55343166Snsouch}
554162289Sjhb
55543166Snsouchstatic int
55643166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte)
55743166Snsouch{
558162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
559162289Sjhb	int error;
560162289Sjhb
561165951Sjhb	INTSMB_LOCK(sc);
562165951Sjhb	error = intsmb_free(sc);
563165951Sjhb	if (error) {
564165951Sjhb		INTSMB_UNLOCK(sc);
565165951Sjhb		return (error);
566165951Sjhb	}
567165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
568165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
569165951Sjhb	error = intsmb_stop(sc);
570165951Sjhb	if (error == 0) {
57143166Snsouch#ifdef RECV_IS_IN_CMD
572165951Sjhb		/*
573165951Sjhb		 * Linux SMBus stuff also troubles
574165951Sjhb		 * Because Intel's datasheet does not make clear.
575165951Sjhb		 */
576165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
57743166Snsouch#else
578165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
57943166Snsouch#endif
580162289Sjhb	}
581165951Sjhb	INTSMB_UNLOCK(sc);
582162289Sjhb	return (error);
58343166Snsouch}
584162289Sjhb
58543166Snsouchstatic int
58643166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
58743166Snsouch{
588162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
589162289Sjhb	int error;
590162289Sjhb
591165951Sjhb	INTSMB_LOCK(sc);
592165951Sjhb	error = intsmb_free(sc);
593165951Sjhb	if (error) {
594165951Sjhb		INTSMB_UNLOCK(sc);
595165951Sjhb		return (error);
596162289Sjhb	}
597165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
598165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
599165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
600165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
601165951Sjhb	error = intsmb_stop(sc);
602165951Sjhb	INTSMB_UNLOCK(sc);
603162289Sjhb	return (error);
60443166Snsouch}
605162289Sjhb
60643166Snsouchstatic int
60743166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word)
60843166Snsouch{
609162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
610162289Sjhb	int error;
611162289Sjhb
612165951Sjhb	INTSMB_LOCK(sc);
613165951Sjhb	error = intsmb_free(sc);
614165951Sjhb	if (error) {
615165951Sjhb		INTSMB_UNLOCK(sc);
616165951Sjhb		return (error);
617162289Sjhb	}
618165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
619165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
620165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
621165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
622165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
623165951Sjhb	error = intsmb_stop(sc);
624165951Sjhb	INTSMB_UNLOCK(sc);
625162289Sjhb	return (error);
62643166Snsouch}
62743166Snsouch
62843166Snsouchstatic int
62943166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
63043166Snsouch{
631162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
632162289Sjhb	int error;
633162289Sjhb
634165951Sjhb	INTSMB_LOCK(sc);
635165951Sjhb	error = intsmb_free(sc);
636165951Sjhb	if (error) {
637165951Sjhb		INTSMB_UNLOCK(sc);
638165951Sjhb		return (error);
639162289Sjhb	}
640165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
641165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
642165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
643165951Sjhb	error = intsmb_stop(sc);
644165951Sjhb	if (error == 0)
645165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
646165951Sjhb	INTSMB_UNLOCK(sc);
647162289Sjhb	return (error);
64843166Snsouch}
649165951Sjhb
65043166Snsouchstatic int
65143166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
65243166Snsouch{
653162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
654162289Sjhb	int error;
655162289Sjhb
656165951Sjhb	INTSMB_LOCK(sc);
657165951Sjhb	error = intsmb_free(sc);
658165951Sjhb	if (error) {
659165951Sjhb		INTSMB_UNLOCK(sc);
660165951Sjhb		return (error);
661162289Sjhb	}
662165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
663165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
664165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
665165951Sjhb	error = intsmb_stop(sc);
666165951Sjhb	if (error == 0) {
667165951Sjhb		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
668165951Sjhb		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
669165951Sjhb	}
670165951Sjhb	INTSMB_UNLOCK(sc);
671162289Sjhb	return (error);
67243166Snsouch}
673162289Sjhb
67443166Snsouch/*
67543166Snsouch * Data sheet claims that it implements all function, but also claims
67643166Snsouch * that it implements 7 function and not mention PCALL. So I don't know
67743166Snsouch * whether it will work.
67843166Snsouch */
67943166Snsouchstatic int
68043166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
68143166Snsouch{
68243166Snsouch#ifdef PROCCALL_TEST
683162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
684162289Sjhb	int error;
685162289Sjhb
686165951Sjhb	INTSMB_LOCK(sc);
687165951Sjhb	error = intsmb_free(sc);
688165951Sjhb	if (error) {
689165951Sjhb		INTSMB_UNLOCK(sc);
690165951Sjhb		return (error);
691162289Sjhb	}
692165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
693165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
694165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
695165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
696165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
697165951Sjhb	error = intsmb_stop(sc);
698165951Sjhb	if (error == 0) {
699165951Sjhb		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
700165951Sjhb		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
701162289Sjhb	}
702165951Sjhb	INTSMB_UNLOCK(sc);
703162289Sjhb	return (error);
70443166Snsouch#else
705165951Sjhb	return (SMB_ENOTSUPP);
70643166Snsouch#endif
70743166Snsouch}
708162289Sjhb
70943166Snsouchstatic int
71043166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
71143166Snsouch{
712162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
713162289Sjhb	int error, i;
714162289Sjhb
715162289Sjhb	if (count > SMBBLOCKTRANS_MAX || count == 0)
716165951Sjhb		return (SMB_EINVAL);
717162289Sjhb
718165951Sjhb	INTSMB_LOCK(sc);
719165951Sjhb	error = intsmb_free(sc);
720165951Sjhb	if (error) {
721165951Sjhb		INTSMB_UNLOCK(sc);
722165951Sjhb		return (error);
723162289Sjhb	}
724165951Sjhb
725165951Sjhb	/* Reset internal array index. */
726165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
727165951Sjhb
728165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
729165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
730165951Sjhb	for (i = 0; i < count; i++)
731165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
732165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
733165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
734165951Sjhb	error = intsmb_stop(sc);
735165951Sjhb	INTSMB_UNLOCK(sc);
736162289Sjhb	return (error);
73743166Snsouch}
73843166Snsouch
73943166Snsouchstatic int
740162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
74143166Snsouch{
742162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
743162289Sjhb	int error, i;
744162234Sjhb	u_char data, nread;
745162289Sjhb
746162289Sjhb	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
747165951Sjhb		return (SMB_EINVAL);
748162289Sjhb
749165951Sjhb	INTSMB_LOCK(sc);
750165951Sjhb	error = intsmb_free(sc);
751165951Sjhb	if (error) {
752165951Sjhb		INTSMB_UNLOCK(sc);
753165951Sjhb		return (error);
754165951Sjhb	}
755165951Sjhb
756165951Sjhb	/* Reset internal array index. */
757165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
758165951Sjhb
759165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
760165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
761165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
762165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
763165951Sjhb	error = intsmb_stop(sc);
764165951Sjhb	if (error == 0) {
765165951Sjhb		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
766165951Sjhb		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
767165951Sjhb			for (i = 0; i < nread; i++) {
768165951Sjhb				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
769165951Sjhb				if (i < *count)
770165951Sjhb					buf[i] = data;
77143166Snsouch			}
772165951Sjhb			*count = nread;
773165951Sjhb		} else
774165951Sjhb			error = EIO;
77543166Snsouch	}
776165951Sjhb	INTSMB_UNLOCK(sc);
777162289Sjhb	return (error);
77843166Snsouch}
77943166Snsouch
780165951Sjhbstatic devclass_t intsmb_devclass;
78143166Snsouch
782165951Sjhbstatic device_method_t intsmb_methods[] = {
783165951Sjhb	/* Device interface */
784165951Sjhb	DEVMETHOD(device_probe,		intsmb_probe),
785165951Sjhb	DEVMETHOD(device_attach,	intsmb_attach),
786165951Sjhb	DEVMETHOD(device_detach,	intsmb_detach),
78746651Speter
788165951Sjhb	/* Bus interface */
789165951Sjhb	DEVMETHOD(bus_print_child,	bus_generic_print_child),
79043166Snsouch
791165951Sjhb	/* SMBus interface */
792165951Sjhb	DEVMETHOD(smbus_callback,	intsmb_callback),
793165951Sjhb	DEVMETHOD(smbus_quick,		intsmb_quick),
794165951Sjhb	DEVMETHOD(smbus_sendb,		intsmb_sendb),
795165951Sjhb	DEVMETHOD(smbus_recvb,		intsmb_recvb),
796165951Sjhb	DEVMETHOD(smbus_writeb,		intsmb_writeb),
797165951Sjhb	DEVMETHOD(smbus_writew,		intsmb_writew),
798165951Sjhb	DEVMETHOD(smbus_readb,		intsmb_readb),
799165951Sjhb	DEVMETHOD(smbus_readw,		intsmb_readw),
800165951Sjhb	DEVMETHOD(smbus_pcall,		intsmb_pcall),
801165951Sjhb	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
802165951Sjhb	DEVMETHOD(smbus_bread,		intsmb_bread),
803162289Sjhb
804165951Sjhb	{ 0, 0 }
805165951Sjhb};
80643166Snsouch
807165951Sjhbstatic driver_t intsmb_driver = {
808165951Sjhb	"intsmb",
809165951Sjhb	intsmb_methods,
810165951Sjhb	sizeof(struct intsmb_softc),
811165951Sjhb};
812162289Sjhb
813165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
814162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
815165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
816165951SjhbMODULE_VERSION(intsmb, 1);
817