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