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