intpm.c revision 189882
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 189882 2009-03-16 16:15:14Z 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;
56165951Sjhb	struct mtx		lock;
5743166Snsouch};
58162289Sjhb
59165951Sjhb#define	INTSMB_LOCK(sc)		mtx_lock(&(sc)->lock)
60165951Sjhb#define	INTSMB_UNLOCK(sc)	mtx_unlock(&(sc)->lock)
61165951Sjhb#define	INTSMB_LOCK_ASSERT(sc)	mtx_assert(&(sc)->lock, MA_OWNED)
62165951Sjhb
6343166Snsouchstatic int intsmb_probe(device_t);
6443166Snsouchstatic int intsmb_attach(device_t);
65165951Sjhbstatic int intsmb_detach(device_t);
66165951Sjhbstatic int intsmb_intr(struct intsmb_softc *sc);
67165951Sjhbstatic int intsmb_slvintr(struct intsmb_softc *sc);
68165951Sjhbstatic void intsmb_alrintr(struct intsmb_softc *sc);
69162234Sjhbstatic int intsmb_callback(device_t dev, int index, void *data);
7043166Snsouchstatic int intsmb_quick(device_t dev, u_char slave, int how);
7143166Snsouchstatic int intsmb_sendb(device_t dev, u_char slave, char byte);
7243166Snsouchstatic int intsmb_recvb(device_t dev, u_char slave, char *byte);
7343166Snsouchstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
7443166Snsouchstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
7543166Snsouchstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
7643166Snsouchstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
7743166Snsouchstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
7843166Snsouchstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
79162234Sjhbstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
80165951Sjhbstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr);
81165951Sjhbstatic int intsmb_stop(struct intsmb_softc *sc);
82165951Sjhbstatic int intsmb_stop_poll(struct intsmb_softc *sc);
83165951Sjhbstatic int intsmb_free(struct intsmb_softc *sc);
84165951Sjhbstatic void intsmb_rawintr(void *arg);
85162289Sjhb
86165951Sjhbstatic int
87165951Sjhbintsmb_probe(device_t dev)
88165951Sjhb{
8943166Snsouch
90165951Sjhb	switch (pci_get_devid(dev)) {
91165951Sjhb	case 0x71138086:	/* Intel 82371AB */
92165951Sjhb	case 0x719b8086:	/* Intel 82443MX */
93165951Sjhb#if 0
94165951Sjhb	/* Not a good idea yet, this stops isab0 functioning */
95165951Sjhb	case 0x02001166:	/* ServerWorks OSB4 */
96165951Sjhb#endif
97165951Sjhb		device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
98165951Sjhb		break;
99165951Sjhb	default:
100165951Sjhb		return (ENXIO);
101165951Sjhb	}
10243166Snsouch
103165951Sjhb	return (BUS_PROBE_DEFAULT);
104165951Sjhb}
105162289Sjhb
106165951Sjhbstatic int
107165951Sjhbintsmb_attach(device_t dev)
108165951Sjhb{
109165951Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
110165951Sjhb	int error, rid, value;
111165951Sjhb	char *str;
112162289Sjhb
113178972Sjhb	sc->dev = dev;
114178972Sjhb
115165951Sjhb	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
11643166Snsouch
117165951Sjhb	rid = PCI_BASE_ADDR_SMB;
118165951Sjhb	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
119165951Sjhb	    RF_ACTIVE);
120165951Sjhb	if (sc->io_res == NULL) {
121165951Sjhb		device_printf(dev, "Could not allocate I/O space\n");
122165951Sjhb		error = ENXIO;
123165951Sjhb		goto fail;
124165951Sjhb	}
12543166Snsouch
126165951Sjhb#ifndef NO_CHANGE_PCICONF
127165951Sjhb	pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
128165951Sjhb	pci_write_config(dev, PCI_HST_CFG_SMB,
129165951Sjhb	    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
130165951Sjhb#endif
131165951Sjhb	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
132165951Sjhb	switch (value & 0xe) {
133165951Sjhb	case PCI_INTR_SMB_SMI:
134165951Sjhb		str = "SMI";
135165951Sjhb		break;
136165951Sjhb	case PCI_INTR_SMB_IRQ9:
137165951Sjhb		str = "IRQ 9";
138165951Sjhb		break;
139165951Sjhb	default:
140165951Sjhb		str = "BOGUS";
141165951Sjhb	}
142165951Sjhb	device_printf(dev, "intr %s %s ", str,
143165951Sjhb	    (value & 1) ? "enabled" : "disabled");
144168870Sjhb	printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
14543166Snsouch
146165951Sjhb	if ((value & 0xe) != PCI_INTR_SMB_IRQ9) {
147165951Sjhb		device_printf(dev, "Unsupported interrupt mode\n");
148165951Sjhb		error = ENXIO;
149165951Sjhb		goto fail;
150165951Sjhb	}
15146651Speter
152165951Sjhb	/* Force IRQ 9. */
153165951Sjhb	rid = 0;
154165951Sjhb	bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
155165951Sjhb	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
156165951Sjhb	    RF_SHAREABLE | RF_ACTIVE);
157165951Sjhb	if (sc->irq_res == NULL) {
158165951Sjhb		device_printf(dev, "Could not allocate irq\n");
159165951Sjhb		error = ENXIO;
160165951Sjhb		goto fail;
161165951Sjhb	}
16243166Snsouch
163179622Sjhb	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
164179622Sjhb	    NULL, intsmb_rawintr, sc, &sc->irq_hand);
165165951Sjhb	if (error) {
166165951Sjhb		device_printf(dev, "Failed to map intr\n");
167165951Sjhb		goto fail;
168165951Sjhb	}
169162289Sjhb
170165951Sjhb	sc->isbusy = 0;
171165951Sjhb	sc->smbus = device_add_child(dev, "smbus", -1);
172165951Sjhb	if (sc->smbus == NULL) {
173165951Sjhb		error = ENXIO;
174165951Sjhb		goto fail;
175165951Sjhb	}
176165951Sjhb	error = device_probe_and_attach(sc->smbus);
177165951Sjhb	if (error)
178165951Sjhb		goto fail;
179162289Sjhb
180165951Sjhb#ifdef ENABLE_ALART
181165951Sjhb	/* Enable Arart */
182165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
183165951Sjhb#endif
184165951Sjhb	return (0);
18543166Snsouch
186165951Sjhbfail:
187165951Sjhb	intsmb_detach(dev);
188165951Sjhb	return (error);
189165951Sjhb}
190165951Sjhb
191162289Sjhbstatic int
192165951Sjhbintsmb_detach(device_t dev)
19343166Snsouch{
194162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
195165951Sjhb	int error;
196162289Sjhb
197165951Sjhb	error = bus_generic_detach(dev);
198165951Sjhb	if (error)
199165951Sjhb		return (error);
200162289Sjhb
201165951Sjhb	if (sc->smbus)
202165951Sjhb		device_delete_child(dev, sc->smbus);
203165951Sjhb	if (sc->irq_hand)
204165951Sjhb		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
205165951Sjhb	if (sc->irq_res)
206165951Sjhb		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
207165951Sjhb	if (sc->io_res)
208165951Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
209165951Sjhb		    sc->io_res);
210165951Sjhb	mtx_destroy(&sc->lock);
211165951Sjhb	return (0);
21243166Snsouch}
213165951Sjhb
214165951Sjhbstatic void
215165951Sjhbintsmb_rawintr(void *arg)
21643166Snsouch{
217165951Sjhb	struct intsmb_softc *sc = arg;
218162289Sjhb
219165951Sjhb	INTSMB_LOCK(sc);
220165951Sjhb	intsmb_intr(sc);
221165951Sjhb	intsmb_slvintr(sc);
222165951Sjhb	INTSMB_UNLOCK(sc);
22343166Snsouch}
22443166Snsouch
225162289Sjhbstatic int
226162234Sjhbintsmb_callback(device_t dev, int index, void *data)
22743166Snsouch{
22843166Snsouch	int error = 0;
229162289Sjhb
23043166Snsouch	switch (index) {
23143166Snsouch	case SMB_REQUEST_BUS:
23243166Snsouch		break;
23343166Snsouch	case SMB_RELEASE_BUS:
23443166Snsouch		break;
23543166Snsouch	default:
23643166Snsouch		error = EINVAL;
23743166Snsouch	}
238162289Sjhb
23943166Snsouch	return (error);
24043166Snsouch}
241162289Sjhb
242162289Sjhb/* Counterpart of smbtx_smb_free(). */
243162289Sjhbstatic int
244165951Sjhbintsmb_free(struct intsmb_softc *sc)
245162289Sjhb{
246162289Sjhb
247165951Sjhb	INTSMB_LOCK_ASSERT(sc);
248165951Sjhb	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
24943166Snsouch#ifdef ENABLE_ALART
250165951Sjhb	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
25143166Snsouch#endif
252162289Sjhb	    sc->isbusy)
253165951Sjhb		return (SMB_EBUSY);
254165951Sjhb
255162289Sjhb	sc->isbusy = 1;
256162289Sjhb	/* Disable Interrupt in slave part. */
25743166Snsouch#ifndef ENABLE_ALART
258165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
25943166Snsouch#endif
260162289Sjhb	/* Reset INTR Flag to prepare INTR. */
261165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
262165951Sjhb	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
263165951Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
264162289Sjhb	return (0);
26543166Snsouch}
26643166Snsouch
26743166Snsouchstatic int
268165951Sjhbintsmb_intr(struct intsmb_softc *sc)
26943166Snsouch{
270165951Sjhb	int status, tmp;
271162289Sjhb
272165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
273162289Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSY)
274162289Sjhb		return (1);
275162289Sjhb
276162289Sjhb	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
277162289Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
278162289Sjhb
279165951Sjhb		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
280165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
281162289Sjhb		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
282162289Sjhb		if (sc->isbusy) {
283162289Sjhb			sc->isbusy = 0;
284162289Sjhb			wakeup(sc);
28549064Snsouch		}
286162289Sjhb		return (0);
28743166Snsouch	}
288162289Sjhb	return (1); /* Not Completed */
28943166Snsouch}
290162289Sjhb
29143166Snsouchstatic int
292165951Sjhbintsmb_slvintr(struct intsmb_softc *sc)
29343166Snsouch{
294165951Sjhb	int status;
295162289Sjhb
296165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
297162289Sjhb	if (status & PIIX4_SMBSLVSTS_BUSY)
298165951Sjhb		return (1);
299165951Sjhb	if (status & PIIX4_SMBSLVSTS_ALART)
300165951Sjhb		intsmb_alrintr(sc);
301165951Sjhb	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
302162289Sjhb		| PIIX4_SMBSLVSTS_SDW1)) {
30343166Snsouch	}
304162289Sjhb
305162289Sjhb	/* Reset Status Register */
306165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
307162289Sjhb	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
308162289Sjhb	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
309165951Sjhb	return (0);
31043166Snsouch}
31143166Snsouch
312162289Sjhbstatic void
313165951Sjhbintsmb_alrintr(struct intsmb_softc *sc)
31443166Snsouch{
31543166Snsouch	int slvcnt;
31643288Sdillon#ifdef ENABLE_ALART
31743288Sdillon	int error;
318165951Sjhb	uint8_t addr;
31943288Sdillon#endif
32043288Sdillon
321162289Sjhb	/* Stop generating INTR from ALART. */
322165951Sjhb	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
32343166Snsouch#ifdef ENABLE_ALART
324165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
325162289Sjhb	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
32643166Snsouch#endif
32743166Snsouch	DELAY(5);
328162289Sjhb
329162289Sjhb	/* Ask bus who asserted it and then ask it what's the matter. */
33043166Snsouch#ifdef ENABLE_ALART
331165951Sjhb	error = intsmb_free(sc);
332165951Sjhb	if (error)
333165951Sjhb		return;
334162289Sjhb
335165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
336165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
337165951Sjhb	error = intsmb_stop_poll(sc);
338165951Sjhb	if (error)
339165951Sjhb		device_printf(sc->dev, "ALART: ERROR\n");
340165951Sjhb	else {
341165951Sjhb		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
342165951Sjhb		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
343165951Sjhb	}
34443166Snsouch
345162289Sjhb	/* Re-enable INTR from ALART. */
346165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
347162289Sjhb	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
34843166Snsouch	DELAY(5);
34943166Snsouch#endif
350162289Sjhb}
35143166Snsouch
35243166Snsouchstatic void
353165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
35443166Snsouch{
35543166Snsouch	unsigned char tmp;
356162289Sjhb
357165951Sjhb	INTSMB_LOCK_ASSERT(sc);
358165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
359162289Sjhb	tmp &= 0xe0;
36043166Snsouch	tmp |= cmd;
361162289Sjhb	tmp |= PIIX4_SMBHSTCNT_START;
362162289Sjhb
363162289Sjhb	/* While not in autoconfiguration enable interrupts. */
364189882Savg	if (!cold && !nointr)
365162289Sjhb		tmp |= PIIX4_SMBHSTCNT_INTREN;
366165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
36743166Snsouch}
36843166Snsouch
369165951Sjhbstatic int
370189882Savgintsmb_error(device_t dev, int status)
371165951Sjhb{
372165951Sjhb	int error = 0;
373165951Sjhb
374165951Sjhb	if (status & PIIX4_SMBHSTSTAT_ERR)
375165951Sjhb		error |= SMB_EBUSERR;
376165951Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSC)
377165951Sjhb		error |= SMB_ECOLLI;
378165951Sjhb	if (status & PIIX4_SMBHSTSTAT_FAIL)
379165951Sjhb		error |= SMB_ENOACK;
380189882Savg
381189882Savg	if (error != 0 && bootverbose)
382189882Savg		device_printf(dev, "error = %d, status = %#x\n", error, status);
383189882Savg
384165951Sjhb	return (error);
385165951Sjhb}
386165951Sjhb
387162289Sjhb/*
388162289Sjhb * Polling Code.
389162289Sjhb *
390162289Sjhb * Polling is not encouraged because it requires waiting for the
391162289Sjhb * device if it is busy.
392162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
393162289Sjhb * polling code then.
39443166Snsouch */
395162289Sjhbstatic int
396165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc)
397162289Sjhb{
398165951Sjhb	int error, i, status, tmp;
39943166Snsouch
400165951Sjhb	INTSMB_LOCK_ASSERT(sc);
401165951Sjhb
402165951Sjhb	/* First, wait for busy to be set. */
403162289Sjhb	for (i = 0; i < 0x7fff; i++)
404165951Sjhb		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
405162289Sjhb		    PIIX4_SMBHSTSTAT_BUSY)
406162289Sjhb			break;
407162289Sjhb
408165951Sjhb	/* Wait for busy to clear. */
409162289Sjhb	for (i = 0; i < 0x7fff; i++) {
410165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
411162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
412162289Sjhb			sc->isbusy = 0;
413189882Savg			error = intsmb_error(sc->dev, status);
414162289Sjhb			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
415178972Sjhb				device_printf(sc->dev, "unknown cause why?\n");
416162289Sjhb			return (error);
41743166Snsouch		}
41843166Snsouch	}
419162289Sjhb
420165951Sjhb	/* Timed out waiting for busy to clear. */
421162289Sjhb	sc->isbusy = 0;
422165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
423165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
424165951Sjhb	return (SMB_ETIMEOUT);
42543166Snsouch}
426162289Sjhb
42743166Snsouch/*
428162289Sjhb * Wait for completion and return result.
42943166Snsouch */
430162289Sjhbstatic int
431165951Sjhbintsmb_stop(struct intsmb_softc *sc)
432162289Sjhb{
433165951Sjhb	int error, status;
434162289Sjhb
435165951Sjhb	INTSMB_LOCK_ASSERT(sc);
436165951Sjhb
437165951Sjhb	if (cold)
438162289Sjhb		/* So that it can use device during device probe on SMBus. */
439165951Sjhb		return (intsmb_stop_poll(sc));
440162289Sjhb
441172667Sjhb	error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8);
442165951Sjhb	if (error == 0) {
443165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
444162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
445189882Savg			error = intsmb_error(sc->dev, status);
446162289Sjhb			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
447165951Sjhb				device_printf(sc->dev, "unknown cause why?\n");
44843166Snsouch#ifdef ENABLE_ALART
449165951Sjhb			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
450162289Sjhb			    PIIX4_SMBSLVCNT_ALTEN);
45143166Snsouch#endif
452162289Sjhb			return (error);
45343166Snsouch		}
45443166Snsouch	}
455162289Sjhb
456162289Sjhb	/* Timeout Procedure. */
457162289Sjhb	sc->isbusy = 0;
458162289Sjhb
459162289Sjhb	/* Re-enable supressed interrupt from slave part. */
460165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
461165951Sjhb	if (error == EWOULDBLOCK)
462165951Sjhb		return (SMB_ETIMEOUT);
463165951Sjhb	else
464165951Sjhb		return (SMB_EABORT);
46543166Snsouch}
46643166Snsouch
46743166Snsouchstatic int
46843166Snsouchintsmb_quick(device_t dev, u_char slave, int how)
46943166Snsouch{
470162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
471165951Sjhb	int error;
472162289Sjhb	u_char data;
473162289Sjhb
474162289Sjhb	data = slave;
475162289Sjhb
476162289Sjhb	/* Quick command is part of Address, I think. */
477162289Sjhb	switch(how) {
478162289Sjhb	case SMB_QWRITE:
479162289Sjhb		data &= ~LSB;
48043166Snsouch		break;
481162289Sjhb	case SMB_QREAD:
482162289Sjhb		data |= LSB;
483162289Sjhb		break;
484162289Sjhb	default:
485165951Sjhb		return (EINVAL);
486162289Sjhb	}
487165951Sjhb
488165951Sjhb	INTSMB_LOCK(sc);
489165951Sjhb	error = intsmb_free(sc);
490165951Sjhb	if (error) {
491165951Sjhb		INTSMB_UNLOCK(sc);
492165951Sjhb		return (error);
493162289Sjhb	}
494165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
495165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
496165951Sjhb	error = intsmb_stop(sc);
497165951Sjhb	INTSMB_UNLOCK(sc);
498162289Sjhb	return (error);
49943166Snsouch}
50043166Snsouch
50143166Snsouchstatic int
50243166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte)
50343166Snsouch{
504162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
505162289Sjhb	int error;
506162289Sjhb
507165951Sjhb	INTSMB_LOCK(sc);
508165951Sjhb	error = intsmb_free(sc);
509165951Sjhb	if (error) {
510165951Sjhb		INTSMB_UNLOCK(sc);
511165951Sjhb		return (error);
512162289Sjhb	}
513165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
514165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
515165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
516165951Sjhb	error = intsmb_stop(sc);
517165951Sjhb	INTSMB_UNLOCK(sc);
518162289Sjhb	return (error);
51943166Snsouch}
520162289Sjhb
52143166Snsouchstatic int
52243166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte)
52343166Snsouch{
524162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
525162289Sjhb	int error;
526162289Sjhb
527165951Sjhb	INTSMB_LOCK(sc);
528165951Sjhb	error = intsmb_free(sc);
529165951Sjhb	if (error) {
530165951Sjhb		INTSMB_UNLOCK(sc);
531165951Sjhb		return (error);
532165951Sjhb	}
533165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
534165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
535165951Sjhb	error = intsmb_stop(sc);
536165951Sjhb	if (error == 0) {
53743166Snsouch#ifdef RECV_IS_IN_CMD
538165951Sjhb		/*
539165951Sjhb		 * Linux SMBus stuff also troubles
540165951Sjhb		 * Because Intel's datasheet does not make clear.
541165951Sjhb		 */
542165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
54343166Snsouch#else
544165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
54543166Snsouch#endif
546162289Sjhb	}
547165951Sjhb	INTSMB_UNLOCK(sc);
548162289Sjhb	return (error);
54943166Snsouch}
550162289Sjhb
55143166Snsouchstatic int
55243166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
55343166Snsouch{
554162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
555162289Sjhb	int error;
556162289Sjhb
557165951Sjhb	INTSMB_LOCK(sc);
558165951Sjhb	error = intsmb_free(sc);
559165951Sjhb	if (error) {
560165951Sjhb		INTSMB_UNLOCK(sc);
561165951Sjhb		return (error);
562162289Sjhb	}
563165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
564165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
565165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
566165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
567165951Sjhb	error = intsmb_stop(sc);
568165951Sjhb	INTSMB_UNLOCK(sc);
569162289Sjhb	return (error);
57043166Snsouch}
571162289Sjhb
57243166Snsouchstatic int
57343166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word)
57443166Snsouch{
575162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
576162289Sjhb	int error;
577162289Sjhb
578165951Sjhb	INTSMB_LOCK(sc);
579165951Sjhb	error = intsmb_free(sc);
580165951Sjhb	if (error) {
581165951Sjhb		INTSMB_UNLOCK(sc);
582165951Sjhb		return (error);
583162289Sjhb	}
584165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
585165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
586165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
587165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
588165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
589165951Sjhb	error = intsmb_stop(sc);
590165951Sjhb	INTSMB_UNLOCK(sc);
591162289Sjhb	return (error);
59243166Snsouch}
59343166Snsouch
59443166Snsouchstatic int
59543166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
59643166Snsouch{
597162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
598162289Sjhb	int error;
599162289Sjhb
600165951Sjhb	INTSMB_LOCK(sc);
601165951Sjhb	error = intsmb_free(sc);
602165951Sjhb	if (error) {
603165951Sjhb		INTSMB_UNLOCK(sc);
604165951Sjhb		return (error);
605162289Sjhb	}
606165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
607165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
608165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
609165951Sjhb	error = intsmb_stop(sc);
610165951Sjhb	if (error == 0)
611165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
612165951Sjhb	INTSMB_UNLOCK(sc);
613162289Sjhb	return (error);
61443166Snsouch}
615165951Sjhb
61643166Snsouchstatic int
61743166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
61843166Snsouch{
619162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
620162289Sjhb	int error;
621162289Sjhb
622165951Sjhb	INTSMB_LOCK(sc);
623165951Sjhb	error = intsmb_free(sc);
624165951Sjhb	if (error) {
625165951Sjhb		INTSMB_UNLOCK(sc);
626165951Sjhb		return (error);
627162289Sjhb	}
628165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
629165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
630165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
631165951Sjhb	error = intsmb_stop(sc);
632165951Sjhb	if (error == 0) {
633165951Sjhb		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
634165951Sjhb		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
635165951Sjhb	}
636165951Sjhb	INTSMB_UNLOCK(sc);
637162289Sjhb	return (error);
63843166Snsouch}
639162289Sjhb
64043166Snsouch/*
64143166Snsouch * Data sheet claims that it implements all function, but also claims
64243166Snsouch * that it implements 7 function and not mention PCALL. So I don't know
64343166Snsouch * whether it will work.
64443166Snsouch */
64543166Snsouchstatic int
64643166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
64743166Snsouch{
64843166Snsouch#ifdef PROCCALL_TEST
649162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
650162289Sjhb	int error;
651162289Sjhb
652165951Sjhb	INTSMB_LOCK(sc);
653165951Sjhb	error = intsmb_free(sc);
654165951Sjhb	if (error) {
655165951Sjhb		INTSMB_UNLOCK(sc);
656165951Sjhb		return (error);
657162289Sjhb	}
658165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
659165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
660165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
661165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
662165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
663165951Sjhb	error = intsmb_stop(sc);
664165951Sjhb	if (error == 0) {
665165951Sjhb		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
666165951Sjhb		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
667162289Sjhb	}
668165951Sjhb	INTSMB_UNLOCK(sc);
669162289Sjhb	return (error);
67043166Snsouch#else
671165951Sjhb	return (SMB_ENOTSUPP);
67243166Snsouch#endif
67343166Snsouch}
674162289Sjhb
67543166Snsouchstatic int
67643166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
67743166Snsouch{
678162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
679162289Sjhb	int error, i;
680162289Sjhb
681162289Sjhb	if (count > SMBBLOCKTRANS_MAX || count == 0)
682165951Sjhb		return (SMB_EINVAL);
683162289Sjhb
684165951Sjhb	INTSMB_LOCK(sc);
685165951Sjhb	error = intsmb_free(sc);
686165951Sjhb	if (error) {
687165951Sjhb		INTSMB_UNLOCK(sc);
688165951Sjhb		return (error);
689162289Sjhb	}
690165951Sjhb
691165951Sjhb	/* Reset internal array index. */
692165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
693165951Sjhb
694165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
695165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
696165951Sjhb	for (i = 0; i < count; i++)
697165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
698165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
699165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
700165951Sjhb	error = intsmb_stop(sc);
701165951Sjhb	INTSMB_UNLOCK(sc);
702162289Sjhb	return (error);
70343166Snsouch}
70443166Snsouch
70543166Snsouchstatic int
706162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
70743166Snsouch{
708162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
709162289Sjhb	int error, i;
710162234Sjhb	u_char data, nread;
711162289Sjhb
712162289Sjhb	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
713165951Sjhb		return (SMB_EINVAL);
714162289Sjhb
715165951Sjhb	INTSMB_LOCK(sc);
716165951Sjhb	error = intsmb_free(sc);
717165951Sjhb	if (error) {
718165951Sjhb		INTSMB_UNLOCK(sc);
719165951Sjhb		return (error);
720165951Sjhb	}
721165951Sjhb
722165951Sjhb	/* Reset internal array index. */
723165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
724165951Sjhb
725165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
726165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
727165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
728165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
729165951Sjhb	error = intsmb_stop(sc);
730165951Sjhb	if (error == 0) {
731165951Sjhb		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
732165951Sjhb		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
733165951Sjhb			for (i = 0; i < nread; i++) {
734165951Sjhb				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
735165951Sjhb				if (i < *count)
736165951Sjhb					buf[i] = data;
73743166Snsouch			}
738165951Sjhb			*count = nread;
739165951Sjhb		} else
740165951Sjhb			error = EIO;
74143166Snsouch	}
742165951Sjhb	INTSMB_UNLOCK(sc);
743162289Sjhb	return (error);
74443166Snsouch}
74543166Snsouch
746165951Sjhbstatic devclass_t intsmb_devclass;
74743166Snsouch
748165951Sjhbstatic device_method_t intsmb_methods[] = {
749165951Sjhb	/* Device interface */
750165951Sjhb	DEVMETHOD(device_probe,		intsmb_probe),
751165951Sjhb	DEVMETHOD(device_attach,	intsmb_attach),
752165951Sjhb	DEVMETHOD(device_detach,	intsmb_detach),
75346651Speter
754165951Sjhb	/* Bus interface */
755165951Sjhb	DEVMETHOD(bus_print_child,	bus_generic_print_child),
75643166Snsouch
757165951Sjhb	/* SMBus interface */
758165951Sjhb	DEVMETHOD(smbus_callback,	intsmb_callback),
759165951Sjhb	DEVMETHOD(smbus_quick,		intsmb_quick),
760165951Sjhb	DEVMETHOD(smbus_sendb,		intsmb_sendb),
761165951Sjhb	DEVMETHOD(smbus_recvb,		intsmb_recvb),
762165951Sjhb	DEVMETHOD(smbus_writeb,		intsmb_writeb),
763165951Sjhb	DEVMETHOD(smbus_writew,		intsmb_writew),
764165951Sjhb	DEVMETHOD(smbus_readb,		intsmb_readb),
765165951Sjhb	DEVMETHOD(smbus_readw,		intsmb_readw),
766165951Sjhb	DEVMETHOD(smbus_pcall,		intsmb_pcall),
767165951Sjhb	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
768165951Sjhb	DEVMETHOD(smbus_bread,		intsmb_bread),
769162289Sjhb
770165951Sjhb	{ 0, 0 }
771165951Sjhb};
77243166Snsouch
773165951Sjhbstatic driver_t intsmb_driver = {
774165951Sjhb	"intsmb",
775165951Sjhb	intsmb_methods,
776165951Sjhb	sizeof(struct intsmb_softc),
777165951Sjhb};
778162289Sjhb
779165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
780162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
781165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
782165951SjhbMODULE_VERSION(intsmb, 1);
783