intpm.c revision 172667
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 172667 2007-10-15 16:18:20Z jhb $");
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
113165951Sjhb	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
11443166Snsouch
115165951Sjhb	rid = PCI_BASE_ADDR_SMB;
116165951Sjhb	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
117165951Sjhb	    RF_ACTIVE);
118165951Sjhb	if (sc->io_res == NULL) {
119165951Sjhb		device_printf(dev, "Could not allocate I/O space\n");
120165951Sjhb		error = ENXIO;
121165951Sjhb		goto fail;
122165951Sjhb	}
12343166Snsouch
124165951Sjhb#ifndef NO_CHANGE_PCICONF
125165951Sjhb	pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
126165951Sjhb	pci_write_config(dev, PCI_HST_CFG_SMB,
127165951Sjhb	    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
128165951Sjhb#endif
129165951Sjhb	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
130165951Sjhb	switch (value & 0xe) {
131165951Sjhb	case PCI_INTR_SMB_SMI:
132165951Sjhb		str = "SMI";
133165951Sjhb		break;
134165951Sjhb	case PCI_INTR_SMB_IRQ9:
135165951Sjhb		str = "IRQ 9";
136165951Sjhb		break;
137165951Sjhb	default:
138165951Sjhb		str = "BOGUS";
139165951Sjhb	}
140165951Sjhb	device_printf(dev, "intr %s %s ", str,
141165951Sjhb	    (value & 1) ? "enabled" : "disabled");
142168870Sjhb	printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
14343166Snsouch
144165951Sjhb	if ((value & 0xe) != PCI_INTR_SMB_IRQ9) {
145165951Sjhb		device_printf(dev, "Unsupported interrupt mode\n");
146165951Sjhb		error = ENXIO;
147165951Sjhb		goto fail;
148165951Sjhb	}
14946651Speter
150165951Sjhb	/* Force IRQ 9. */
151165951Sjhb	rid = 0;
152165951Sjhb	bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
153165951Sjhb	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
154165951Sjhb	    RF_SHAREABLE | RF_ACTIVE);
155165951Sjhb	if (sc->irq_res == NULL) {
156165951Sjhb		device_printf(dev, "Could not allocate irq\n");
157165951Sjhb		error = ENXIO;
158165951Sjhb		goto fail;
159165951Sjhb	}
16043166Snsouch
161166901Spiso	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, NULL,
162166901Spiso	    intsmb_rawintr, sc, &sc->irq_hand);
163165951Sjhb	if (error) {
164165951Sjhb		device_printf(dev, "Failed to map intr\n");
165165951Sjhb		goto fail;
166165951Sjhb	}
167162289Sjhb
168165951Sjhb	value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4);
169165951Sjhb	device_printf(dev, "PM %s %x\n", (value & 1) ? "I/O mapped" : "Memory",
170165951Sjhb	    value & 0xfffe);
171162289Sjhb
172165951Sjhb	sc->isbusy = 0;
173165951Sjhb	sc->smbus = device_add_child(dev, "smbus", -1);
174165951Sjhb	if (sc->smbus == NULL) {
175165951Sjhb		error = ENXIO;
176165951Sjhb		goto fail;
177165951Sjhb	}
178165951Sjhb	error = device_probe_and_attach(sc->smbus);
179165951Sjhb	if (error)
180165951Sjhb		goto fail;
181162289Sjhb
182165951Sjhb#ifdef ENABLE_ALART
183165951Sjhb	/* Enable Arart */
184165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
185165951Sjhb#endif
186165951Sjhb	return (0);
18743166Snsouch
188165951Sjhbfail:
189165951Sjhb	intsmb_detach(dev);
190165951Sjhb	return (error);
191165951Sjhb}
192165951Sjhb
193162289Sjhbstatic int
194165951Sjhbintsmb_detach(device_t dev)
19543166Snsouch{
196162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
197165951Sjhb	int error;
198162289Sjhb
199165951Sjhb	error = bus_generic_detach(dev);
200165951Sjhb	if (error)
201165951Sjhb		return (error);
202162289Sjhb
203165951Sjhb	if (sc->smbus)
204165951Sjhb		device_delete_child(dev, sc->smbus);
205165951Sjhb	if (sc->irq_hand)
206165951Sjhb		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
207165951Sjhb	if (sc->irq_res)
208165951Sjhb		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
209165951Sjhb	if (sc->io_res)
210165951Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
211165951Sjhb		    sc->io_res);
212165951Sjhb	mtx_destroy(&sc->lock);
213165951Sjhb	return (0);
21443166Snsouch}
215165951Sjhb
216165951Sjhbstatic void
217165951Sjhbintsmb_rawintr(void *arg)
21843166Snsouch{
219165951Sjhb	struct intsmb_softc *sc = arg;
220162289Sjhb
221165951Sjhb	INTSMB_LOCK(sc);
222165951Sjhb	intsmb_intr(sc);
223165951Sjhb	intsmb_slvintr(sc);
224165951Sjhb	INTSMB_UNLOCK(sc);
22543166Snsouch}
22643166Snsouch
227162289Sjhbstatic int
228162234Sjhbintsmb_callback(device_t dev, int index, void *data)
22943166Snsouch{
23043166Snsouch	int error = 0;
231162289Sjhb
23243166Snsouch	switch (index) {
23343166Snsouch	case SMB_REQUEST_BUS:
23443166Snsouch		break;
23543166Snsouch	case SMB_RELEASE_BUS:
23643166Snsouch		break;
23743166Snsouch	default:
23843166Snsouch		error = EINVAL;
23943166Snsouch	}
240162289Sjhb
24143166Snsouch	return (error);
24243166Snsouch}
243162289Sjhb
244162289Sjhb/* Counterpart of smbtx_smb_free(). */
245162289Sjhbstatic int
246165951Sjhbintsmb_free(struct intsmb_softc *sc)
247162289Sjhb{
248162289Sjhb
249165951Sjhb	INTSMB_LOCK_ASSERT(sc);
250165951Sjhb	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
25143166Snsouch#ifdef ENABLE_ALART
252165951Sjhb	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
25343166Snsouch#endif
254162289Sjhb	    sc->isbusy)
255165951Sjhb		return (SMB_EBUSY);
256165951Sjhb
257162289Sjhb	sc->isbusy = 1;
258162289Sjhb	/* Disable Interrupt in slave part. */
25943166Snsouch#ifndef ENABLE_ALART
260165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
26143166Snsouch#endif
262162289Sjhb	/* Reset INTR Flag to prepare INTR. */
263165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
264165951Sjhb	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
265165951Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
266162289Sjhb	return (0);
26743166Snsouch}
26843166Snsouch
26943166Snsouchstatic int
270165951Sjhbintsmb_intr(struct intsmb_softc *sc)
27143166Snsouch{
272165951Sjhb	int status, tmp;
273162289Sjhb
274165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
275162289Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSY)
276162289Sjhb		return (1);
277162289Sjhb
278162289Sjhb	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
279162289Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
280162289Sjhb
281165951Sjhb		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
282165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
283162289Sjhb		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
284162289Sjhb		if (sc->isbusy) {
285162289Sjhb			sc->isbusy = 0;
286162289Sjhb			wakeup(sc);
28749064Snsouch		}
288162289Sjhb		return (0);
28943166Snsouch	}
290162289Sjhb	return (1); /* Not Completed */
29143166Snsouch}
292162289Sjhb
29343166Snsouchstatic int
294165951Sjhbintsmb_slvintr(struct intsmb_softc *sc)
29543166Snsouch{
296165951Sjhb	int status;
297162289Sjhb
298165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
299162289Sjhb	if (status & PIIX4_SMBSLVSTS_BUSY)
300165951Sjhb		return (1);
301165951Sjhb	if (status & PIIX4_SMBSLVSTS_ALART)
302165951Sjhb		intsmb_alrintr(sc);
303165951Sjhb	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
304162289Sjhb		| PIIX4_SMBSLVSTS_SDW1)) {
30543166Snsouch	}
306162289Sjhb
307162289Sjhb	/* Reset Status Register */
308165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
309162289Sjhb	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
310162289Sjhb	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
311165951Sjhb	return (0);
31243166Snsouch}
31343166Snsouch
314162289Sjhbstatic void
315165951Sjhbintsmb_alrintr(struct intsmb_softc *sc)
31643166Snsouch{
31743166Snsouch	int slvcnt;
31843288Sdillon#ifdef ENABLE_ALART
31943288Sdillon	int error;
320165951Sjhb	uint8_t addr;
32143288Sdillon#endif
32243288Sdillon
323162289Sjhb	/* Stop generating INTR from ALART. */
324165951Sjhb	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
32543166Snsouch#ifdef ENABLE_ALART
326165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
327162289Sjhb	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
32843166Snsouch#endif
32943166Snsouch	DELAY(5);
330162289Sjhb
331162289Sjhb	/* Ask bus who asserted it and then ask it what's the matter. */
33243166Snsouch#ifdef ENABLE_ALART
333165951Sjhb	error = intsmb_free(sc);
334165951Sjhb	if (error)
335165951Sjhb		return;
336162289Sjhb
337165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
338165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
339165951Sjhb	error = intsmb_stop_poll(sc);
340165951Sjhb	if (error)
341165951Sjhb		device_printf(sc->dev, "ALART: ERROR\n");
342165951Sjhb	else {
343165951Sjhb		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
344165951Sjhb		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
345165951Sjhb	}
34643166Snsouch
347162289Sjhb	/* Re-enable INTR from ALART. */
348165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
349162289Sjhb	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
35043166Snsouch	DELAY(5);
35143166Snsouch#endif
352162289Sjhb}
35343166Snsouch
35443166Snsouchstatic void
355165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
35643166Snsouch{
35743166Snsouch	unsigned char tmp;
358162289Sjhb
359165951Sjhb	INTSMB_LOCK_ASSERT(sc);
360165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
361162289Sjhb	tmp &= 0xe0;
36243166Snsouch	tmp |= cmd;
363162289Sjhb	tmp |= PIIX4_SMBHSTCNT_START;
364162289Sjhb
365162289Sjhb	/* While not in autoconfiguration enable interrupts. */
366162289Sjhb	if (!cold || !nointr)
367162289Sjhb		tmp |= PIIX4_SMBHSTCNT_INTREN;
368165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
36943166Snsouch}
37043166Snsouch
371165951Sjhbstatic int
372165951Sjhbintsmb_error(int status)
373165951Sjhb{
374165951Sjhb	int error = 0;
375165951Sjhb
376165951Sjhb	if (status & PIIX4_SMBHSTSTAT_ERR)
377165951Sjhb		error |= SMB_EBUSERR;
378165951Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSC)
379165951Sjhb		error |= SMB_ECOLLI;
380165951Sjhb	if (status & PIIX4_SMBHSTSTAT_FAIL)
381165951Sjhb		error |= SMB_ENOACK;
382165951Sjhb	return (error);
383165951Sjhb}
384165951Sjhb
385162289Sjhb/*
386162289Sjhb * Polling Code.
387162289Sjhb *
388162289Sjhb * Polling is not encouraged because it requires waiting for the
389162289Sjhb * device if it is busy.
390162289Sjhb * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
391162289Sjhb * polling code then.
39243166Snsouch */
393162289Sjhbstatic int
394165951Sjhbintsmb_stop_poll(struct intsmb_softc *sc)
395162289Sjhb{
396165951Sjhb	int error, i, status, tmp;
39743166Snsouch
398165951Sjhb	INTSMB_LOCK_ASSERT(sc);
399165951Sjhb
400165951Sjhb	/* First, wait for busy to be set. */
401162289Sjhb	for (i = 0; i < 0x7fff; i++)
402165951Sjhb		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
403162289Sjhb		    PIIX4_SMBHSTSTAT_BUSY)
404162289Sjhb			break;
405162289Sjhb
406165951Sjhb	/* Wait for busy to clear. */
407162289Sjhb	for (i = 0; i < 0x7fff; i++) {
408165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
409162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
410162289Sjhb			sc->isbusy = 0;
411165951Sjhb			error = intsmb_error(status);
412162289Sjhb			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
413165951Sjhb				device_printf(sc->dev, "unknown cause why?");
414162289Sjhb			return (error);
41543166Snsouch		}
41643166Snsouch	}
417162289Sjhb
418165951Sjhb	/* Timed out waiting for busy to clear. */
419162289Sjhb	sc->isbusy = 0;
420165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
421165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
422165951Sjhb	return (SMB_ETIMEOUT);
42343166Snsouch}
424162289Sjhb
42543166Snsouch/*
426162289Sjhb * Wait for completion and return result.
42743166Snsouch */
428162289Sjhbstatic int
429165951Sjhbintsmb_stop(struct intsmb_softc *sc)
430162289Sjhb{
431165951Sjhb	int error, status;
432162289Sjhb
433165951Sjhb	INTSMB_LOCK_ASSERT(sc);
434165951Sjhb
435165951Sjhb	if (cold)
436162289Sjhb		/* So that it can use device during device probe on SMBus. */
437165951Sjhb		return (intsmb_stop_poll(sc));
438162289Sjhb
439172667Sjhb	error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8);
440165951Sjhb	if (error == 0) {
441165951Sjhb		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
442162289Sjhb		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
443165951Sjhb			error = intsmb_error(status);
444162289Sjhb			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
445165951Sjhb				device_printf(sc->dev, "unknown cause why?\n");
44643166Snsouch#ifdef ENABLE_ALART
447165951Sjhb			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
448162289Sjhb			    PIIX4_SMBSLVCNT_ALTEN);
44943166Snsouch#endif
450162289Sjhb			return (error);
45143166Snsouch		}
45243166Snsouch	}
453162289Sjhb
454162289Sjhb	/* Timeout Procedure. */
455162289Sjhb	sc->isbusy = 0;
456162289Sjhb
457162289Sjhb	/* Re-enable supressed interrupt from slave part. */
458165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
459165951Sjhb	if (error == EWOULDBLOCK)
460165951Sjhb		return (SMB_ETIMEOUT);
461165951Sjhb	else
462165951Sjhb		return (SMB_EABORT);
46343166Snsouch}
46443166Snsouch
46543166Snsouchstatic int
46643166Snsouchintsmb_quick(device_t dev, u_char slave, int how)
46743166Snsouch{
468162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
469165951Sjhb	int error;
470162289Sjhb	u_char data;
471162289Sjhb
472162289Sjhb	data = slave;
473162289Sjhb
474162289Sjhb	/* Quick command is part of Address, I think. */
475162289Sjhb	switch(how) {
476162289Sjhb	case SMB_QWRITE:
477162289Sjhb		data &= ~LSB;
47843166Snsouch		break;
479162289Sjhb	case SMB_QREAD:
480162289Sjhb		data |= LSB;
481162289Sjhb		break;
482162289Sjhb	default:
483165951Sjhb		return (EINVAL);
484162289Sjhb	}
485165951Sjhb
486165951Sjhb	INTSMB_LOCK(sc);
487165951Sjhb	error = intsmb_free(sc);
488165951Sjhb	if (error) {
489165951Sjhb		INTSMB_UNLOCK(sc);
490165951Sjhb		return (error);
491162289Sjhb	}
492165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
493165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
494165951Sjhb	error = intsmb_stop(sc);
495165951Sjhb	INTSMB_UNLOCK(sc);
496162289Sjhb	return (error);
49743166Snsouch}
49843166Snsouch
49943166Snsouchstatic int
50043166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte)
50143166Snsouch{
502162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
503162289Sjhb	int error;
504162289Sjhb
505165951Sjhb	INTSMB_LOCK(sc);
506165951Sjhb	error = intsmb_free(sc);
507165951Sjhb	if (error) {
508165951Sjhb		INTSMB_UNLOCK(sc);
509165951Sjhb		return (error);
510162289Sjhb	}
511165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
512165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
513165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
514165951Sjhb	error = intsmb_stop(sc);
515165951Sjhb	INTSMB_UNLOCK(sc);
516162289Sjhb	return (error);
51743166Snsouch}
518162289Sjhb
51943166Snsouchstatic int
52043166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte)
52143166Snsouch{
522162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
523162289Sjhb	int error;
524162289Sjhb
525165951Sjhb	INTSMB_LOCK(sc);
526165951Sjhb	error = intsmb_free(sc);
527165951Sjhb	if (error) {
528165951Sjhb		INTSMB_UNLOCK(sc);
529165951Sjhb		return (error);
530165951Sjhb	}
531165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
532165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
533165951Sjhb	error = intsmb_stop(sc);
534165951Sjhb	if (error == 0) {
53543166Snsouch#ifdef RECV_IS_IN_CMD
536165951Sjhb		/*
537165951Sjhb		 * Linux SMBus stuff also troubles
538165951Sjhb		 * Because Intel's datasheet does not make clear.
539165951Sjhb		 */
540165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
54143166Snsouch#else
542165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
54343166Snsouch#endif
544162289Sjhb	}
545165951Sjhb	INTSMB_UNLOCK(sc);
546162289Sjhb	return (error);
54743166Snsouch}
548162289Sjhb
54943166Snsouchstatic int
55043166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
55143166Snsouch{
552162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
553162289Sjhb	int error;
554162289Sjhb
555165951Sjhb	INTSMB_LOCK(sc);
556165951Sjhb	error = intsmb_free(sc);
557165951Sjhb	if (error) {
558165951Sjhb		INTSMB_UNLOCK(sc);
559165951Sjhb		return (error);
560162289Sjhb	}
561165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
562165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
563165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
564165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
565165951Sjhb	error = intsmb_stop(sc);
566165951Sjhb	INTSMB_UNLOCK(sc);
567162289Sjhb	return (error);
56843166Snsouch}
569162289Sjhb
57043166Snsouchstatic int
57143166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word)
57243166Snsouch{
573162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
574162289Sjhb	int error;
575162289Sjhb
576165951Sjhb	INTSMB_LOCK(sc);
577165951Sjhb	error = intsmb_free(sc);
578165951Sjhb	if (error) {
579165951Sjhb		INTSMB_UNLOCK(sc);
580165951Sjhb		return (error);
581162289Sjhb	}
582165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
583165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
584165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
585165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
586165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
587165951Sjhb	error = intsmb_stop(sc);
588165951Sjhb	INTSMB_UNLOCK(sc);
589162289Sjhb	return (error);
59043166Snsouch}
59143166Snsouch
59243166Snsouchstatic int
59343166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
59443166Snsouch{
595162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
596162289Sjhb	int error;
597162289Sjhb
598165951Sjhb	INTSMB_LOCK(sc);
599165951Sjhb	error = intsmb_free(sc);
600165951Sjhb	if (error) {
601165951Sjhb		INTSMB_UNLOCK(sc);
602165951Sjhb		return (error);
603162289Sjhb	}
604165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
605165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
606165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
607165951Sjhb	error = intsmb_stop(sc);
608165951Sjhb	if (error == 0)
609165951Sjhb		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
610165951Sjhb	INTSMB_UNLOCK(sc);
611162289Sjhb	return (error);
61243166Snsouch}
613165951Sjhb
61443166Snsouchstatic int
61543166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
61643166Snsouch{
617162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
618162289Sjhb	int error;
619162289Sjhb
620165951Sjhb	INTSMB_LOCK(sc);
621165951Sjhb	error = intsmb_free(sc);
622165951Sjhb	if (error) {
623165951Sjhb		INTSMB_UNLOCK(sc);
624165951Sjhb		return (error);
625162289Sjhb	}
626165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
627165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
628165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
629165951Sjhb	error = intsmb_stop(sc);
630165951Sjhb	if (error == 0) {
631165951Sjhb		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
632165951Sjhb		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
633165951Sjhb	}
634165951Sjhb	INTSMB_UNLOCK(sc);
635162289Sjhb	return (error);
63643166Snsouch}
637162289Sjhb
63843166Snsouch/*
63943166Snsouch * Data sheet claims that it implements all function, but also claims
64043166Snsouch * that it implements 7 function and not mention PCALL. So I don't know
64143166Snsouch * whether it will work.
64243166Snsouch */
64343166Snsouchstatic int
64443166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
64543166Snsouch{
64643166Snsouch#ifdef PROCCALL_TEST
647162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
648162289Sjhb	int error;
649162289Sjhb
650165951Sjhb	INTSMB_LOCK(sc);
651165951Sjhb	error = intsmb_free(sc);
652165951Sjhb	if (error) {
653165951Sjhb		INTSMB_UNLOCK(sc);
654165951Sjhb		return (error);
655162289Sjhb	}
656165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
657165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
658165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
659165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
660165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
661165951Sjhb	error = intsmb_stop(sc);
662165951Sjhb	if (error == 0) {
663165951Sjhb		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
664165951Sjhb		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
665162289Sjhb	}
666165951Sjhb	INTSMB_UNLOCK(sc);
667162289Sjhb	return (error);
66843166Snsouch#else
669165951Sjhb	return (SMB_ENOTSUPP);
67043166Snsouch#endif
67143166Snsouch}
672162289Sjhb
67343166Snsouchstatic int
67443166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
67543166Snsouch{
676162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
677162289Sjhb	int error, i;
678162289Sjhb
679162289Sjhb	if (count > SMBBLOCKTRANS_MAX || count == 0)
680165951Sjhb		return (SMB_EINVAL);
681162289Sjhb
682165951Sjhb	INTSMB_LOCK(sc);
683165951Sjhb	error = intsmb_free(sc);
684165951Sjhb	if (error) {
685165951Sjhb		INTSMB_UNLOCK(sc);
686165951Sjhb		return (error);
687162289Sjhb	}
688165951Sjhb
689165951Sjhb	/* Reset internal array index. */
690165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
691165951Sjhb
692165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
693165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
694165951Sjhb	for (i = 0; i < count; i++)
695165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
696165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
697165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
698165951Sjhb	error = intsmb_stop(sc);
699165951Sjhb	INTSMB_UNLOCK(sc);
700162289Sjhb	return (error);
70143166Snsouch}
70243166Snsouch
70343166Snsouchstatic int
704162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
70543166Snsouch{
706162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
707162289Sjhb	int error, i;
708162234Sjhb	u_char data, nread;
709162289Sjhb
710162289Sjhb	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
711165951Sjhb		return (SMB_EINVAL);
712162289Sjhb
713165951Sjhb	INTSMB_LOCK(sc);
714165951Sjhb	error = intsmb_free(sc);
715165951Sjhb	if (error) {
716165951Sjhb		INTSMB_UNLOCK(sc);
717165951Sjhb		return (error);
718165951Sjhb	}
719165951Sjhb
720165951Sjhb	/* Reset internal array index. */
721165951Sjhb	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
722165951Sjhb
723165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
724165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
725165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
726165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
727165951Sjhb	error = intsmb_stop(sc);
728165951Sjhb	if (error == 0) {
729165951Sjhb		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
730165951Sjhb		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
731165951Sjhb			for (i = 0; i < nread; i++) {
732165951Sjhb				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
733165951Sjhb				if (i < *count)
734165951Sjhb					buf[i] = data;
73543166Snsouch			}
736165951Sjhb			*count = nread;
737165951Sjhb		} else
738165951Sjhb			error = EIO;
73943166Snsouch	}
740165951Sjhb	INTSMB_UNLOCK(sc);
741162289Sjhb	return (error);
74243166Snsouch}
74343166Snsouch
744165951Sjhbstatic devclass_t intsmb_devclass;
74543166Snsouch
746165951Sjhbstatic device_method_t intsmb_methods[] = {
747165951Sjhb	/* Device interface */
748165951Sjhb	DEVMETHOD(device_probe,		intsmb_probe),
749165951Sjhb	DEVMETHOD(device_attach,	intsmb_attach),
750165951Sjhb	DEVMETHOD(device_detach,	intsmb_detach),
75146651Speter
752165951Sjhb	/* Bus interface */
753165951Sjhb	DEVMETHOD(bus_print_child,	bus_generic_print_child),
75443166Snsouch
755165951Sjhb	/* SMBus interface */
756165951Sjhb	DEVMETHOD(smbus_callback,	intsmb_callback),
757165951Sjhb	DEVMETHOD(smbus_quick,		intsmb_quick),
758165951Sjhb	DEVMETHOD(smbus_sendb,		intsmb_sendb),
759165951Sjhb	DEVMETHOD(smbus_recvb,		intsmb_recvb),
760165951Sjhb	DEVMETHOD(smbus_writeb,		intsmb_writeb),
761165951Sjhb	DEVMETHOD(smbus_writew,		intsmb_writew),
762165951Sjhb	DEVMETHOD(smbus_readb,		intsmb_readb),
763165951Sjhb	DEVMETHOD(smbus_readw,		intsmb_readw),
764165951Sjhb	DEVMETHOD(smbus_pcall,		intsmb_pcall),
765165951Sjhb	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
766165951Sjhb	DEVMETHOD(smbus_bread,		intsmb_bread),
767162289Sjhb
768165951Sjhb	{ 0, 0 }
769165951Sjhb};
77043166Snsouch
771165951Sjhbstatic driver_t intsmb_driver = {
772165951Sjhb	"intsmb",
773165951Sjhb	intsmb_methods,
774165951Sjhb	sizeof(struct intsmb_softc),
775165951Sjhb};
776162289Sjhb
777165951SjhbDRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
778162289SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
779165951SjhbMODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
780165951SjhbMODULE_VERSION(intsmb, 1);
781