intpm.c revision 306121
1/*-
2 * Copyright (c) 1998, 1999 Takanori Watanabe
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *        notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *        notice, this list of conditions and the following disclaimer in the
12 *        documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.    IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/sys/pci/intpm.c 306121 2016-09-21 16:23:31Z avg $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/lock.h>
35#include <sys/module.h>
36#include <sys/mutex.h>
37#include <sys/rman.h>
38#include <machine/bus.h>
39#include <dev/smbus/smbconf.h>
40
41#include "smbus_if.h"
42
43#include <dev/pci/pcireg.h>
44#include <dev/pci/pcivar.h>
45#include <pci/intpmreg.h>
46
47#include "opt_intpm.h"
48
49struct intsmb_softc {
50	device_t		dev;
51	struct resource		*io_res;
52	struct resource		*irq_res;
53	void			*irq_hand;
54	device_t		smbus;
55	int			io_rid;
56	int			isbusy;
57	int			cfg_irq9;
58	int			sb8xx;
59	int			poll;
60	struct mtx		lock;
61};
62
63#define	INTSMB_LOCK(sc)		mtx_lock(&(sc)->lock)
64#define	INTSMB_UNLOCK(sc)	mtx_unlock(&(sc)->lock)
65#define	INTSMB_LOCK_ASSERT(sc)	mtx_assert(&(sc)->lock, MA_OWNED)
66
67static int intsmb_probe(device_t);
68static int intsmb_attach(device_t);
69static int intsmb_detach(device_t);
70static int intsmb_intr(struct intsmb_softc *sc);
71static int intsmb_slvintr(struct intsmb_softc *sc);
72static void intsmb_alrintr(struct intsmb_softc *sc);
73static int intsmb_callback(device_t dev, int index, void *data);
74static int intsmb_quick(device_t dev, u_char slave, int how);
75static int intsmb_sendb(device_t dev, u_char slave, char byte);
76static int intsmb_recvb(device_t dev, u_char slave, char *byte);
77static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
78static int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
79static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
80static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
81static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
82static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
83static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
84static void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr);
85static int intsmb_stop(struct intsmb_softc *sc);
86static int intsmb_stop_poll(struct intsmb_softc *sc);
87static int intsmb_free(struct intsmb_softc *sc);
88static void intsmb_rawintr(void *arg);
89
90static int
91intsmb_probe(device_t dev)
92{
93
94	switch (pci_get_devid(dev)) {
95	case 0x71138086:	/* Intel 82371AB */
96	case 0x719b8086:	/* Intel 82443MX */
97#if 0
98	/* Not a good idea yet, this stops isab0 functioning */
99	case 0x02001166:	/* ServerWorks OSB4 */
100#endif
101		device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
102		break;
103	case 0x43721002:
104		device_set_desc(dev, "ATI IXP400 SMBus Controller");
105		break;
106	case 0x43851002:
107		device_set_desc(dev, "AMD SB600/7xx/8xx/9xx SMBus Controller");
108		break;
109	case 0x780b1022:	/* AMD FCH */
110		if (pci_get_revid(dev) < 0x40)
111			return (ENXIO);
112		device_set_desc(dev, "AMD FCH SMBus Controller");
113		break;
114	default:
115		return (ENXIO);
116	}
117
118	return (BUS_PROBE_DEFAULT);
119}
120
121static uint8_t
122sb8xx_pmio_read(struct resource *res, uint8_t reg)
123{
124	bus_write_1(res, 0, reg);	/* Index */
125	return (bus_read_1(res, 1));	/* Data */
126}
127
128static int
129sb8xx_attach(device_t dev)
130{
131	static const int	AMDSB_PMIO_INDEX = 0xcd6;
132	static const int	AMDSB_PMIO_WIDTH = 2;
133	static const int	AMDSB8_SMBUS_ADDR = 0x2c;
134	static const int		AMDSB8_SMBUS_EN = 0x01;
135	static const int		AMDSB8_SMBUS_ADDR_MASK = ~0x1fu;
136	static const int	AMDSB_SMBIO_WIDTH = 0x14;
137	static const int	AMDSB_SMBUS_CFG = 0x10;
138	static const int		AMDSB_SMBUS_IRQ = 0x01;
139	static const int		AMDSB_SMBUS_REV_MASK = ~0x0fu;
140	static const int		AMDSB_SMBUS_REV_SHIFT = 4;
141	static const int	AMDSB_IO_RID = 0;
142
143	struct intsmb_softc	*sc;
144	struct resource		*res;
145	uint16_t		addr;
146	uint8_t			cfg;
147	int			rid;
148	int			rc;
149
150	sc = device_get_softc(dev);
151	rid = AMDSB_IO_RID;
152	rc = bus_set_resource(dev, SYS_RES_IOPORT, rid, AMDSB_PMIO_INDEX,
153	    AMDSB_PMIO_WIDTH);
154	if (rc != 0) {
155		device_printf(dev, "bus_set_resource for PM IO failed\n");
156		return (ENXIO);
157	}
158	res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
159	    RF_ACTIVE | RF_SHAREABLE);
160	if (res == NULL) {
161		device_printf(dev, "bus_alloc_resource for PM IO failed\n");
162		return (ENXIO);
163	}
164
165	addr = sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR + 1);
166	addr <<= 8;
167	addr |= sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR);
168
169	bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
170	bus_delete_resource(dev, SYS_RES_IOPORT, rid);
171
172	if ((addr & AMDSB8_SMBUS_EN) == 0) {
173		device_printf(dev, "SB8xx SMBus not enabled\n");
174		return (ENXIO);
175	}
176
177	addr &= AMDSB8_SMBUS_ADDR_MASK;
178	sc->io_rid = AMDSB_IO_RID;
179	rc = bus_set_resource(dev, SYS_RES_IOPORT, sc->io_rid, addr,
180	    AMDSB_SMBIO_WIDTH);
181	if (rc != 0) {
182		device_printf(dev, "bus_set_resource for SMBus IO failed\n");
183		return (ENXIO);
184	}
185	if (res == NULL) {
186		device_printf(dev, "bus_alloc_resource for SMBus IO failed\n");
187		return (ENXIO);
188	}
189	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
190	    RF_ACTIVE | RF_SHAREABLE);
191	cfg = bus_read_1(sc->io_res, AMDSB_SMBUS_CFG);
192
193	sc->poll = 1;
194	device_printf(dev, "intr %s disabled ",
195	    (cfg & AMDSB_SMBUS_IRQ) != 0 ? "IRQ" : "SMI");
196	printf("revision %d\n",
197	    (cfg & AMDSB_SMBUS_REV_MASK) >> AMDSB_SMBUS_REV_SHIFT);
198
199	return (0);
200}
201
202static int
203intsmb_attach(device_t dev)
204{
205	struct intsmb_softc *sc = device_get_softc(dev);
206	int error, rid, value;
207	int intr;
208	char *str;
209
210	sc->dev = dev;
211
212	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
213
214	sc->cfg_irq9 = 0;
215	switch (pci_get_devid(dev)) {
216#ifndef NO_CHANGE_PCICONF
217	case 0x71138086:	/* Intel 82371AB */
218	case 0x719b8086:	/* Intel 82443MX */
219		/* Changing configuration is allowed. */
220		sc->cfg_irq9 = 1;
221		break;
222#endif
223	case 0x43851002:
224	case 0x780b1022:
225		if (pci_get_revid(dev) >= 0x40)
226			sc->sb8xx = 1;
227		break;
228	}
229
230	if (sc->sb8xx) {
231		error = sb8xx_attach(dev);
232		if (error != 0)
233			goto fail;
234		else
235			goto no_intr;
236	}
237
238	sc->io_rid = PCI_BASE_ADDR_SMB;
239	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
240	    RF_ACTIVE);
241	if (sc->io_res == NULL) {
242		device_printf(dev, "Could not allocate I/O space\n");
243		error = ENXIO;
244		goto fail;
245	}
246
247	if (sc->cfg_irq9) {
248		pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
249		pci_write_config(dev, PCI_HST_CFG_SMB,
250		    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
251	}
252	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
253	sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0;
254	intr = value & PCI_INTR_SMB_MASK;
255	switch (intr) {
256	case PCI_INTR_SMB_SMI:
257		str = "SMI";
258		break;
259	case PCI_INTR_SMB_IRQ9:
260		str = "IRQ 9";
261		break;
262	case PCI_INTR_SMB_IRQ_PCI:
263		str = "PCI IRQ";
264		break;
265	default:
266		str = "BOGUS";
267	}
268
269	device_printf(dev, "intr %s %s ", str,
270	    sc->poll == 0 ? "enabled" : "disabled");
271	printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
272
273	if (!sc->poll && intr == PCI_INTR_SMB_SMI) {
274		device_printf(dev,
275		    "using polling mode when configured interrupt is SMI\n");
276		sc->poll = 1;
277	}
278
279	if (sc->poll)
280	    goto no_intr;
281
282	if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) {
283		device_printf(dev, "Unsupported interrupt mode\n");
284		error = ENXIO;
285		goto fail;
286	}
287
288	/* Force IRQ 9. */
289	rid = 0;
290	if (sc->cfg_irq9)
291		bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
292
293	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
294	    RF_SHAREABLE | RF_ACTIVE);
295	if (sc->irq_res == NULL) {
296		device_printf(dev, "Could not allocate irq\n");
297		error = ENXIO;
298		goto fail;
299	}
300
301	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
302	    NULL, intsmb_rawintr, sc, &sc->irq_hand);
303	if (error) {
304		device_printf(dev, "Failed to map intr\n");
305		goto fail;
306	}
307
308no_intr:
309	sc->isbusy = 0;
310	sc->smbus = device_add_child(dev, "smbus", -1);
311	if (sc->smbus == NULL) {
312		error = ENXIO;
313		goto fail;
314	}
315	error = device_probe_and_attach(sc->smbus);
316	if (error)
317		goto fail;
318
319#ifdef ENABLE_ALART
320	/* Enable Arart */
321	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
322#endif
323	return (0);
324
325fail:
326	intsmb_detach(dev);
327	return (error);
328}
329
330static int
331intsmb_detach(device_t dev)
332{
333	struct intsmb_softc *sc = device_get_softc(dev);
334	int error;
335
336	error = bus_generic_detach(dev);
337	if (error)
338		return (error);
339
340	if (sc->smbus)
341		device_delete_child(dev, sc->smbus);
342	if (sc->irq_hand)
343		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
344	if (sc->irq_res)
345		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
346	if (sc->io_res)
347		bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid,
348		    sc->io_res);
349	mtx_destroy(&sc->lock);
350	return (0);
351}
352
353static void
354intsmb_rawintr(void *arg)
355{
356	struct intsmb_softc *sc = arg;
357
358	INTSMB_LOCK(sc);
359	intsmb_intr(sc);
360	intsmb_slvintr(sc);
361	INTSMB_UNLOCK(sc);
362}
363
364static int
365intsmb_callback(device_t dev, int index, void *data)
366{
367	int error = 0;
368
369	switch (index) {
370	case SMB_REQUEST_BUS:
371		break;
372	case SMB_RELEASE_BUS:
373		break;
374	default:
375		error = SMB_EINVAL;
376	}
377
378	return (error);
379}
380
381/* Counterpart of smbtx_smb_free(). */
382static int
383intsmb_free(struct intsmb_softc *sc)
384{
385
386	INTSMB_LOCK_ASSERT(sc);
387	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
388#ifdef ENABLE_ALART
389	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
390#endif
391	    sc->isbusy)
392		return (SMB_EBUSY);
393
394	sc->isbusy = 1;
395	/* Disable Interrupt in slave part. */
396#ifndef ENABLE_ALART
397	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
398#endif
399	/* Reset INTR Flag to prepare INTR. */
400	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
401	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
402	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
403	return (0);
404}
405
406static int
407intsmb_intr(struct intsmb_softc *sc)
408{
409	int status, tmp;
410
411	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
412	if (status & PIIX4_SMBHSTSTAT_BUSY)
413		return (1);
414
415	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
416	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
417
418		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
419		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
420		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
421		if (sc->isbusy) {
422			sc->isbusy = 0;
423			wakeup(sc);
424		}
425		return (0);
426	}
427	return (1); /* Not Completed */
428}
429
430static int
431intsmb_slvintr(struct intsmb_softc *sc)
432{
433	int status;
434
435	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
436	if (status & PIIX4_SMBSLVSTS_BUSY)
437		return (1);
438	if (status & PIIX4_SMBSLVSTS_ALART)
439		intsmb_alrintr(sc);
440	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
441		| PIIX4_SMBSLVSTS_SDW1)) {
442	}
443
444	/* Reset Status Register */
445	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
446	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
447	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
448	return (0);
449}
450
451static void
452intsmb_alrintr(struct intsmb_softc *sc)
453{
454	int slvcnt;
455#ifdef ENABLE_ALART
456	int error;
457	uint8_t addr;
458#endif
459
460	/* Stop generating INTR from ALART. */
461	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
462#ifdef ENABLE_ALART
463	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
464	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
465#endif
466	DELAY(5);
467
468	/* Ask bus who asserted it and then ask it what's the matter. */
469#ifdef ENABLE_ALART
470	error = intsmb_free(sc);
471	if (error)
472		return;
473
474	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
475	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
476	error = intsmb_stop_poll(sc);
477	if (error)
478		device_printf(sc->dev, "ALART: ERROR\n");
479	else {
480		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
481		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
482	}
483
484	/* Re-enable INTR from ALART. */
485	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
486	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
487	DELAY(5);
488#endif
489}
490
491static void
492intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
493{
494	unsigned char tmp;
495
496	INTSMB_LOCK_ASSERT(sc);
497	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
498	tmp &= 0xe0;
499	tmp |= cmd;
500	tmp |= PIIX4_SMBHSTCNT_START;
501
502	/* While not in autoconfiguration enable interrupts. */
503	if (!sc->poll && !cold && !nointr)
504		tmp |= PIIX4_SMBHSTCNT_INTREN;
505	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
506}
507
508static int
509intsmb_error(device_t dev, int status)
510{
511	int error = 0;
512
513	if (status & PIIX4_SMBHSTSTAT_ERR)
514		error |= SMB_EBUSERR;
515	if (status & PIIX4_SMBHSTSTAT_BUSC)
516		error |= SMB_ECOLLI;
517	if (status & PIIX4_SMBHSTSTAT_FAIL)
518		error |= SMB_ENOACK;
519
520	if (error != 0 && bootverbose)
521		device_printf(dev, "error = %d, status = %#x\n", error, status);
522
523	return (error);
524}
525
526/*
527 * Polling Code.
528 *
529 * Polling is not encouraged because it requires waiting for the
530 * device if it is busy.
531 * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
532 * polling code then.
533 */
534static int
535intsmb_stop_poll(struct intsmb_softc *sc)
536{
537	int error, i, status, tmp;
538
539	INTSMB_LOCK_ASSERT(sc);
540
541	/* First, wait for busy to be set. */
542	for (i = 0; i < 0x7fff; i++)
543		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
544		    PIIX4_SMBHSTSTAT_BUSY)
545			break;
546
547	/* Wait for busy to clear. */
548	for (i = 0; i < 0x7fff; i++) {
549		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
550		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
551			sc->isbusy = 0;
552			error = intsmb_error(sc->dev, status);
553			return (error);
554		}
555	}
556
557	/* Timed out waiting for busy to clear. */
558	sc->isbusy = 0;
559	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
560	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
561	return (SMB_ETIMEOUT);
562}
563
564/*
565 * Wait for completion and return result.
566 */
567static int
568intsmb_stop(struct intsmb_softc *sc)
569{
570	int error, status;
571
572	INTSMB_LOCK_ASSERT(sc);
573
574	if (sc->poll || cold)
575		/* So that it can use device during device probe on SMBus. */
576		return (intsmb_stop_poll(sc));
577
578	error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8);
579	if (error == 0) {
580		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
581		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
582			error = intsmb_error(sc->dev, status);
583			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
584				device_printf(sc->dev, "unknown cause why?\n");
585#ifdef ENABLE_ALART
586			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
587			    PIIX4_SMBSLVCNT_ALTEN);
588#endif
589			return (error);
590		}
591	}
592
593	/* Timeout Procedure. */
594	sc->isbusy = 0;
595
596	/* Re-enable supressed interrupt from slave part. */
597	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
598	if (error == EWOULDBLOCK)
599		return (SMB_ETIMEOUT);
600	else
601		return (SMB_EABORT);
602}
603
604static int
605intsmb_quick(device_t dev, u_char slave, int how)
606{
607	struct intsmb_softc *sc = device_get_softc(dev);
608	int error;
609	u_char data;
610
611	data = slave;
612
613	/* Quick command is part of Address, I think. */
614	switch(how) {
615	case SMB_QWRITE:
616		data &= ~LSB;
617		break;
618	case SMB_QREAD:
619		data |= LSB;
620		break;
621	default:
622		return (SMB_EINVAL);
623	}
624
625	INTSMB_LOCK(sc);
626	error = intsmb_free(sc);
627	if (error) {
628		INTSMB_UNLOCK(sc);
629		return (error);
630	}
631	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
632	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
633	error = intsmb_stop(sc);
634	INTSMB_UNLOCK(sc);
635	return (error);
636}
637
638static int
639intsmb_sendb(device_t dev, u_char slave, char byte)
640{
641	struct intsmb_softc *sc = device_get_softc(dev);
642	int error;
643
644	INTSMB_LOCK(sc);
645	error = intsmb_free(sc);
646	if (error) {
647		INTSMB_UNLOCK(sc);
648		return (error);
649	}
650	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
651	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
652	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
653	error = intsmb_stop(sc);
654	INTSMB_UNLOCK(sc);
655	return (error);
656}
657
658static int
659intsmb_recvb(device_t dev, u_char slave, char *byte)
660{
661	struct intsmb_softc *sc = device_get_softc(dev);
662	int error;
663
664	INTSMB_LOCK(sc);
665	error = intsmb_free(sc);
666	if (error) {
667		INTSMB_UNLOCK(sc);
668		return (error);
669	}
670	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
671	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
672	error = intsmb_stop(sc);
673	if (error == 0) {
674#ifdef RECV_IS_IN_CMD
675		/*
676		 * Linux SMBus stuff also troubles
677		 * Because Intel's datasheet does not make clear.
678		 */
679		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
680#else
681		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
682#endif
683	}
684	INTSMB_UNLOCK(sc);
685	return (error);
686}
687
688static int
689intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
690{
691	struct intsmb_softc *sc = device_get_softc(dev);
692	int error;
693
694	INTSMB_LOCK(sc);
695	error = intsmb_free(sc);
696	if (error) {
697		INTSMB_UNLOCK(sc);
698		return (error);
699	}
700	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
701	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
702	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
703	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
704	error = intsmb_stop(sc);
705	INTSMB_UNLOCK(sc);
706	return (error);
707}
708
709static int
710intsmb_writew(device_t dev, u_char slave, char cmd, short word)
711{
712	struct intsmb_softc *sc = device_get_softc(dev);
713	int error;
714
715	INTSMB_LOCK(sc);
716	error = intsmb_free(sc);
717	if (error) {
718		INTSMB_UNLOCK(sc);
719		return (error);
720	}
721	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
722	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
723	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
724	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
725	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
726	error = intsmb_stop(sc);
727	INTSMB_UNLOCK(sc);
728	return (error);
729}
730
731static int
732intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
733{
734	struct intsmb_softc *sc = device_get_softc(dev);
735	int error;
736
737	INTSMB_LOCK(sc);
738	error = intsmb_free(sc);
739	if (error) {
740		INTSMB_UNLOCK(sc);
741		return (error);
742	}
743	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
744	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
745	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
746	error = intsmb_stop(sc);
747	if (error == 0)
748		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
749	INTSMB_UNLOCK(sc);
750	return (error);
751}
752
753static int
754intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
755{
756	struct intsmb_softc *sc = device_get_softc(dev);
757	int error;
758
759	INTSMB_LOCK(sc);
760	error = intsmb_free(sc);
761	if (error) {
762		INTSMB_UNLOCK(sc);
763		return (error);
764	}
765	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
766	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
767	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
768	error = intsmb_stop(sc);
769	if (error == 0) {
770		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
771		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
772	}
773	INTSMB_UNLOCK(sc);
774	return (error);
775}
776
777/*
778 * Data sheet claims that it implements all function, but also claims
779 * that it implements 7 function and not mention PCALL. So I don't know
780 * whether it will work.
781 */
782static int
783intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
784{
785#ifdef PROCCALL_TEST
786	struct intsmb_softc *sc = device_get_softc(dev);
787	int error;
788
789	INTSMB_LOCK(sc);
790	error = intsmb_free(sc);
791	if (error) {
792		INTSMB_UNLOCK(sc);
793		return (error);
794	}
795	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
796	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
797	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
798	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
799	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
800	error = intsmb_stop(sc);
801	if (error == 0) {
802		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
803		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
804	}
805	INTSMB_UNLOCK(sc);
806	return (error);
807#else
808	return (SMB_ENOTSUPP);
809#endif
810}
811
812static int
813intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
814{
815	struct intsmb_softc *sc = device_get_softc(dev);
816	int error, i;
817
818	if (count > SMBBLOCKTRANS_MAX || count == 0)
819		return (SMB_EINVAL);
820
821	INTSMB_LOCK(sc);
822	error = intsmb_free(sc);
823	if (error) {
824		INTSMB_UNLOCK(sc);
825		return (error);
826	}
827
828	/* Reset internal array index. */
829	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
830
831	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
832	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
833	for (i = 0; i < count; i++)
834		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
835	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
836	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
837	error = intsmb_stop(sc);
838	INTSMB_UNLOCK(sc);
839	return (error);
840}
841
842static int
843intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
844{
845	struct intsmb_softc *sc = device_get_softc(dev);
846	int error, i;
847	u_char data, nread;
848
849	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
850		return (SMB_EINVAL);
851
852	INTSMB_LOCK(sc);
853	error = intsmb_free(sc);
854	if (error) {
855		INTSMB_UNLOCK(sc);
856		return (error);
857	}
858
859	/* Reset internal array index. */
860	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
861
862	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
863	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
864	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
865	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
866	error = intsmb_stop(sc);
867	if (error == 0) {
868		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
869		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
870			for (i = 0; i < nread; i++) {
871				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
872				if (i < *count)
873					buf[i] = data;
874			}
875			*count = nread;
876		} else
877			error = SMB_EBUSERR;
878	}
879	INTSMB_UNLOCK(sc);
880	return (error);
881}
882
883static devclass_t intsmb_devclass;
884
885static device_method_t intsmb_methods[] = {
886	/* Device interface */
887	DEVMETHOD(device_probe,		intsmb_probe),
888	DEVMETHOD(device_attach,	intsmb_attach),
889	DEVMETHOD(device_detach,	intsmb_detach),
890
891	/* SMBus interface */
892	DEVMETHOD(smbus_callback,	intsmb_callback),
893	DEVMETHOD(smbus_quick,		intsmb_quick),
894	DEVMETHOD(smbus_sendb,		intsmb_sendb),
895	DEVMETHOD(smbus_recvb,		intsmb_recvb),
896	DEVMETHOD(smbus_writeb,		intsmb_writeb),
897	DEVMETHOD(smbus_writew,		intsmb_writew),
898	DEVMETHOD(smbus_readb,		intsmb_readb),
899	DEVMETHOD(smbus_readw,		intsmb_readw),
900	DEVMETHOD(smbus_pcall,		intsmb_pcall),
901	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
902	DEVMETHOD(smbus_bread,		intsmb_bread),
903
904	DEVMETHOD_END
905};
906
907static driver_t intsmb_driver = {
908	"intsmb",
909	intsmb_methods,
910	sizeof(struct intsmb_softc),
911};
912
913DRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
914DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
915MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
916MODULE_VERSION(intsmb, 1);
917