intpm.c revision 179622
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 179622 2008-06-06 18:29:56Z 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
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	value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4);
171165951Sjhb	device_printf(dev, "PM %s %x\n", (value & 1) ? "I/O mapped" : "Memory",
172165951Sjhb	    value & 0xfffe);
173162289Sjhb
174165951Sjhb	sc->isbusy = 0;
175165951Sjhb	sc->smbus = device_add_child(dev, "smbus", -1);
176165951Sjhb	if (sc->smbus == NULL) {
177165951Sjhb		error = ENXIO;
178165951Sjhb		goto fail;
179165951Sjhb	}
180165951Sjhb	error = device_probe_and_attach(sc->smbus);
181165951Sjhb	if (error)
182165951Sjhb		goto fail;
183162289Sjhb
184165951Sjhb#ifdef ENABLE_ALART
185165951Sjhb	/* Enable Arart */
186165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
187165951Sjhb#endif
188165951Sjhb	return (0);
18943166Snsouch
190165951Sjhbfail:
191165951Sjhb	intsmb_detach(dev);
192165951Sjhb	return (error);
193165951Sjhb}
194165951Sjhb
195162289Sjhbstatic int
196165951Sjhbintsmb_detach(device_t dev)
19743166Snsouch{
198162289Sjhb	struct intsmb_softc *sc = device_get_softc(dev);
199165951Sjhb	int error;
200162289Sjhb
201165951Sjhb	error = bus_generic_detach(dev);
202165951Sjhb	if (error)
203165951Sjhb		return (error);
204162289Sjhb
205165951Sjhb	if (sc->smbus)
206165951Sjhb		device_delete_child(dev, sc->smbus);
207165951Sjhb	if (sc->irq_hand)
208165951Sjhb		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
209165951Sjhb	if (sc->irq_res)
210165951Sjhb		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
211165951Sjhb	if (sc->io_res)
212165951Sjhb		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
213165951Sjhb		    sc->io_res);
214165951Sjhb	mtx_destroy(&sc->lock);
215165951Sjhb	return (0);
21643166Snsouch}
217165951Sjhb
218165951Sjhbstatic void
219165951Sjhbintsmb_rawintr(void *arg)
22043166Snsouch{
221165951Sjhb	struct intsmb_softc *sc = arg;
222162289Sjhb
223165951Sjhb	INTSMB_LOCK(sc);
224165951Sjhb	intsmb_intr(sc);
225165951Sjhb	intsmb_slvintr(sc);
226165951Sjhb	INTSMB_UNLOCK(sc);
22743166Snsouch}
22843166Snsouch
229162289Sjhbstatic int
230162234Sjhbintsmb_callback(device_t dev, int index, void *data)
23143166Snsouch{
23243166Snsouch	int error = 0;
233162289Sjhb
23443166Snsouch	switch (index) {
23543166Snsouch	case SMB_REQUEST_BUS:
23643166Snsouch		break;
23743166Snsouch	case SMB_RELEASE_BUS:
23843166Snsouch		break;
23943166Snsouch	default:
24043166Snsouch		error = EINVAL;
24143166Snsouch	}
242162289Sjhb
24343166Snsouch	return (error);
24443166Snsouch}
245162289Sjhb
246162289Sjhb/* Counterpart of smbtx_smb_free(). */
247162289Sjhbstatic int
248165951Sjhbintsmb_free(struct intsmb_softc *sc)
249162289Sjhb{
250162289Sjhb
251165951Sjhb	INTSMB_LOCK_ASSERT(sc);
252165951Sjhb	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
25343166Snsouch#ifdef ENABLE_ALART
254165951Sjhb	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
25543166Snsouch#endif
256162289Sjhb	    sc->isbusy)
257165951Sjhb		return (SMB_EBUSY);
258165951Sjhb
259162289Sjhb	sc->isbusy = 1;
260162289Sjhb	/* Disable Interrupt in slave part. */
26143166Snsouch#ifndef ENABLE_ALART
262165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
26343166Snsouch#endif
264162289Sjhb	/* Reset INTR Flag to prepare INTR. */
265165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
266165951Sjhb	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
267165951Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
268162289Sjhb	return (0);
26943166Snsouch}
27043166Snsouch
27143166Snsouchstatic int
272165951Sjhbintsmb_intr(struct intsmb_softc *sc)
27343166Snsouch{
274165951Sjhb	int status, tmp;
275162289Sjhb
276165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
277162289Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSY)
278162289Sjhb		return (1);
279162289Sjhb
280162289Sjhb	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
281162289Sjhb	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
282162289Sjhb
283165951Sjhb		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
284165951Sjhb		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
285162289Sjhb		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
286162289Sjhb		if (sc->isbusy) {
287162289Sjhb			sc->isbusy = 0;
288162289Sjhb			wakeup(sc);
28949064Snsouch		}
290162289Sjhb		return (0);
29143166Snsouch	}
292162289Sjhb	return (1); /* Not Completed */
29343166Snsouch}
294162289Sjhb
29543166Snsouchstatic int
296165951Sjhbintsmb_slvintr(struct intsmb_softc *sc)
29743166Snsouch{
298165951Sjhb	int status;
299162289Sjhb
300165951Sjhb	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
301162289Sjhb	if (status & PIIX4_SMBSLVSTS_BUSY)
302165951Sjhb		return (1);
303165951Sjhb	if (status & PIIX4_SMBSLVSTS_ALART)
304165951Sjhb		intsmb_alrintr(sc);
305165951Sjhb	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
306162289Sjhb		| PIIX4_SMBSLVSTS_SDW1)) {
30743166Snsouch	}
308162289Sjhb
309162289Sjhb	/* Reset Status Register */
310165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
311162289Sjhb	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
312162289Sjhb	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
313165951Sjhb	return (0);
31443166Snsouch}
31543166Snsouch
316162289Sjhbstatic void
317165951Sjhbintsmb_alrintr(struct intsmb_softc *sc)
31843166Snsouch{
31943166Snsouch	int slvcnt;
32043288Sdillon#ifdef ENABLE_ALART
32143288Sdillon	int error;
322165951Sjhb	uint8_t addr;
32343288Sdillon#endif
32443288Sdillon
325162289Sjhb	/* Stop generating INTR from ALART. */
326165951Sjhb	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
32743166Snsouch#ifdef ENABLE_ALART
328165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
329162289Sjhb	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
33043166Snsouch#endif
33143166Snsouch	DELAY(5);
332162289Sjhb
333162289Sjhb	/* Ask bus who asserted it and then ask it what's the matter. */
33443166Snsouch#ifdef ENABLE_ALART
335165951Sjhb	error = intsmb_free(sc);
336165951Sjhb	if (error)
337165951Sjhb		return;
338162289Sjhb
339165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
340165951Sjhb	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
341165951Sjhb	error = intsmb_stop_poll(sc);
342165951Sjhb	if (error)
343165951Sjhb		device_printf(sc->dev, "ALART: ERROR\n");
344165951Sjhb	else {
345165951Sjhb		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
346165951Sjhb		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
347165951Sjhb	}
34843166Snsouch
349162289Sjhb	/* Re-enable INTR from ALART. */
350165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
351162289Sjhb	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
35243166Snsouch	DELAY(5);
35343166Snsouch#endif
354162289Sjhb}
35543166Snsouch
35643166Snsouchstatic void
357165951Sjhbintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
35843166Snsouch{
35943166Snsouch	unsigned char tmp;
360162289Sjhb
361165951Sjhb	INTSMB_LOCK_ASSERT(sc);
362165951Sjhb	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
363162289Sjhb	tmp &= 0xe0;
36443166Snsouch	tmp |= cmd;
365162289Sjhb	tmp |= PIIX4_SMBHSTCNT_START;
366162289Sjhb
367162289Sjhb	/* While not in autoconfiguration enable interrupts. */
368162289Sjhb	if (!cold || !nointr)
369162289Sjhb		tmp |= PIIX4_SMBHSTCNT_INTREN;
370165951Sjhb	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
37143166Snsouch}
37243166Snsouch
373165951Sjhbstatic int
374165951Sjhbintsmb_error(int status)
375165951Sjhb{
376165951Sjhb	int error = 0;
377165951Sjhb
378165951Sjhb	if (status & PIIX4_SMBHSTSTAT_ERR)
379165951Sjhb		error |= SMB_EBUSERR;
380165951Sjhb	if (status & PIIX4_SMBHSTSTAT_BUSC)
381165951Sjhb		error |= SMB_ECOLLI;
382165951Sjhb	if (status & PIIX4_SMBHSTSTAT_FAIL)
383165951Sjhb		error |= SMB_ENOACK;
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;
413165951Sjhb			error = intsmb_error(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)) {
445165951Sjhb			error = intsmb_error(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