intpm.c revision 166901
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: head/sys/pci/intpm.c 166901 2007-02-23 12:19:07Z piso $");
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			isbusy;
56	struct mtx		lock;
57};
58
59#define	INTSMB_LOCK(sc)		mtx_lock(&(sc)->lock)
60#define	INTSMB_UNLOCK(sc)	mtx_unlock(&(sc)->lock)
61#define	INTSMB_LOCK_ASSERT(sc)	mtx_assert(&(sc)->lock, MA_OWNED)
62
63static int intsmb_probe(device_t);
64static int intsmb_attach(device_t);
65static int intsmb_detach(device_t);
66static int intsmb_intr(struct intsmb_softc *sc);
67static int intsmb_slvintr(struct intsmb_softc *sc);
68static void intsmb_alrintr(struct intsmb_softc *sc);
69static int intsmb_callback(device_t dev, int index, void *data);
70static int intsmb_quick(device_t dev, u_char slave, int how);
71static int intsmb_sendb(device_t dev, u_char slave, char byte);
72static int intsmb_recvb(device_t dev, u_char slave, char *byte);
73static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
74static int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
75static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
76static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
77static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
78static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
79static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
80static void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr);
81static int intsmb_stop(struct intsmb_softc *sc);
82static int intsmb_stop_poll(struct intsmb_softc *sc);
83static int intsmb_free(struct intsmb_softc *sc);
84static void intsmb_rawintr(void *arg);
85
86static int
87intsmb_probe(device_t dev)
88{
89
90	switch (pci_get_devid(dev)) {
91	case 0x71138086:	/* Intel 82371AB */
92	case 0x719b8086:	/* Intel 82443MX */
93#if 0
94	/* Not a good idea yet, this stops isab0 functioning */
95	case 0x02001166:	/* ServerWorks OSB4 */
96#endif
97		device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
98		break;
99	default:
100		return (ENXIO);
101	}
102
103	return (BUS_PROBE_DEFAULT);
104}
105
106static int
107intsmb_attach(device_t dev)
108{
109	struct intsmb_softc *sc = device_get_softc(dev);
110	int error, rid, value;
111	char *str;
112
113	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
114
115	rid = PCI_BASE_ADDR_SMB;
116	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
117	    RF_ACTIVE);
118	if (sc->io_res == NULL) {
119		device_printf(dev, "Could not allocate I/O space\n");
120		error = ENXIO;
121		goto fail;
122	}
123
124#ifndef NO_CHANGE_PCICONF
125	pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
126	pci_write_config(dev, PCI_HST_CFG_SMB,
127	    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
128#endif
129	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
130	switch (value & 0xe) {
131	case PCI_INTR_SMB_SMI:
132		str = "SMI";
133		break;
134	case PCI_INTR_SMB_IRQ9:
135		str = "IRQ 9";
136		break;
137	default:
138		str = "BOGUS";
139	}
140	device_printf(dev, "intr %s %s ", str,
141	    (value & 1) ? "enabled" : "disabled");
142	value = pci_read_config(dev, PCI_REVID_SMB, 1);
143	printf("revision %d\n", value);
144
145	if ((value & 0xe) != PCI_INTR_SMB_IRQ9) {
146		device_printf(dev, "Unsupported interrupt mode\n");
147		error = ENXIO;
148		goto fail;
149	}
150
151	/* Force IRQ 9. */
152	rid = 0;
153	bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
154	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
155	    RF_SHAREABLE | RF_ACTIVE);
156	if (sc->irq_res == NULL) {
157		device_printf(dev, "Could not allocate irq\n");
158		error = ENXIO;
159		goto fail;
160	}
161
162	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, NULL,
163	    intsmb_rawintr, sc, &sc->irq_hand);
164	if (error) {
165		device_printf(dev, "Failed to map intr\n");
166		goto fail;
167	}
168
169	value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4);
170	device_printf(dev, "PM %s %x\n", (value & 1) ? "I/O mapped" : "Memory",
171	    value & 0xfffe);
172
173	sc->isbusy = 0;
174	sc->smbus = device_add_child(dev, "smbus", -1);
175	if (sc->smbus == NULL) {
176		error = ENXIO;
177		goto fail;
178	}
179	error = device_probe_and_attach(sc->smbus);
180	if (error)
181		goto fail;
182
183#ifdef ENABLE_ALART
184	/* Enable Arart */
185	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
186#endif
187	return (0);
188
189fail:
190	intsmb_detach(dev);
191	return (error);
192}
193
194static int
195intsmb_detach(device_t dev)
196{
197	struct intsmb_softc *sc = device_get_softc(dev);
198	int error;
199
200	error = bus_generic_detach(dev);
201	if (error)
202		return (error);
203
204	if (sc->smbus)
205		device_delete_child(dev, sc->smbus);
206	if (sc->irq_hand)
207		bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
208	if (sc->irq_res)
209		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
210	if (sc->io_res)
211		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
212		    sc->io_res);
213	mtx_destroy(&sc->lock);
214	return (0);
215}
216
217static void
218intsmb_rawintr(void *arg)
219{
220	struct intsmb_softc *sc = arg;
221
222	INTSMB_LOCK(sc);
223	intsmb_intr(sc);
224	intsmb_slvintr(sc);
225	INTSMB_UNLOCK(sc);
226}
227
228static int
229intsmb_callback(device_t dev, int index, void *data)
230{
231	int error = 0;
232
233	switch (index) {
234	case SMB_REQUEST_BUS:
235		break;
236	case SMB_RELEASE_BUS:
237		break;
238	default:
239		error = EINVAL;
240	}
241
242	return (error);
243}
244
245/* Counterpart of smbtx_smb_free(). */
246static int
247intsmb_free(struct intsmb_softc *sc)
248{
249
250	INTSMB_LOCK_ASSERT(sc);
251	if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
252#ifdef ENABLE_ALART
253	    (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
254#endif
255	    sc->isbusy)
256		return (SMB_EBUSY);
257
258	sc->isbusy = 1;
259	/* Disable Interrupt in slave part. */
260#ifndef ENABLE_ALART
261	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
262#endif
263	/* Reset INTR Flag to prepare INTR. */
264	bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
265	    PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
266	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
267	return (0);
268}
269
270static int
271intsmb_intr(struct intsmb_softc *sc)
272{
273	int status, tmp;
274
275	status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
276	if (status & PIIX4_SMBHSTSTAT_BUSY)
277		return (1);
278
279	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
280	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
281
282		tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
283		bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
284		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
285		if (sc->isbusy) {
286			sc->isbusy = 0;
287			wakeup(sc);
288		}
289		return (0);
290	}
291	return (1); /* Not Completed */
292}
293
294static int
295intsmb_slvintr(struct intsmb_softc *sc)
296{
297	int status;
298
299	status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
300	if (status & PIIX4_SMBSLVSTS_BUSY)
301		return (1);
302	if (status & PIIX4_SMBSLVSTS_ALART)
303		intsmb_alrintr(sc);
304	else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
305		| PIIX4_SMBSLVSTS_SDW1)) {
306	}
307
308	/* Reset Status Register */
309	bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
310	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
311	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
312	return (0);
313}
314
315static void
316intsmb_alrintr(struct intsmb_softc *sc)
317{
318	int slvcnt;
319#ifdef ENABLE_ALART
320	int error;
321	uint8_t addr;
322#endif
323
324	/* Stop generating INTR from ALART. */
325	slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
326#ifdef ENABLE_ALART
327	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
328	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
329#endif
330	DELAY(5);
331
332	/* Ask bus who asserted it and then ask it what's the matter. */
333#ifdef ENABLE_ALART
334	error = intsmb_free(sc);
335	if (error)
336		return;
337
338	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
339	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
340	error = intsmb_stop_poll(sc);
341	if (error)
342		device_printf(sc->dev, "ALART: ERROR\n");
343	else {
344		addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
345		device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
346	}
347
348	/* Re-enable INTR from ALART. */
349	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
350	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
351	DELAY(5);
352#endif
353}
354
355static void
356intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
357{
358	unsigned char tmp;
359
360	INTSMB_LOCK_ASSERT(sc);
361	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
362	tmp &= 0xe0;
363	tmp |= cmd;
364	tmp |= PIIX4_SMBHSTCNT_START;
365
366	/* While not in autoconfiguration enable interrupts. */
367	if (!cold || !nointr)
368		tmp |= PIIX4_SMBHSTCNT_INTREN;
369	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
370}
371
372static int
373intsmb_error(int status)
374{
375	int error = 0;
376
377	if (status & PIIX4_SMBHSTSTAT_ERR)
378		error |= SMB_EBUSERR;
379	if (status & PIIX4_SMBHSTSTAT_BUSC)
380		error |= SMB_ECOLLI;
381	if (status & PIIX4_SMBHSTSTAT_FAIL)
382		error |= SMB_ENOACK;
383	return (error);
384}
385
386/*
387 * Polling Code.
388 *
389 * Polling is not encouraged because it requires waiting for the
390 * device if it is busy.
391 * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
392 * polling code then.
393 */
394static int
395intsmb_stop_poll(struct intsmb_softc *sc)
396{
397	int error, i, status, tmp;
398
399	INTSMB_LOCK_ASSERT(sc);
400
401	/* First, wait for busy to be set. */
402	for (i = 0; i < 0x7fff; i++)
403		if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
404		    PIIX4_SMBHSTSTAT_BUSY)
405			break;
406
407	/* Wait for busy to clear. */
408	for (i = 0; i < 0x7fff; i++) {
409		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
410		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
411			sc->isbusy = 0;
412			error = intsmb_error(status);
413			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
414				device_printf(sc->dev, "unknown cause why?");
415			return (error);
416		}
417	}
418
419	/* Timed out waiting for busy to clear. */
420	sc->isbusy = 0;
421	tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
422	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
423	return (SMB_ETIMEOUT);
424}
425
426/*
427 * Wait for completion and return result.
428 */
429static int
430intsmb_stop(struct intsmb_softc *sc)
431{
432	int error, status;
433
434	INTSMB_LOCK_ASSERT(sc);
435
436	if (cold)
437		/* So that it can use device during device probe on SMBus. */
438		return (intsmb_stop_poll(sc));
439
440	error = tsleep(sc, PWAIT | PCATCH, "SMBWAI", hz / 8);
441	if (error == 0) {
442		status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
443		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
444			error = intsmb_error(status);
445			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
446				device_printf(sc->dev, "unknown cause why?\n");
447#ifdef ENABLE_ALART
448			bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
449			    PIIX4_SMBSLVCNT_ALTEN);
450#endif
451			return (error);
452		}
453	}
454
455	/* Timeout Procedure. */
456	sc->isbusy = 0;
457
458	/* Re-enable supressed interrupt from slave part. */
459	bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
460	if (error == EWOULDBLOCK)
461		return (SMB_ETIMEOUT);
462	else
463		return (SMB_EABORT);
464}
465
466static int
467intsmb_quick(device_t dev, u_char slave, int how)
468{
469	struct intsmb_softc *sc = device_get_softc(dev);
470	int error;
471	u_char data;
472
473	data = slave;
474
475	/* Quick command is part of Address, I think. */
476	switch(how) {
477	case SMB_QWRITE:
478		data &= ~LSB;
479		break;
480	case SMB_QREAD:
481		data |= LSB;
482		break;
483	default:
484		return (EINVAL);
485	}
486
487	INTSMB_LOCK(sc);
488	error = intsmb_free(sc);
489	if (error) {
490		INTSMB_UNLOCK(sc);
491		return (error);
492	}
493	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
494	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
495	error = intsmb_stop(sc);
496	INTSMB_UNLOCK(sc);
497	return (error);
498}
499
500static int
501intsmb_sendb(device_t dev, u_char slave, char byte)
502{
503	struct intsmb_softc *sc = device_get_softc(dev);
504	int error;
505
506	INTSMB_LOCK(sc);
507	error = intsmb_free(sc);
508	if (error) {
509		INTSMB_UNLOCK(sc);
510		return (error);
511	}
512	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
513	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
514	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
515	error = intsmb_stop(sc);
516	INTSMB_UNLOCK(sc);
517	return (error);
518}
519
520static int
521intsmb_recvb(device_t dev, u_char slave, char *byte)
522{
523	struct intsmb_softc *sc = device_get_softc(dev);
524	int error;
525
526	INTSMB_LOCK(sc);
527	error = intsmb_free(sc);
528	if (error) {
529		INTSMB_UNLOCK(sc);
530		return (error);
531	}
532	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
533	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
534	error = intsmb_stop(sc);
535	if (error == 0) {
536#ifdef RECV_IS_IN_CMD
537		/*
538		 * Linux SMBus stuff also troubles
539		 * Because Intel's datasheet does not make clear.
540		 */
541		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
542#else
543		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
544#endif
545	}
546	INTSMB_UNLOCK(sc);
547	return (error);
548}
549
550static int
551intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
552{
553	struct intsmb_softc *sc = device_get_softc(dev);
554	int error;
555
556	INTSMB_LOCK(sc);
557	error = intsmb_free(sc);
558	if (error) {
559		INTSMB_UNLOCK(sc);
560		return (error);
561	}
562	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
563	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
564	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
565	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
566	error = intsmb_stop(sc);
567	INTSMB_UNLOCK(sc);
568	return (error);
569}
570
571static int
572intsmb_writew(device_t dev, u_char slave, char cmd, short word)
573{
574	struct intsmb_softc *sc = device_get_softc(dev);
575	int error;
576
577	INTSMB_LOCK(sc);
578	error = intsmb_free(sc);
579	if (error) {
580		INTSMB_UNLOCK(sc);
581		return (error);
582	}
583	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
584	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
585	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
586	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
587	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
588	error = intsmb_stop(sc);
589	INTSMB_UNLOCK(sc);
590	return (error);
591}
592
593static int
594intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
595{
596	struct intsmb_softc *sc = device_get_softc(dev);
597	int error;
598
599	INTSMB_LOCK(sc);
600	error = intsmb_free(sc);
601	if (error) {
602		INTSMB_UNLOCK(sc);
603		return (error);
604	}
605	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
606	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
607	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
608	error = intsmb_stop(sc);
609	if (error == 0)
610		*byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
611	INTSMB_UNLOCK(sc);
612	return (error);
613}
614
615static int
616intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
617{
618	struct intsmb_softc *sc = device_get_softc(dev);
619	int error;
620
621	INTSMB_LOCK(sc);
622	error = intsmb_free(sc);
623	if (error) {
624		INTSMB_UNLOCK(sc);
625		return (error);
626	}
627	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
628	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
629	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
630	error = intsmb_stop(sc);
631	if (error == 0) {
632		*word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
633		*word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
634	}
635	INTSMB_UNLOCK(sc);
636	return (error);
637}
638
639/*
640 * Data sheet claims that it implements all function, but also claims
641 * that it implements 7 function and not mention PCALL. So I don't know
642 * whether it will work.
643 */
644static int
645intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
646{
647#ifdef PROCCALL_TEST
648	struct intsmb_softc *sc = device_get_softc(dev);
649	int error;
650
651	INTSMB_LOCK(sc);
652	error = intsmb_free(sc);
653	if (error) {
654		INTSMB_UNLOCK(sc);
655		return (error);
656	}
657	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
658	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
659	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
660	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
661	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
662	error = intsmb_stop(sc);
663	if (error == 0) {
664		*rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
665		*rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
666	}
667	INTSMB_UNLOCK(sc);
668	return (error);
669#else
670	return (SMB_ENOTSUPP);
671#endif
672}
673
674static int
675intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
676{
677	struct intsmb_softc *sc = device_get_softc(dev);
678	int error, i;
679
680	if (count > SMBBLOCKTRANS_MAX || count == 0)
681		return (SMB_EINVAL);
682
683	INTSMB_LOCK(sc);
684	error = intsmb_free(sc);
685	if (error) {
686		INTSMB_UNLOCK(sc);
687		return (error);
688	}
689
690	/* Reset internal array index. */
691	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
692
693	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
694	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
695	for (i = 0; i < count; i++)
696		bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
697	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
698	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
699	error = intsmb_stop(sc);
700	INTSMB_UNLOCK(sc);
701	return (error);
702}
703
704static int
705intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
706{
707	struct intsmb_softc *sc = device_get_softc(dev);
708	int error, i;
709	u_char data, nread;
710
711	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
712		return (SMB_EINVAL);
713
714	INTSMB_LOCK(sc);
715	error = intsmb_free(sc);
716	if (error) {
717		INTSMB_UNLOCK(sc);
718		return (error);
719	}
720
721	/* Reset internal array index. */
722	bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
723
724	bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
725	bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
726	bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
727	intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
728	error = intsmb_stop(sc);
729	if (error == 0) {
730		nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
731		if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
732			for (i = 0; i < nread; i++) {
733				data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
734				if (i < *count)
735					buf[i] = data;
736			}
737			*count = nread;
738		} else
739			error = EIO;
740	}
741	INTSMB_UNLOCK(sc);
742	return (error);
743}
744
745static devclass_t intsmb_devclass;
746
747static device_method_t intsmb_methods[] = {
748	/* Device interface */
749	DEVMETHOD(device_probe,		intsmb_probe),
750	DEVMETHOD(device_attach,	intsmb_attach),
751	DEVMETHOD(device_detach,	intsmb_detach),
752
753	/* Bus interface */
754	DEVMETHOD(bus_print_child,	bus_generic_print_child),
755
756	/* SMBus interface */
757	DEVMETHOD(smbus_callback,	intsmb_callback),
758	DEVMETHOD(smbus_quick,		intsmb_quick),
759	DEVMETHOD(smbus_sendb,		intsmb_sendb),
760	DEVMETHOD(smbus_recvb,		intsmb_recvb),
761	DEVMETHOD(smbus_writeb,		intsmb_writeb),
762	DEVMETHOD(smbus_writew,		intsmb_writew),
763	DEVMETHOD(smbus_readb,		intsmb_readb),
764	DEVMETHOD(smbus_readw,		intsmb_readw),
765	DEVMETHOD(smbus_pcall,		intsmb_pcall),
766	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
767	DEVMETHOD(smbus_bread,		intsmb_bread),
768
769	{ 0, 0 }
770};
771
772static driver_t intsmb_driver = {
773	"intsmb",
774	intsmb_methods,
775	sizeof(struct intsmb_softc),
776};
777
778DRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
779DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
780MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
781MODULE_VERSION(intsmb, 1);
782