viapm.c revision 151901
1/*-
2 * Copyright (c) 2001 Alcove - Nicolas Souchu
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/viapm.c 151901 2005-10-31 18:43:28Z jhb $");
29
30#include "opt_isa.h"
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/module.h>
36#include <sys/bus.h>
37#include <sys/uio.h>
38
39#include <machine/bus.h>
40#include <machine/clock.h>		/* for DELAY */
41#include <machine/resource.h>
42#include <sys/rman.h>
43
44#ifdef DEV_ISA
45#include <isa/isavar.h>
46#include <isa/isa_common.h>
47#endif
48#include <dev/pci/pcivar.h>
49#include <dev/pci/pcireg.h>
50
51#include <dev/iicbus/iiconf.h>
52#include <dev/iicbus/iicbus.h>
53
54#include <dev/smbus/smbconf.h>
55#include <dev/smbus/smbus.h>
56
57#include "iicbb_if.h"
58#include "smbus_if.h"
59
60#define VIAPM_DEBUG(x)	if (viapm_debug) (x)
61
62#ifdef DEBUG
63static int viapm_debug = 1;
64#else
65static int viapm_debug = 0;
66#endif
67
68#define VIA_586B_PMU_ID		0x30401106
69#define VIA_596A_PMU_ID		0x30501106
70#define VIA_596B_PMU_ID		0x30511106
71#define VIA_686A_PMU_ID		0x30571106
72#define VIA_8233_PMU_ID		0x30741106
73#define	VIA_8233A_PMU_ID	0x31471106
74#define	VIA_8235_PMU_ID		0x31771106
75
76#define VIAPM_INB(port) \
77	((u_char)bus_space_read_1(viapm->st, viapm->sh, port))
78#define VIAPM_OUTB(port,val) \
79	(bus_space_write_1(viapm->st, viapm->sh, port, (u_char)(val)))
80
81#define VIAPM_TYP_UNKNOWN	0
82#define VIAPM_TYP_586B_3040E	1
83#define VIAPM_TYP_586B_3040F	2
84#define VIAPM_TYP_596B		3
85#define VIAPM_TYP_686A		4
86#define VIAPM_TYP_8233		5
87
88struct viapm_softc {
89	int type;
90	u_int32_t base;
91        bus_space_tag_t st;
92        bus_space_handle_t sh;
93	int iorid;
94	int irqrid;
95	struct resource *iores;
96	struct resource *irqres;
97	void *irqih;
98
99	device_t iicbb;
100	device_t smbus;
101};
102
103static devclass_t viapm_devclass;
104static devclass_t viapropm_devclass;
105
106/*
107 * VT82C586B definitions
108 */
109
110#define VIAPM_586B_REVID	0x08
111
112#define VIAPM_586B_3040E_BASE	0x20
113#define VIAPM_586B_3040E_ACTIV	0x4		/* 16 bits */
114
115#define VIAPM_586B_3040F_BASE	0x48
116#define VIAPM_586B_3040F_ACTIV	0x41		/* 8 bits */
117
118#define VIAPM_586B_OEM_REV_E	0x00
119#define VIAPM_586B_OEM_REV_F	0x01
120#define VIAPM_586B_PROD_REV_A	0x10
121
122#define VIAPM_586B_BA_MASK	0x0000ff00
123
124#define GPIO_DIR	0x40
125#define GPIO_VAL	0x42
126#define EXTSMI_VAL	0x44
127
128#define VIAPM_SCL	0x02			/* GPIO1_VAL */
129#define VIAPM_SDA	0x04			/* GPIO2_VAL */
130
131/*
132 * VIAPRO common definitions
133 */
134
135#define VIAPM_PRO_BA_MASK	0x0000fff0
136#define VIAPM_PRO_SMBCTRL	0xd2
137#define VIAPM_PRO_REVID		0xd6
138
139/*
140 * VT82C686A definitions
141 */
142
143#define VIAPM_PRO_BASE		0x90
144
145#define SMBHST			0x0
146#define SMBHSL			0x1
147#define SMBHCTRL		0x2
148#define SMBHCMD			0x3
149#define SMBHADDR		0x4
150#define SMBHDATA0		0x5
151#define SMBHDATA1		0x6
152#define SMBHBLOCK		0x7
153
154#define SMBSST			0x1
155#define SMBSCTRL		0x8
156#define SMBSSDWCMD		0x9
157#define SMBSEVENT		0xa
158#define SMBSDATA		0xc
159
160#define SMBHST_RESERVED		0xef	/* reserved bits */
161#define SMBHST_FAILED		0x10	/* failed bus transaction */
162#define SMBHST_COLLID		0x08	/* bus collision */
163#define SMBHST_ERROR		0x04	/* device error */
164#define SMBHST_INTR		0x02	/* command completed */
165#define SMBHST_BUSY		0x01	/* host busy */
166
167#define SMBHCTRL_START		0x40	/* start command */
168#define SMBHCTRL_PROTO		0x1c	/* command protocol mask */
169#define SMBHCTRL_QUICK		0x00
170#define SMBHCTRL_SENDRECV	0x04
171#define SMBHCTRL_BYTE		0x08
172#define SMBHCTRL_WORD		0x0c
173#define SMBHCTRL_BLOCK		0x14
174#define SMBHCTRL_KILL		0x02	/* stop the current transaction */
175#define SMBHCTRL_ENABLE		0x01	/* enable interrupts */
176
177#define SMBSCTRL_ENABLE		0x01	/* enable slave */
178
179
180/*
181 * VIA8233 definitions
182 */
183
184#define VIAPM_8233_BASE		0xD0
185
186static int
187viapm_586b_probe(device_t dev)
188{
189	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
190	u_int32_t l;
191	u_int16_t s;
192	u_int8_t c;
193
194	switch (pci_get_devid(dev)) {
195	case VIA_586B_PMU_ID:
196
197		bzero(viapm, sizeof(struct viapm_softc));
198
199		l = pci_read_config(dev, VIAPM_586B_REVID, 1);
200		switch (l) {
201		case VIAPM_586B_OEM_REV_E:
202			viapm->type = VIAPM_TYP_586B_3040E;
203			viapm->iorid = VIAPM_586B_3040E_BASE;
204
205			/* Activate IO block access */
206			s = pci_read_config(dev, VIAPM_586B_3040E_ACTIV, 2);
207			pci_write_config(dev, VIAPM_586B_3040E_ACTIV, s | 0x1, 2);
208			break;
209
210		case VIAPM_586B_OEM_REV_F:
211		case VIAPM_586B_PROD_REV_A:
212		default:
213			viapm->type = VIAPM_TYP_586B_3040F;
214			viapm->iorid = VIAPM_586B_3040F_BASE;
215
216			/* Activate IO block access */
217			c = pci_read_config(dev, VIAPM_586B_3040F_ACTIV, 1);
218			pci_write_config(dev, VIAPM_586B_3040F_ACTIV, c | 0x80, 1);
219			break;
220		}
221
222		viapm->base = pci_read_config(dev, viapm->iorid, 4) &
223				VIAPM_586B_BA_MASK;
224
225		/*
226		 * We have to set the I/O resources by hand because it is
227		 * described outside the viapmope of the traditional maps
228		 */
229		if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
230							viapm->base, 256)) {
231			device_printf(dev, "could not set bus resource\n");
232			return ENXIO;
233		}
234		device_set_desc(dev, "VIA VT82C586B Power Management Unit");
235		return (BUS_PROBE_DEFAULT);
236
237	default:
238		break;
239	}
240
241	return ENXIO;
242}
243
244
245static int
246viapm_pro_probe(device_t dev)
247{
248	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
249#ifdef VIAPM_BASE_ADDR
250	u_int32_t l;
251#endif
252	u_int32_t base_cfgreg;
253	char *desc;
254
255	switch (pci_get_devid(dev)) {
256	case VIA_596A_PMU_ID:
257		desc = "VIA VT82C596A Power Management Unit";
258		viapm->type = VIAPM_TYP_596B;
259		base_cfgreg = VIAPM_PRO_BASE;
260		goto viapro;
261
262	case VIA_596B_PMU_ID:
263		desc = "VIA VT82C596B Power Management Unit";
264		viapm->type = VIAPM_TYP_596B;
265		base_cfgreg = VIAPM_PRO_BASE;
266		goto viapro;
267
268	case VIA_686A_PMU_ID:
269		desc = "VIA VT82C686A Power Management Unit";
270		viapm->type = VIAPM_TYP_686A;
271		base_cfgreg = VIAPM_PRO_BASE;
272		goto viapro;
273
274	case VIA_8233_PMU_ID:
275	case VIA_8233A_PMU_ID:
276		desc = "VIA VT8233 Power Management Unit";
277		viapm->type = VIAPM_TYP_UNKNOWN;
278		base_cfgreg = VIAPM_8233_BASE;
279		goto viapro;
280
281	case VIA_8235_PMU_ID:
282		desc = "VIA VT8235 Power Management Unit";
283		viapm->type = VIAPM_TYP_UNKNOWN;
284		base_cfgreg = VIAPM_8233_BASE;
285		goto viapro;
286
287	viapro:
288
289#ifdef VIAPM_BASE_ADDR
290		/* force VIAPM I/O base address */
291
292		/* enable the SMBus controller function */
293		l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
294		pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
295
296		/* write the base address */
297		pci_write_config(dev, base_cfgreg,
298				 VIAPM_BASE_ADDR & VIAPM_PRO_BA_MASK, 4);
299#endif
300
301		viapm->base = pci_read_config(dev, base_cfgreg, 4) & VIAPM_PRO_BA_MASK;
302
303		/*
304		 * We have to set the I/O resources by hand because it is
305		 * described outside the viapmope of the traditional maps
306		 */
307		viapm->iorid = base_cfgreg;
308		if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
309				     viapm->base, 16)) {
310			device_printf(dev, "could not set bus resource 0x%x\n",
311					viapm->base);
312			return ENXIO;
313		}
314
315		if (1 || bootverbose) {
316			device_printf(dev, "SMBus I/O base at 0x%x\n", viapm->base);
317		}
318
319		device_set_desc(dev, desc);
320		return (BUS_PROBE_DEFAULT);
321
322	default:
323		break;
324	}
325
326	return ENXIO;
327}
328
329static int
330viapm_pro_attach(device_t dev)
331{
332	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
333	u_int32_t l;
334
335	if (!(viapm->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
336		&viapm->iorid, RF_ACTIVE))) {
337		device_printf(dev, "could not allocate bus space\n");
338		goto error;
339	}
340	viapm->st = rman_get_bustag(viapm->iores);
341	viapm->sh = rman_get_bushandle(viapm->iores);
342
343#if notyet
344	/* force irq 9 */
345	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
346	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 0x80, 1);
347
348	viapm->irqrid = 0;
349	if (!(viapm->irqres = bus_alloc_resource(dev, SYS_RES_IRQ,
350				&viapm->irqrid, 9, 9, 1,
351				RF_SHAREABLE | RF_ACTIVE))) {
352		device_printf(dev, "could not allocate irq\n");
353		goto error;
354	}
355
356	if (bus_setup_intr(dev, viapm->irqres, INTR_TYPE_MISC,
357			(driver_intr_t *) viasmb_intr, viapm, &viapm->irqih)) {
358		device_printf(dev, "could not setup irq\n");
359		goto error;
360	}
361#endif
362
363	if (1 | bootverbose) {
364		l = pci_read_config(dev, VIAPM_PRO_REVID, 1);
365		device_printf(dev, "SMBus revision code 0x%x\n", l);
366	}
367
368	viapm->smbus = device_add_child(dev, "smbus", -1);
369
370	/* probe and attach the smbus */
371	bus_generic_attach(dev);
372
373	/* disable slave function */
374	VIAPM_OUTB(SMBSCTRL, VIAPM_INB(SMBSCTRL) & ~SMBSCTRL_ENABLE);
375
376	/* enable the SMBus controller function */
377	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
378	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
379
380#if notyet
381	/* enable interrupts */
382	VIAPM_OUTB(SMBHCTRL, VIAPM_INB(SMBHCTRL) | SMBHCTRL_ENABLE);
383#endif
384
385#ifdef DEV_ISA
386	/* If this device is a PCI-ISA bridge, then attach an ISA bus. */
387	if ((pci_get_class(dev) == PCIC_BRIDGE) &&
388	    (pci_get_subclass(dev) == PCIS_BRIDGE_ISA))
389		isab_attach(dev);
390#endif
391	return 0;
392
393error:
394	if (viapm->iores)
395		bus_release_resource(dev, SYS_RES_IOPORT, viapm->iorid, viapm->iores);
396#if notyet
397	if (viapm->irqres)
398		bus_release_resource(dev, SYS_RES_IRQ, viapm->irqrid, viapm->irqres);
399#endif
400
401	return ENXIO;
402}
403
404static int
405viapm_586b_attach(device_t dev)
406{
407	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
408
409	if (!(viapm->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
410		&viapm->iorid, RF_ACTIVE | RF_SHAREABLE))) {
411		device_printf(dev, "could not allocate bus resource\n");
412		return ENXIO;
413	}
414	viapm->st = rman_get_bustag(viapm->iores);
415	viapm->sh = rman_get_bushandle(viapm->iores);
416
417	VIAPM_OUTB(GPIO_DIR, VIAPM_INB(GPIO_DIR) | VIAPM_SCL | VIAPM_SDA);
418
419	/* add generic bit-banging code */
420	if (!(viapm->iicbb = device_add_child(dev, "iicbb", -1)))
421		goto error;
422
423	bus_generic_attach(dev);
424
425	return 0;
426
427error:
428	if (viapm->iores)
429		bus_release_resource(dev, SYS_RES_IOPORT,
430					viapm->iorid, viapm->iores);
431	return ENXIO;
432}
433
434static int
435viapm_586b_detach(device_t dev)
436{
437	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
438	int error;
439
440	bus_generic_detach(dev);
441	if (viapm->iicbb) {
442		device_delete_child(dev, viapm->iicbb);
443	}
444
445	if (viapm->iores && (error = bus_release_resource(dev, SYS_RES_IOPORT,
446						viapm->iorid, viapm->iores)))
447		return (error);
448
449	return 0;
450}
451
452static int
453viapm_pro_detach(device_t dev)
454{
455	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
456	int error;
457
458	bus_generic_detach(dev);
459	if (viapm->smbus) {
460		device_delete_child(dev, viapm->smbus);
461	}
462
463	if ((error = bus_release_resource(dev, SYS_RES_IOPORT,
464				viapm->iorid, viapm->iores)))
465		return (error);
466
467#if notyet
468	if ((error = bus_release_resource(dev, SYS_RES_IRQ,
469					viapm->irqrid, viapm->irqres))
470		return (error);
471#endif
472
473	return 0;
474}
475
476static int
477viabb_callback(device_t dev, int index, caddr_t *data)
478{
479	return 0;
480}
481
482static void
483viabb_setscl(device_t dev, int ctrl)
484{
485	struct viapm_softc *viapm = device_get_softc(dev);
486	u_char val;
487
488	val = VIAPM_INB(GPIO_VAL);
489
490	if (ctrl)
491		val |= VIAPM_SCL;
492	else
493		val &= ~VIAPM_SCL;
494
495	VIAPM_OUTB(GPIO_VAL, val);
496
497	return;
498}
499
500static void
501viabb_setsda(device_t dev, int data)
502{
503	struct viapm_softc *viapm = device_get_softc(dev);
504	u_char val;
505
506	val = VIAPM_INB(GPIO_VAL);
507
508	if (data)
509		val |= VIAPM_SDA;
510	else
511		val &= ~VIAPM_SDA;
512
513	VIAPM_OUTB(GPIO_VAL, val);
514
515	return;
516}
517
518static int
519viabb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
520{
521	/* reset bus */
522	viabb_setsda(dev, 1);
523	viabb_setscl(dev, 1);
524
525	return (IIC_ENOADDR);
526}
527
528static int
529viabb_getscl(device_t dev)
530{
531	struct viapm_softc *viapm = device_get_softc(dev);
532
533	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SCL) != 0);
534}
535
536static int
537viabb_getsda(device_t dev)
538{
539	struct viapm_softc *viapm = device_get_softc(dev);
540
541	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SDA) != 0);
542}
543
544static int
545viapm_abort(struct viapm_softc *viapm)
546{
547	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_KILL);
548	DELAY(10);
549
550	return (0);
551}
552
553static int
554viapm_clear(struct viapm_softc *viapm)
555{
556	VIAPM_OUTB(SMBHST, SMBHST_FAILED | SMBHST_COLLID |
557		SMBHST_ERROR | SMBHST_INTR);
558	DELAY(10);
559
560	return (0);
561}
562
563static int
564viapm_busy(struct viapm_softc *viapm)
565{
566	u_char sts;
567
568	sts = VIAPM_INB(SMBHST);
569
570	VIAPM_DEBUG(printf("viapm: idle? STS=0x%x\n", sts));
571
572	return (sts & SMBHST_BUSY);
573}
574
575/*
576 * Poll the SMBus controller
577 */
578static int
579viapm_wait(struct viapm_softc *viapm)
580{
581	int count = 10000;
582	u_char sts = 0;
583	int error;
584
585	/* wait for command to complete and SMBus controller is idle */
586	while(count--) {
587		DELAY(10);
588		sts = VIAPM_INB(SMBHST);
589
590		/* check if the controller is processing a command */
591		if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR))
592			break;
593	}
594
595	VIAPM_DEBUG(printf("viapm: SMBHST=0x%x\n", sts));
596
597	error = SMB_ENOERR;
598
599	if (!count)
600		error |= SMB_ETIMEOUT;
601
602	if (sts & SMBHST_FAILED)
603		error |= SMB_EABORT;
604
605	if (sts & SMBHST_COLLID)
606		error |= SMB_ENOACK;
607
608	if (sts & SMBHST_ERROR)
609		error |= SMB_EBUSERR;
610
611	if (error != SMB_ENOERR)
612		viapm_abort(viapm);
613
614	viapm_clear(viapm);
615
616	return (error);
617}
618
619static int
620viasmb_callback(device_t dev, int index, caddr_t *data)
621{
622	int error = 0;
623
624	switch (index) {
625	case SMB_REQUEST_BUS:
626	case SMB_RELEASE_BUS:
627		/* ok, bus allocation accepted */
628		break;
629	default:
630		error = EINVAL;
631	}
632
633	return (error);
634}
635
636static int
637viasmb_quick(device_t dev, u_char slave, int how)
638{
639	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
640	int error;
641
642	viapm_clear(viapm);
643	if (viapm_busy(viapm))
644		return (EBUSY);
645
646	switch (how) {
647	case SMB_QWRITE:
648		VIAPM_DEBUG(printf("viapm: QWRITE to 0x%x", slave));
649		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
650		break;
651	case SMB_QREAD:
652		VIAPM_DEBUG(printf("viapm: QREAD to 0x%x", slave));
653		VIAPM_OUTB(SMBHADDR, slave | LSB);
654		break;
655	default:
656		panic("%s: unknown QUICK command (%x)!", __func__, how);
657	}
658
659	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_QUICK);
660
661	error = viapm_wait(viapm);
662
663	return (error);
664}
665
666static int
667viasmb_sendb(device_t dev, u_char slave, char byte)
668{
669	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
670	int error;
671
672	viapm_clear(viapm);
673	if (viapm_busy(viapm))
674		return (EBUSY);
675
676	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
677	VIAPM_OUTB(SMBHCMD, byte);
678
679	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
680
681	error = viapm_wait(viapm);
682
683	VIAPM_DEBUG(printf("viapm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
684
685	return (error);
686}
687
688static int
689viasmb_recvb(device_t dev, u_char slave, char *byte)
690{
691	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
692	int error;
693
694	viapm_clear(viapm);
695	if (viapm_busy(viapm))
696		return (EBUSY);
697
698	VIAPM_OUTB(SMBHADDR, slave | LSB);
699
700	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
701
702	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
703		*byte = VIAPM_INB(SMBHDATA0);
704
705	VIAPM_DEBUG(printf("viapm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
706
707	return (error);
708}
709
710static int
711viasmb_writeb(device_t dev, u_char slave, char cmd, char byte)
712{
713	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
714	int error;
715
716	viapm_clear(viapm);
717	if (viapm_busy(viapm))
718		return (EBUSY);
719
720	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
721	VIAPM_OUTB(SMBHCMD, cmd);
722	VIAPM_OUTB(SMBHDATA0, byte);
723
724	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
725
726	error = viapm_wait(viapm);
727
728	VIAPM_DEBUG(printf("viapm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
729
730	return (error);
731}
732
733static int
734viasmb_readb(device_t dev, u_char slave, char cmd, char *byte)
735{
736	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
737	int error;
738
739	viapm_clear(viapm);
740	if (viapm_busy(viapm))
741		return (EBUSY);
742
743	VIAPM_OUTB(SMBHADDR, slave | LSB);
744	VIAPM_OUTB(SMBHCMD, cmd);
745
746	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
747
748	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
749		*byte = VIAPM_INB(SMBHDATA0);
750
751	VIAPM_DEBUG(printf("viapm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
752
753	return (error);
754}
755
756static int
757viasmb_writew(device_t dev, u_char slave, char cmd, short word)
758{
759	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
760	int error;
761
762	viapm_clear(viapm);
763	if (viapm_busy(viapm))
764		return (EBUSY);
765
766	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
767	VIAPM_OUTB(SMBHCMD, cmd);
768	VIAPM_OUTB(SMBHDATA0, word & 0x00ff);
769	VIAPM_OUTB(SMBHDATA1, (word & 0xff00) >> 8);
770
771	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
772
773	error = viapm_wait(viapm);
774
775	VIAPM_DEBUG(printf("viapm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
776
777	return (error);
778}
779
780static int
781viasmb_readw(device_t dev, u_char slave, char cmd, short *word)
782{
783	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
784	int error;
785	u_char high, low;
786
787	viapm_clear(viapm);
788	if (viapm_busy(viapm))
789		return (EBUSY);
790
791	VIAPM_OUTB(SMBHADDR, slave | LSB);
792	VIAPM_OUTB(SMBHCMD, cmd);
793
794	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
795
796	if ((error = viapm_wait(viapm)) == SMB_ENOERR) {
797		low = VIAPM_INB(SMBHDATA0);
798		high = VIAPM_INB(SMBHDATA1);
799
800		*word = ((high & 0xff) << 8) | (low & 0xff);
801	}
802
803	VIAPM_DEBUG(printf("viapm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
804
805	return (error);
806}
807
808static int
809viasmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
810{
811	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
812	u_char remain, len, i;
813	int error = SMB_ENOERR;
814
815	viapm_clear(viapm);
816	if (viapm_busy(viapm))
817		return (EBUSY);
818
819	remain = count;
820	while (remain) {
821		len = min(remain, 32);
822
823		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
824		VIAPM_OUTB(SMBHCMD, cmd);
825		VIAPM_OUTB(SMBHDATA0, len);
826		i = VIAPM_INB(SMBHCTRL);
827
828		/* fill the 32-byte internal buffer */
829		for (i=0; i<len; i++) {
830			VIAPM_OUTB(SMBHBLOCK, buf[count-remain+i]);
831			DELAY(2);
832		}
833		VIAPM_OUTB(SMBHCMD, cmd);
834		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
835
836		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
837			goto error;
838
839		remain -= len;
840	}
841
842error:
843	VIAPM_DEBUG(printf("viapm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
844
845	return (error);
846
847}
848
849static int
850viasmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
851{
852	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
853	u_char remain, len, i;
854	int error = SMB_ENOERR;
855
856	viapm_clear(viapm);
857	if (viapm_busy(viapm))
858		return (EBUSY);
859
860	remain = count;
861	while (remain) {
862		VIAPM_OUTB(SMBHADDR, slave | LSB);
863		VIAPM_OUTB(SMBHCMD, cmd);
864		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
865
866		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
867			goto error;
868
869		len = VIAPM_INB(SMBHDATA0);
870		i = VIAPM_INB(SMBHCTRL); 		/* reset counter */
871
872		len = min(len, remain);
873
874		/* read the 32-byte internal buffer */
875		for (i=0; i<len; i++) {
876			buf[count-remain+i] = VIAPM_INB(SMBHBLOCK);
877			DELAY(2);
878		}
879
880		remain -= len;
881	}
882error:
883	VIAPM_DEBUG(printf("viapm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
884
885	return (error);
886}
887
888static device_method_t viapm_methods[] = {
889	/* device interface */
890	DEVMETHOD(device_probe,		viapm_586b_probe),
891	DEVMETHOD(device_attach,	viapm_586b_attach),
892	DEVMETHOD(device_detach,	viapm_586b_detach),
893
894	/* iicbb interface */
895	DEVMETHOD(iicbb_callback,	viabb_callback),
896	DEVMETHOD(iicbb_setscl,		viabb_setscl),
897	DEVMETHOD(iicbb_setsda,		viabb_setsda),
898	DEVMETHOD(iicbb_getscl,		viabb_getscl),
899	DEVMETHOD(iicbb_getsda,		viabb_getsda),
900	DEVMETHOD(iicbb_reset,		viabb_reset),
901
902	/* Bus interface */
903	DEVMETHOD(bus_print_child,	bus_generic_print_child),
904	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
905	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
906	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
907	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
908	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
909	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
910
911	{ 0, 0 }
912};
913
914static driver_t viapm_driver = {
915	"viapm",
916	viapm_methods,
917	sizeof(struct viapm_softc),
918};
919
920static device_method_t viapropm_methods[] = {
921	/* device interface */
922	DEVMETHOD(device_probe,		viapm_pro_probe),
923	DEVMETHOD(device_attach,	viapm_pro_attach),
924	DEVMETHOD(device_detach,	viapm_pro_detach),
925
926	/* smbus interface */
927	DEVMETHOD(smbus_callback,	viasmb_callback),
928	DEVMETHOD(smbus_quick,		viasmb_quick),
929	DEVMETHOD(smbus_sendb,		viasmb_sendb),
930	DEVMETHOD(smbus_recvb,		viasmb_recvb),
931	DEVMETHOD(smbus_writeb,		viasmb_writeb),
932	DEVMETHOD(smbus_readb,		viasmb_readb),
933	DEVMETHOD(smbus_writew,		viasmb_writew),
934	DEVMETHOD(smbus_readw,		viasmb_readw),
935	DEVMETHOD(smbus_bwrite,		viasmb_bwrite),
936	DEVMETHOD(smbus_bread,		viasmb_bread),
937
938	/* Bus interface */
939	DEVMETHOD(bus_print_child,	bus_generic_print_child),
940	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
941	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
942	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
943	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
944	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
945	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
946
947	{ 0, 0 }
948};
949
950static driver_t viapropm_driver = {
951	"viapropm",
952	viapropm_methods,
953	sizeof(struct viapm_softc),
954};
955
956DRIVER_MODULE(viapm, pci, viapm_driver, viapm_devclass, 0, 0);
957DRIVER_MODULE(viapropm, pci, viapropm_driver, viapropm_devclass, 0, 0);
958
959MODULE_DEPEND(viapm, pci, 1, 1, 1);
960MODULE_DEPEND(viapropm, pci, 1, 1, 1);
961MODULE_DEPEND(viapm, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
962MODULE_DEPEND(viapropm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
963MODULE_VERSION(viapm, 1);
964
965#ifdef DEV_ISA
966DRIVER_MODULE(isa, viapm, isa_driver, isa_devclass, 0, 0);
967DRIVER_MODULE(isa, viapropm, isa_driver, isa_devclass, 0, 0);
968MODULE_DEPEND(viapm, isa, 1, 1, 1);
969MODULE_DEPEND(viapropm, isa, 1, 1, 1);
970#endif
971