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