intpm.c revision 306140
1139735Simp/*-
2129198Scognet * Copyright (c) 1998, 1999 Takanori Watanabe
3129198Scognet * All rights reserved.
4129198Scognet *
5129198Scognet * Redistribution and use in source and binary forms, with or without
6129198Scognet * modification, are permitted provided that the following conditions
7129198Scognet * are met:
8129198Scognet * 1. Redistributions of source code must retain the above copyright
9129198Scognet *        notice, this list of conditions and the following disclaimer.
10129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
11129198Scognet *        notice, this list of conditions and the following disclaimer in the
12129198Scognet *        documentation and/or other materials provided with the distribution.
13129198Scognet *
14129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16129198Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17129198Scognet * ARE DISCLAIMED.    IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18129198Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19129198Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20129198Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24129198Scognet * SUCH DAMAGE.
25129198Scognet */
26129198Scognet
27129198Scognet#include <sys/cdefs.h>
28129198Scognet__FBSDID("$FreeBSD: stable/11/sys/dev/intpm/intpm.c 306140 2016-09-21 19:51:59Z avg $");
29129198Scognet
30129198Scognet#include <sys/param.h>
31129198Scognet#include <sys/systm.h>
32129198Scognet#include <sys/bus.h>
33129198Scognet#include <sys/kernel.h>
34129198Scognet#include <sys/lock.h>
35129198Scognet#include <sys/module.h>
36129198Scognet#include <sys/mutex.h>
37129198Scognet#include <sys/rman.h>
38129198Scognet#include <machine/bus.h>
39129198Scognet#include <dev/smbus/smbconf.h>
40129198Scognet
41129198Scognet#include "smbus_if.h"
42129198Scognet
43129198Scognet#include <dev/pci/pcireg.h>
44129198Scognet#include <dev/pci/pcivar.h>
45129198Scognet#include <dev/intpm/intpmreg.h>
46129198Scognet
47129198Scognet#include "opt_intpm.h"
48129198Scognet
49140310Scognetstruct intsmb_softc {
50146597Scognet	device_t		dev;
51129198Scognet	struct resource		*io_res;
52129198Scognet	struct resource		*irq_res;
53129198Scognet	void			*irq_hand;
54129198Scognet	device_t		smbus;
55129198Scognet	int			io_rid;
56129198Scognet	int			isbusy;
57129198Scognet	int			cfg_irq9;
58129198Scognet	int			sb8xx;
59129198Scognet	int			poll;
60129198Scognet	struct mtx		lock;
61129198Scognet};
62129198Scognet
63129198Scognet#define	INTSMB_LOCK(sc)		mtx_lock(&(sc)->lock)
64129198Scognet#define	INTSMB_UNLOCK(sc)	mtx_unlock(&(sc)->lock)
65129198Scognet#define	INTSMB_LOCK_ASSERT(sc)	mtx_assert(&(sc)->lock, MA_OWNED)
66129198Scognet
67129198Scognetstatic int intsmb_probe(device_t);
68129198Scognetstatic int intsmb_attach(device_t);
69129198Scognetstatic int intsmb_detach(device_t);
70129198Scognetstatic int intsmb_intr(struct intsmb_softc *sc);
71129198Scognetstatic int intsmb_slvintr(struct intsmb_softc *sc);
72129198Scognetstatic void intsmb_alrintr(struct intsmb_softc *sc);
73129198Scognetstatic int intsmb_callback(device_t dev, int index, void *data);
74129198Scognetstatic int intsmb_quick(device_t dev, u_char slave, int how);
75129198Scognetstatic int intsmb_sendb(device_t dev, u_char slave, char byte);
76129198Scognetstatic int intsmb_recvb(device_t dev, u_char slave, char *byte);
77129198Scognetstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
78129198Scognetstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
79129198Scognetstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
80129198Scognetstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
81129198Scognetstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
82129198Scognetstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
83129198Scognetstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
84129198Scognetstatic void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr);
85129198Scognetstatic int intsmb_stop(struct intsmb_softc *sc);
86135644Scognetstatic int intsmb_stop_poll(struct intsmb_softc *sc);
87135644Scognetstatic int intsmb_free(struct intsmb_softc *sc);
88135644Scognetstatic void intsmb_rawintr(void *arg);
89146597Scognet
90135644Scognetstatic int
91135644Scognetintsmb_probe(device_t dev)
92129198Scognet{
93135644Scognet
94135644Scognet	switch (pci_get_devid(dev)) {
95135644Scognet	case 0x71138086:	/* Intel 82371AB */
96156191Scognet	case 0x719b8086:	/* Intel 82443MX */
97156191Scognet#if 0
98147591Scognet	/* Not a good idea yet, this stops isab0 functioning */
99135644Scognet	case 0x02001166:	/* ServerWorks OSB4 */
100129198Scognet#endif
101129198Scognet		device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
102147591Scognet		break;
103147591Scognet	case 0x43721002:
104147591Scognet		device_set_desc(dev, "ATI IXP400 SMBus Controller");
105146597Scognet		break;
106146597Scognet	case 0x43851002:
107146597Scognet		device_set_desc(dev, "AMD SB600/7xx/8xx/9xx SMBus Controller");
108146597Scognet		break;
109146597Scognet	case 0x780b1022:	/* AMD FCH */
110146597Scognet		if (pci_get_revid(dev) < 0x40)
111146597Scognet			return (ENXIO);
112147591Scognet		device_set_desc(dev, "AMD FCH SMBus Controller");
113147591Scognet		break;
114147591Scognet	default:
115147591Scognet		return (ENXIO);
116147591Scognet	}
117147591Scognet
118147591Scognet	return (BUS_PROBE_DEFAULT);
119147591Scognet}
120147591Scognet
121147591Scognetstatic uint8_t
122147591Scognetsb8xx_pmio_read(struct resource *res, uint8_t reg)
123129198Scognet{
124129198Scognet	bus_write_1(res, 0, reg);	/* Index */
125129198Scognet	return (bus_read_1(res, 1));	/* Data */
126129198Scognet}
127137758Scognet
128140349Scognetstatic int
129137758Scognetsb8xx_attach(device_t dev)
130137760Scognet{
131135644Scognet	static const int	AMDSB_PMIO_INDEX = 0xcd6;
132129198Scognet	static const int	AMDSB_PMIO_WIDTH = 2;
133129198Scognet	static const int	AMDSB8_SMBUS_ADDR = 0x2c;
134129198Scognet	static const int		AMDSB8_SMBUS_EN = 0x01;
135129198Scognet	static const int		AMDSB8_SMBUS_ADDR_MASK = ~0x1fu;
136129198Scognet	static const int	AMDSB_SMBIO_WIDTH = 0x14;
137129198Scognet	static const int	AMDSB_SMBUS_CFG = 0x10;
138129198Scognet	static const int		AMDSB_SMBUS_IRQ = 0x01;
139129198Scognet	static const int		AMDSB_SMBUS_REV_MASK = ~0x0fu;
140129198Scognet	static const int		AMDSB_SMBUS_REV_SHIFT = 4;
141129198Scognet	static const int	AMDSB_IO_RID = 0;
142129198Scognet
143129198Scognet	struct intsmb_softc	*sc;
144129198Scognet	struct resource		*res;
145129198Scognet	uint16_t		addr;
146129198Scognet	uint8_t			cfg;
147129198Scognet	int			rid;
148129198Scognet	int			rc;
149129198Scognet
150129198Scognet	sc = device_get_softc(dev);
151129198Scognet	rid = AMDSB_IO_RID;
152129198Scognet	rc = bus_set_resource(dev, SYS_RES_IOPORT, rid, AMDSB_PMIO_INDEX,
153129198Scognet	    AMDSB_PMIO_WIDTH);
154129198Scognet	if (rc != 0) {
155129198Scognet		device_printf(dev, "bus_set_resource for PM IO failed\n");
156129198Scognet		return (ENXIO);
157129198Scognet	}
158129198Scognet	res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
159129198Scognet	    RF_ACTIVE | RF_SHAREABLE);
160129198Scognet	if (res == NULL) {
161129198Scognet		device_printf(dev, "bus_alloc_resource for PM IO failed\n");
162129198Scognet		return (ENXIO);
163129198Scognet	}
164129198Scognet
165129198Scognet	addr = sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR + 1);
166129198Scognet	addr <<= 8;
167129198Scognet	addr |= sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR);
168129198Scognet
169129198Scognet	bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
170129198Scognet	bus_delete_resource(dev, SYS_RES_IOPORT, rid);
171129198Scognet
172129198Scognet	if ((addr & AMDSB8_SMBUS_EN) == 0) {
173129198Scognet		device_printf(dev, "SB8xx SMBus not enabled\n");
174129198Scognet		return (ENXIO);
175129198Scognet	}
176129198Scognet
177129198Scognet	addr &= AMDSB8_SMBUS_ADDR_MASK;
178129198Scognet	sc->io_rid = AMDSB_IO_RID;
179129198Scognet	rc = bus_set_resource(dev, SYS_RES_IOPORT, sc->io_rid, addr,
180129198Scognet	    AMDSB_SMBIO_WIDTH);
181129198Scognet	if (rc != 0) {
182129198Scognet		device_printf(dev, "bus_set_resource for SMBus IO failed\n");
183129198Scognet		return (ENXIO);
184129198Scognet	}
185129198Scognet	if (res == NULL) {
186129198Scognet		device_printf(dev, "bus_alloc_resource for SMBus IO failed\n");
187147591Scognet		return (ENXIO);
188146597Scognet	}
189146597Scognet	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
190146597Scognet	    RF_ACTIVE | RF_SHAREABLE);
191146597Scognet	cfg = bus_read_1(sc->io_res, AMDSB_SMBUS_CFG);
192146597Scognet
193147591Scognet	sc->poll = 1;
194150860Scognet	device_printf(dev, "intr %s disabled ",
195150860Scognet	    (cfg & AMDSB_SMBUS_IRQ) != 0 ? "IRQ" : "SMI");
196146597Scognet	printf("revision %d\n",
197147591Scognet	    (cfg & AMDSB_SMBUS_REV_MASK) >> AMDSB_SMBUS_REV_SHIFT);
198147591Scognet
199147591Scognet	return (0);
200147591Scognet}
201147591Scognet
202147591Scognetstatic void
203146597Scognetintsmb_release_resources(device_t dev)
204146597Scognet{
205146597Scognet	struct intsmb_softc *sc = device_get_softc(dev);
206147591Scognet
207146597Scognet	if (sc->smbus)
208146597Scognet		device_delete_child(dev, sc->smbus);
209146597Scognet	if (sc->irq_hand)
210146597Scognet		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
211146597Scognet	if (sc->irq_res)
212146597Scognet		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
213147591Scognet	if (sc->io_res)
214146597Scognet		bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid,
215146597Scognet		    sc->io_res);
216146597Scognet	mtx_destroy(&sc->lock);
217146597Scognet}
218129198Scognet
219129198Scognetstatic int
220129198Scognetintsmb_attach(device_t dev)
221135644Scognet{
222135644Scognet	struct intsmb_softc *sc = device_get_softc(dev);
223129198Scognet	int error, rid, value;
224129198Scognet	int intr;
225129198Scognet	char *str;
226129198Scognet
227129198Scognet	sc->dev = dev;
228129198Scognet
229129198Scognet	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
230129198Scognet
231129198Scognet	sc->cfg_irq9 = 0;
232129198Scognet	switch (pci_get_devid(dev)) {
233129198Scognet#ifndef NO_CHANGE_PCICONF
234129198Scognet	case 0x71138086:	/* Intel 82371AB */
235129198Scognet	case 0x719b8086:	/* Intel 82443MX */
236129198Scognet		/* Changing configuration is allowed. */
237140313Scognet		sc->cfg_irq9 = 1;
238143294Smux		break;
239143284Smux#endif
240129198Scognet	case 0x43851002:
241140313Scognet		if (pci_get_revid(dev) >= 0x40)
242129198Scognet			sc->sb8xx = 1;
243129198Scognet		break;
244129198Scognet	case 0x780b1022:
245129198Scognet		sc->sb8xx = 1;
246129198Scognet		break;
247129198Scognet	}
248129198Scognet
249129198Scognet	if (sc->sb8xx) {
250129198Scognet		error = sb8xx_attach(dev);
251129198Scognet		if (error != 0)
252129198Scognet			goto fail;
253129198Scognet		else
254129198Scognet			goto no_intr;
255129198Scognet	}
256129198Scognet
257135644Scognet	sc->io_rid = PCI_BASE_ADDR_SMB;
258129198Scognet	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
259129198Scognet	    RF_ACTIVE);
260129198Scognet	if (sc->io_res == NULL) {
261129198Scognet		device_printf(dev, "Could not allocate I/O space\n");
262129198Scognet		error = ENXIO;
263129198Scognet		goto fail;
264129198Scognet	}
265129198Scognet
266129198Scognet	if (sc->cfg_irq9) {
267129198Scognet		pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
268129198Scognet		pci_write_config(dev, PCI_HST_CFG_SMB,
269129198Scognet		    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
270129198Scognet	}
271134934Sscottl	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
272134934Sscottl	sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0;
273134934Sscottl	intr = value & PCI_INTR_SMB_MASK;
274134934Sscottl	switch (intr) {
275134934Sscottl	case PCI_INTR_SMB_SMI:
276129198Scognet		str = "SMI";
277129198Scognet		break;
278129198Scognet	case PCI_INTR_SMB_IRQ9:
279129198Scognet		str = "IRQ 9";
280129198Scognet		break;
281129198Scognet	case PCI_INTR_SMB_IRQ_PCI:
282129198Scognet		str = "PCI IRQ";
283129198Scognet		break;
284129198Scognet	default:
285129198Scognet		str = "BOGUS";
286129198Scognet	}
287129198Scognet
288129198Scognet	device_printf(dev, "intr %s %s ", str,
289129198Scognet	    sc->poll == 0 ? "enabled" : "disabled");
290143294Smux	printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
291143284Smux
292140313Scognet	if (!sc->poll && intr == PCI_INTR_SMB_SMI) {
293129198Scognet		device_printf(dev,
294129198Scognet		    "using polling mode when configured interrupt is SMI\n");
295129198Scognet		sc->poll = 1;
296129198Scognet	}
297129198Scognet
298129198Scognet	if (sc->poll)
299140680Scognet	    goto no_intr;
300140313Scognet
301140680Scognet	if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) {
302140313Scognet		device_printf(dev, "Unsupported interrupt mode\n");
303129198Scognet		error = ENXIO;
304129198Scognet		goto fail;
305129198Scognet	}
306129198Scognet
307129198Scognet	/* Force IRQ 9. */
308129198Scognet	rid = 0;
309129198Scognet	if (sc->cfg_irq9)
310129198Scognet		bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
311129198Scognet
312129198Scognet	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
313129198Scognet	    RF_SHAREABLE | RF_ACTIVE);
314129198Scognet	if (sc->irq_res == NULL) {
315129198Scognet		device_printf(dev, "Could not allocate irq\n");
316129198Scognet		error = ENXIO;
317129198Scognet		goto fail;
318129198Scognet	}
319129198Scognet
320129198Scognet	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
321129198Scognet	    NULL, intsmb_rawintr, sc, &sc->irq_hand);
322129198Scognet	if (error) {
323129198Scognet		device_printf(dev, "Failed to map intr\n");
324129198Scognet		goto fail;
325143294Smux	}
326140313Scognet
327129198Scognetno_intr:
328129198Scognet	sc->isbusy = 0;
329129198Scognet	sc->smbus = device_add_child(dev, "smbus", -1);
330129198Scognet	if (sc->smbus == NULL) {
331129198Scognet		device_printf(dev, "failed to add smbus child\n");
332129198Scognet		error = ENXIO;
333129198Scognet		goto fail;
334129198Scognet	}
335129198Scognet	error = device_probe_and_attach(sc->smbus);
336129198Scognet	if (error) {
337129198Scognet		device_printf(dev, "failed to probe+attach smbus child\n");
338140680Scognet		goto fail;
339140313Scognet	}
340140680Scognet
341129198Scognet#ifdef ENABLE_ALART
342146597Scognet	/* Enable Arart */
343140313Scognet	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
344143294Smux#endif
345129198Scognet	return (0);
346140313Scognet
347129198Scognetfail:
348135644Scognet	intsmb_release_resources(dev);
349129198Scognet	return (error);
350129198Scognet}
351143294Smux
352143284Smuxstatic int
353140313Scognetintsmb_detach(device_t dev)
354129198Scognet{
355129198Scognet	int error;
356129198Scognet
357129198Scognet	error = bus_generic_detach(dev);
358129198Scognet	if (error) {
359129198Scognet		device_printf(dev, "bus detach failed\n");
360129198Scognet		return (error);
361129198Scognet	}
362129198Scognet
363129198Scognet	intsmb_release_resources(dev);
364135644Scognet	return (0);
365146597Scognet}
366129198Scognet
367143294Smuxstatic void
368129198Scognetintsmb_rawintr(void *arg)
369129198Scognet{
370129198Scognet	struct intsmb_softc *sc = arg;
371129198Scognet
372129198Scognet	INTSMB_LOCK(sc);
373129198Scognet	intsmb_intr(sc);
374129198Scognet	intsmb_slvintr(sc);
375129198Scognet	INTSMB_UNLOCK(sc);
376129198Scognet}
377129198Scognet
378129198Scognetstatic int
379129198Scognetintsmb_callback(device_t dev, int index, void *data)
380135644Scognet{
381129198Scognet	int error = 0;
382129198Scognet
383129198Scognet	switch (index) {
384129198Scognet	case SMB_REQUEST_BUS:
385129198Scognet		break;
386129198Scognet	case SMB_RELEASE_BUS:
387129198Scognet		break;
388129198Scognet	default:
389129198Scognet		error = SMB_EINVAL;
390129198Scognet	}
391146597Scognet
392143671Sjmg	return (error);
393143671Sjmg}
394143671Sjmg
395143671Sjmg/* Counterpart of smbtx_smb_free(). */
396135644Scognetstatic int
397143671Sjmgintsmb_free(struct intsmb_softc *sc)
398143671Sjmg{
399143671Sjmg
400135644Scognet	INTSMB_LOCK_ASSERT(sc);
401129198Scognet	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
402129198Scognet#ifdef ENABLE_ALART
403129198Scognet	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
404129198Scognet#endif
405129198Scognet	    sc->isbusy)
406129198Scognet		return (SMB_EBUSY);
407129198Scognet
408129198Scognet	sc->isbusy = 1;
409129198Scognet	/* Disable Interrupt in slave part. */
410129198Scognet#ifndef ENABLE_ALART
411129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
412129198Scognet#endif
413140313Scognet	/* Reset INTR Flag to prepare INTR. */
414140313Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
415146597Scognet	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
416140313Scognet	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
417140313Scognet	return (0);
418129198Scognet}
419129198Scognet
420129198Scognetstatic int
421156191Scognetintsmb_intr(struct intsmb_softc *sc)
422156191Scognet{
423156191Scognet	int status, tmp;
424156191Scognet
425156191Scognet	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
426156191Scognet	if (status & PIIX4_SMBHSTSTAT_BUSY)
427156191Scognet		return (1);
428156191Scognet
429156191Scognet	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
430156191Scognet	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
431156191Scognet
432156191Scognet		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
433156191Scognet		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
434156191Scognet		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
435156191Scognet		if (sc->isbusy) {
436156191Scognet			sc->isbusy = 0;
437156191Scognet			wakeup(sc);
438129198Scognet		}
439129198Scognet		return (0);
440129198Scognet	}
441129198Scognet	return (1); /* Not Completed */
442129198Scognet}
443129198Scognet
444129198Scognetstatic int
445129198Scognetintsmb_slvintr(struct intsmb_softc *sc)
446129198Scognet{
447129198Scognet	int status;
448156191Scognet
449156191Scognet	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
450156191Scognet	if (status & PIIX4_SMBSLVSTS_BUSY)
451156191Scognet		return (1);
452156191Scognet	if (status & PIIX4_SMBSLVSTS_ALART)
453156191Scognet		intsmb_alrintr(sc);
454129198Scognet	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
455129198Scognet		| PIIX4_SMBSLVSTS_SDW1)) {
456129198Scognet	}
457129198Scognet
458129198Scognet	/* Reset Status Register */
459135644Scognet	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
460146597Scognet	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
461143294Smux	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
462129198Scognet	return (0);
463129198Scognet}
464129198Scognet
465129198Scognetstatic void
466129198Scognetintsmb_alrintr(struct intsmb_softc *sc)
467129198Scognet{
468129198Scognet	int slvcnt;
469129198Scognet#ifdef ENABLE_ALART
470147591Scognet	int error;
471140349Scognet	uint8_t addr;
472137758Scognet#endif
473137760Scognet
474129198Scognet	/* Stop generating INTR from ALART. */
475129198Scognet	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
476129198Scognet#ifdef ENABLE_ALART
477129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
478129198Scognet	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
479129198Scognet#endif
480129198Scognet	DELAY(5);
481129198Scognet
482129198Scognet	/* Ask bus who asserted it and then ask it what's the matter. */
483129198Scognet#ifdef ENABLE_ALART
484129198Scognet	error = intsmb_free(sc);
485129198Scognet	if (error)
486129198Scognet		return;
487140313Scognet
488140313Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
489140313Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
490129198Scognet	error = intsmb_stop_poll(sc);
491129198Scognet	if (error)
492129198Scognet		device_printf(sc->dev, "ALART: ERROR\n");
493129198Scognet	else {
494129198Scognet		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
495129198Scognet		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
496129198Scognet	}
497147591Scognet
498129198Scognet	/* Re-enable INTR from ALART. */
499129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
500129198Scognet	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
501129198Scognet	DELAY(5);
502129198Scognet#endif
503129198Scognet}
504135644Scognet
505129198Scognetstatic void
506129198Scognetintsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
507129198Scognet{
508129198Scognet	unsigned char tmp;
509132514Scognet
510129198Scognet	INTSMB_LOCK_ASSERT(sc);
511129198Scognet	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
512129198Scognet	tmp &= 0xe0;
513129198Scognet	tmp |= cmd;
514129198Scognet	tmp |= PIIX4_SMBHSTCNT_START;
515129198Scognet
516135644Scognet	/* While not in autoconfiguration enable interrupts. */
517135644Scognet	if (!sc->poll && !cold && !nointr)
518129198Scognet		tmp |= PIIX4_SMBHSTCNT_INTREN;
519129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
520129198Scognet}
521129198Scognet
522129198Scognetstatic int
523129198Scognetintsmb_error(device_t dev, int status)
524135644Scognet{
525129198Scognet	int error = 0;
526129198Scognet
527129198Scognet	if (status & PIIX4_SMBHSTSTAT_ERR)
528129198Scognet		error |= SMB_EBUSERR;
529129198Scognet	if (status & PIIX4_SMBHSTSTAT_BUSC)
530135644Scognet		error |= SMB_ECOLLI;
531129198Scognet	if (status & PIIX4_SMBHSTSTAT_FAIL)
532129198Scognet		error |= SMB_ENOACK;
533135644Scognet
534135644Scognet	if (error != 0 && bootverbose)
535135644Scognet		device_printf(dev, "error = %d, status = %#x\n", error, status);
536135644Scognet
537135644Scognet	return (error);
538135644Scognet}
539135644Scognet
540135644Scognet/*
541135644Scognet * Polling Code.
542135644Scognet *
543135644Scognet * Polling is not encouraged because it requires waiting for the
544135644Scognet * device if it is busy.
545135644Scognet * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
546135644Scognet * polling code then.
547129198Scognet */
548129198Scognetstatic int
549129198Scognetintsmb_stop_poll(struct intsmb_softc *sc)
550129198Scognet{
551129198Scognet	int error, i, status, tmp;
552129198Scognet
553129198Scognet	INTSMB_LOCK_ASSERT(sc);
554129198Scognet
555129198Scognet	/* First, wait for busy to be set. */
556129198Scognet	for (i = 0; i < 0x7fff; i++)
557129198Scognet		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
558129198Scognet		    PIIX4_SMBHSTSTAT_BUSY)
559129198Scognet			break;
560129198Scognet
561129198Scognet	/* Wait for busy to clear. */
562129198Scognet	for (i = 0; i < 0x7fff; i++) {
563129198Scognet		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
564129198Scognet		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
565129198Scognet			sc->isbusy = 0;
566129198Scognet			error = intsmb_error(sc->dev, status);
567137760Scognet			return (error);
568137760Scognet		}
569137760Scognet	}
570137760Scognet
571137760Scognet	/* Timed out waiting for busy to clear. */
572137760Scognet	sc->isbusy = 0;
573140349Scognet	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
574137760Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
575137760Scognet	return (SMB_ETIMEOUT);
576137760Scognet}
577129198Scognet
578129198Scognet/*
579129198Scognet * Wait for completion and return result.
580135644Scognet */
581135644Scognetstatic int
582135644Scognetintsmb_stop(struct intsmb_softc *sc)
583129198Scognet{
584129198Scognet	int error, status;
585129198Scognet
586129198Scognet	INTSMB_LOCK_ASSERT(sc);
587129198Scognet
588129198Scognet	if (sc->poll || cold)
589129198Scognet		/* So that it can use device during device probe on SMBus. */
590129198Scognet		return (intsmb_stop_poll(sc));
591129198Scognet
592129198Scognet	error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8);
593129198Scognet	if (error == 0) {
594129198Scognet		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
595129198Scognet		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
596129198Scognet			error = intsmb_error(sc->dev, status);
597129198Scognet			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
598129198Scognet				device_printf(sc->dev, "unknown cause why?\n");
599129198Scognet#ifdef ENABLE_ALART
600140682Scognet			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
601140682Scognet			    PIIX4_SMBSLVCNT_ALTEN);
602140682Scognet#endif
603140682Scognet			return (error);
604140682Scognet		}
605140682Scognet	}
606140682Scognet
607140682Scognet	/* Timeout Procedure. */
608140682Scognet	sc->isbusy = 0;
609143063Sjoerg
610140682Scognet	/* Re-enable suppressed interrupt from slave part. */
611140682Scognet	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
612140682Scognet	if (error == EWOULDBLOCK)
613140682Scognet		return (SMB_ETIMEOUT);
614140682Scognet	else
615143671Sjmg		return (SMB_EABORT);
616143671Sjmg}
617140682Scognet
618140682Scognetstatic int
619140682Scognetintsmb_quick(device_t dev, u_char slave, int how)
620140682Scognet{
621140682Scognet	struct intsmb_softc *sc = device_get_softc(dev);
622140682Scognet	int error;
623140682Scognet	u_char data;
624140682Scognet
625140682Scognet	data = slave;
626140682Scognet
627140682Scognet	/* Quick command is part of Address, I think. */
628140682Scognet	switch(how) {
629143294Smux	case SMB_QWRITE:
630143284Smux		data &= ~LSB;
631140682Scognet		break;
632140682Scognet	case SMB_QREAD:
633140682Scognet		data |= LSB;
634140682Scognet		break;
635140682Scognet	default:
636129198Scognet		return (SMB_EINVAL);
637129198Scognet	}
638129198Scognet
639129198Scognet	INTSMB_LOCK(sc);
640129198Scognet	error = intsmb_free(sc);
641129198Scognet	if (error) {
642129198Scognet		INTSMB_UNLOCK(sc);
643143063Sjoerg		return (error);
644129198Scognet	}
645129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
646129198Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
647129198Scognet	error = intsmb_stop(sc);
648137760Scognet	INTSMB_UNLOCK(sc);
649129198Scognet	return (error);
650129198Scognet}
651129198Scognet
652135644Scognetstatic int
653135644Scognetintsmb_sendb(device_t dev, u_char slave, char byte)
654135644Scognet{
655146597Scognet	struct intsmb_softc *sc = device_get_softc(dev);
656129198Scognet	int error;
657129198Scognet
658129198Scognet	INTSMB_LOCK(sc);
659129198Scognet	error = intsmb_free(sc);
660129198Scognet	if (error) {
661146597Scognet		INTSMB_UNLOCK(sc);
662129198Scognet		return (error);
663137758Scognet	}
664137760Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
665146597Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
666146597Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
667129198Scognet	error = intsmb_stop(sc);
668129198Scognet	INTSMB_UNLOCK(sc);
669129198Scognet	return (error);
670129198Scognet}
671129198Scognet
672129198Scognetstatic int
673129198Scognetintsmb_recvb(device_t dev, u_char slave, char *byte)
674129198Scognet{
675129198Scognet	struct intsmb_softc *sc = device_get_softc(dev);
676129198Scognet	int error;
677129198Scognet
678135644Scognet	INTSMB_LOCK(sc);
679129198Scognet	error = intsmb_free(sc);
680129198Scognet	if (error) {
681143294Smux		INTSMB_UNLOCK(sc);
682143284Smux		return (error);
683140313Scognet	}
684129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
685129198Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
686129198Scognet	error = intsmb_stop(sc);
687140310Scognet	if (error == 0) {
688140310Scognet#ifdef RECV_IS_IN_CMD
689140310Scognet		/*
690140310Scognet		 * Linux SMBus stuff also troubles
691140310Scognet		 * Because Intel's datasheet does not make clear.
692140310Scognet		 */
693140310Scognet		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
694140310Scognet#else
695140310Scognet		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
696140310Scognet#endif
697140349Scognet	}
698140349Scognet	INTSMB_UNLOCK(sc);
699140349Scognet	return (error);
700146597Scognet}
701140310Scognet
702140310Scognetstatic int
703140310Scognetintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
704140310Scognet{
705140310Scognet	struct intsmb_softc *sc = device_get_softc(dev);
706140310Scognet	int error;
707140310Scognet
708140310Scognet	INTSMB_LOCK(sc);
709140310Scognet	error = intsmb_free(sc);
710140310Scognet	if (error) {
711146597Scognet		INTSMB_UNLOCK(sc);
712140310Scognet		return (error);
713140310Scognet	}
714140310Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
715140310Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
716140310Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
717140310Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
718140310Scognet	error = intsmb_stop(sc);
719140310Scognet	INTSMB_UNLOCK(sc);
720143294Smux	return (error);
721143284Smux}
722140310Scognet
723140310Scognetstatic int
724140310Scognetintsmb_writew(device_t dev, u_char slave, char cmd, short word)
725129198Scognet{
726129198Scognet	struct intsmb_softc *sc = device_get_softc(dev);
727129198Scognet	int error;
728129198Scognet
729129198Scognet	INTSMB_LOCK(sc);
730129198Scognet	error = intsmb_free(sc);
731129198Scognet	if (error) {
732129198Scognet		INTSMB_UNLOCK(sc);
733129198Scognet		return (error);
734143063Sjoerg	}
735129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
736129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
737129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
738129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
739137760Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
740129198Scognet	error = intsmb_stop(sc);
741129198Scognet	INTSMB_UNLOCK(sc);
742137758Scognet	return (error);
743129198Scognet}
744129198Scognet
745129198Scognetstatic int
746135644Scognetintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
747135644Scognet{
748135644Scognet	struct intsmb_softc *sc = device_get_softc(dev);
749146597Scognet	int error;
750129198Scognet
751129198Scognet	INTSMB_LOCK(sc);
752138683Scognet	error = intsmb_free(sc);
753138683Scognet	if (error) {
754137758Scognet		INTSMB_UNLOCK(sc);
755137758Scognet		return (error);
756137758Scognet	}
757129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
758137760Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
759137760Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
760129198Scognet	error = intsmb_stop(sc);
761129198Scognet	if (error == 0)
762129198Scognet		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
763129198Scognet	INTSMB_UNLOCK(sc);
764129198Scognet	return (error);
765129198Scognet}
766129198Scognet
767129198Scognetstatic int
768129198Scognetintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
769129198Scognet{
770129198Scognet	struct intsmb_softc *sc = device_get_softc(dev);
771137760Scognet	int error;
772129198Scognet
773146597Scognet	INTSMB_LOCK(sc);
774129198Scognet	error = intsmb_free(sc);
775129198Scognet	if (error) {
776129198Scognet		INTSMB_UNLOCK(sc);
777129198Scognet		return (error);
778129198Scognet	}
779129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
780129198Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
781129198Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
782129198Scognet	error = intsmb_stop(sc);
783129198Scognet	if (error == 0) {
784129198Scognet		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
785129198Scognet		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
786129198Scognet	}
787129198Scognet	INTSMB_UNLOCK(sc);
788143294Smux	return (error);
789143284Smux}
790129198Scognet
791129198Scognet/*
792129198Scognet * Data sheet claims that it implements all function, but also claims
793129198Scognet * that it implements 7 function and not mention PCALL. So I don't know
794135644Scognet * whether it will work.
795129198Scognet */
796129198Scognetstatic int
797143655Sjmgintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
798150893Scognet{
799135644Scognet#ifdef PROCCALL_TEST
800129198Scognet	struct intsmb_softc *sc = device_get_softc(dev);
801129198Scognet	int error;
802129198Scognet
803147591Scognet	INTSMB_LOCK(sc);
804135644Scognet	error = intsmb_free(sc);
805135644Scognet	if (error) {
806135644Scognet		INTSMB_UNLOCK(sc);
807135644Scognet		return (error);
808135644Scognet	}
809159107Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
810159107Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
811159107Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
812159107Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
813159107Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
814159107Scognet	error = intsmb_stop(sc);
815159107Scognet	if (error == 0) {
816135644Scognet		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
817159107Scognet		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
818159107Scognet	}
819135644Scognet	INTSMB_UNLOCK(sc);
820135644Scognet	return (error);
821129198Scognet#else
822143655Sjmg	return (SMB_ENOTSUPP);
823129198Scognet#endif
824135644Scognet}
825135644Scognet
826135644Scognetstatic int
827135644Scognetintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
828135644Scognet{
829159107Scognet	struct intsmb_softc *sc = device_get_softc(dev);
830129198Scognet	int error, i;
831150893Scognet
832129198Scognet	if (count > SMBBLOCKTRANS_MAX || count == 0)
833150860Scognet		return (SMB_EINVAL);
834146597Scognet
835146597Scognet	INTSMB_LOCK(sc);
836146597Scognet	error = intsmb_free(sc);
837143294Smux	if (error) {
838135644Scognet		INTSMB_UNLOCK(sc);
839135644Scognet		return (error);
840135644Scognet	}
841135644Scognet
842135644Scognet	/* Reset internal array index. */
843135644Scognet	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
844135644Scognet
845147591Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
846147591Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
847135644Scognet	for (i = 0; i < count; i++)
848135644Scognet		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
849135644Scognet	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
850135644Scognet	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
851135644Scognet	error = intsmb_stop(sc);
852135644Scognet	INTSMB_UNLOCK(sc);
853135644Scognet	return (error);
854135644Scognet}
855135644Scognet
856135644Scognetstatic int
857135644Scognetintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
858135644Scognet{
859135644Scognet	struct intsmb_softc *sc = device_get_softc(dev);
860135644Scognet	int error, i;
861135644Scognet	u_char data, nread;
862135644Scognet
863135644Scognet	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
864135644Scognet		return (SMB_EINVAL);
865135644Scognet
866129198Scognet	INTSMB_LOCK(sc);
867135644Scognet	error = intsmb_free(sc);
868129198Scognet	if (error) {
869		INTSMB_UNLOCK(sc);
870		return (error);
871	}
872
873	/* Reset internal array index. */
874	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
875
876	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
877	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
878	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
879	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
880	error = intsmb_stop(sc);
881	if (error == 0) {
882		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
883		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
884			for (i = 0; i < nread; i++) {
885				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
886				if (i < *count)
887					buf[i] = data;
888			}
889			*count = nread;
890		} else
891			error = SMB_EBUSERR;
892	}
893	INTSMB_UNLOCK(sc);
894	return (error);
895}
896
897static devclass_t intsmb_devclass;
898
899static device_method_t intsmb_methods[] = {
900	/* Device interface */
901	DEVMETHOD(device_probe,		intsmb_probe),
902	DEVMETHOD(device_attach,	intsmb_attach),
903	DEVMETHOD(device_detach,	intsmb_detach),
904
905	/* SMBus interface */
906	DEVMETHOD(smbus_callback,	intsmb_callback),
907	DEVMETHOD(smbus_quick,		intsmb_quick),
908	DEVMETHOD(smbus_sendb,		intsmb_sendb),
909	DEVMETHOD(smbus_recvb,		intsmb_recvb),
910	DEVMETHOD(smbus_writeb,		intsmb_writeb),
911	DEVMETHOD(smbus_writew,		intsmb_writew),
912	DEVMETHOD(smbus_readb,		intsmb_readb),
913	DEVMETHOD(smbus_readw,		intsmb_readw),
914	DEVMETHOD(smbus_pcall,		intsmb_pcall),
915	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
916	DEVMETHOD(smbus_bread,		intsmb_bread),
917
918	DEVMETHOD_END
919};
920
921static driver_t intsmb_driver = {
922	"intsmb",
923	intsmb_methods,
924	sizeof(struct intsmb_softc),
925};
926
927DRIVER_MODULE_ORDERED(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0,
928    SI_ORDER_ANY);
929DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
930MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
931MODULE_VERSION(intsmb, 1);
932