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