viapm.c revision 116192
155682Smarkm/*-
2233294Sstas * Copyright (c) 2001 Alcove - Nicolas Souchu
355682Smarkm * All rights reserved.
455682Smarkm *
5233294Sstas * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
9233294Sstas *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
12233294Sstas *    documentation and/or other materials provided with the distribution.
1355682Smarkm *
1455682Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1555682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2255682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455682Smarkm * SUCH DAMAGE.
2555682Smarkm */
2655682Smarkm
2755682Smarkm#include <sys/cdefs.h>
2855682Smarkm__FBSDID("$FreeBSD: head/sys/pci/viapm.c 116192 2003-06-11 06:34:30Z obrien $");
2955682Smarkm
3055682Smarkm#include <sys/param.h>
3155682Smarkm#include <sys/kernel.h>
3255682Smarkm#include <sys/systm.h>
3355682Smarkm#include <sys/module.h>
3455682Smarkm#include <sys/bus.h>
3555682Smarkm#include <sys/uio.h>
3655682Smarkm
3755682Smarkm#include <machine/bus_pio.h>
3855682Smarkm#include <machine/bus_memio.h>
3955682Smarkm#include <machine/bus.h>
40178825Sdfr#include <machine/clock.h>		/* for DELAY */
41178825Sdfr#include <machine/resource.h>
4255682Smarkm#include <sys/rman.h>
4355682Smarkm
4455682Smarkm#include <pci/pcivar.h>
4555682Smarkm#include <pci/pcireg.h>
4655682Smarkm
4755682Smarkm#include <dev/iicbus/iiconf.h>
4855682Smarkm#include <dev/iicbus/iicbus.h>
4955682Smarkm
5055682Smarkm#include <dev/smbus/smbconf.h>
5155682Smarkm#include <dev/smbus/smbus.h>
5255682Smarkm
5355682Smarkm#include "iicbb_if.h"
5455682Smarkm#include "smbus_if.h"
5590926Snectar
56178825Sdfr#define VIAPM_DEBUG(x)	if (viapm_debug) (x)
57178825Sdfr
58178825Sdfr#ifdef DEBUG
59178825Sdfrstatic int viapm_debug = 1;
60178825Sdfr#else
61178825Sdfrstatic int viapm_debug = 0;
62178825Sdfr#endif
6355682Smarkm
6455682Smarkm#define VIA_586B_PMU_ID		0x30401106
6590926Snectar#define VIA_596A_PMU_ID		0x30501106
6655682Smarkm#define VIA_596B_PMU_ID		0x30511106
6790926Snectar#define VIA_686A_PMU_ID		0x30571106
6855682Smarkm#define VIA_8233_PMU_ID		0x30741106
69178825Sdfr
7055682Smarkm#define VIAPM_INB(port) \
7155682Smarkm	((u_char)bus_space_read_1(viapm->st, viapm->sh, port))
7290926Snectar#define VIAPM_OUTB(port,val) \
7390926Snectar	(bus_space_write_1(viapm->st, viapm->sh, port, (u_char)(val)))
7455682Smarkm
75178825Sdfr#define VIAPM_TYP_UNKNOWN	0
76178825Sdfr#define VIAPM_TYP_586B_3040E	1
7755682Smarkm#define VIAPM_TYP_586B_3040F	2
7855682Smarkm#define VIAPM_TYP_596B		3
7955682Smarkm#define VIAPM_TYP_686A		4
8055682Smarkm#define VIAPM_TYP_8233		5
8190926Snectar
8255682Smarkmstruct viapm_softc {
83178825Sdfr	int type;
84178825Sdfr	u_int32_t base;
85178825Sdfr        bus_space_tag_t st;
8655682Smarkm        bus_space_handle_t sh;
87178825Sdfr	int iorid;
88178825Sdfr	int irqrid;
8955682Smarkm	struct resource *iores;
90178825Sdfr	struct resource *irqres;
91178825Sdfr	void *irqih;
92178825Sdfr
93178825Sdfr	device_t iicbb;
94178825Sdfr	device_t smbus;
95178825Sdfr};
96178825Sdfr
97178825Sdfrstatic devclass_t viapm_devclass;
98178825Sdfrstatic devclass_t viapropm_devclass;
99178825Sdfr
100178825Sdfr/*
10155682Smarkm * VT82C586B definitions
10255682Smarkm */
10390926Snectar
10490926Snectar#define VIAPM_586B_REVID	0x08
10555682Smarkm
106178825Sdfr#define VIAPM_586B_3040E_BASE	0x20
107178825Sdfr#define VIAPM_586B_3040E_ACTIV	0x4		/* 16 bits */
10855682Smarkm
10955682Smarkm#define VIAPM_586B_3040F_BASE	0x48
11090926Snectar#define VIAPM_586B_3040F_ACTIV	0x41		/* 8 bits */
11190926Snectar
11290926Snectar#define VIAPM_586B_OEM_REV_E	0x00
11390926Snectar#define VIAPM_586B_OEM_REV_F	0x01
11490926Snectar#define VIAPM_586B_PROD_REV_A	0x10
11590926Snectar
11690926Snectar#define VIAPM_586B_BA_MASK	0x0000ff00
11790926Snectar
11890926Snectar#define GPIO_DIR	0x40
11990926Snectar#define GPIO_VAL	0x42
12090926Snectar#define EXTSMI_VAL	0x44
121178825Sdfr
122233294Sstas#define VIAPM_SCL	0x02			/* GPIO1_VAL */
123178825Sdfr#define VIAPM_SDA	0x04			/* GPIO2_VAL */
124178825Sdfr
125233294Sstas/*
126178825Sdfr * VIAPRO common definitions
127178825Sdfr */
128178825Sdfr
129178825Sdfr#define VIAPM_PRO_BA_MASK	0x0000fff0
130178825Sdfr#define VIAPM_PRO_SMBCTRL	0xd2
131178825Sdfr#define VIAPM_PRO_REVID		0xd6
132178825Sdfr
133178825Sdfr/*
134178825Sdfr * VT82C686A definitions
13590926Snectar */
13690926Snectar
137178825Sdfr#define VIAPM_PRO_BASE		0x90
13890926Snectar
13990926Snectar#define SMBHST			0x0
14090926Snectar#define SMBHSL			0x1
14190926Snectar#define SMBHCTRL		0x2
142102644Snectar#define SMBHCMD			0x3
14355682Smarkm#define SMBHADDR		0x4
14455682Smarkm#define SMBHDATA0		0x5
145178825Sdfr#define SMBHDATA1		0x6
146178825Sdfr#define SMBHBLOCK		0x7
147178825Sdfr
148178825Sdfr#define SMBSST			0x1
149178825Sdfr#define SMBSCTRL		0x8
15055682Smarkm#define SMBSSDWCMD		0x9
151178825Sdfr#define SMBSEVENT		0xa
152178825Sdfr#define SMBSDATA		0xc
153178825Sdfr
154178825Sdfr#define SMBHST_RESERVED		0xef	/* reserved bits */
155178825Sdfr#define SMBHST_FAILED		0x10	/* failed bus transaction */
156178825Sdfr#define SMBHST_COLLID		0x08	/* bus collision */
157178825Sdfr#define SMBHST_ERROR		0x04	/* device error */
158178825Sdfr#define SMBHST_INTR		0x02	/* command completed */
159178825Sdfr#define SMBHST_BUSY		0x01	/* host busy */
160178825Sdfr
161178825Sdfr#define SMBHCTRL_START		0x40	/* start command */
162178825Sdfr#define SMBHCTRL_PROTO		0x1c	/* command protocol mask */
163178825Sdfr#define SMBHCTRL_QUICK		0x00
164178825Sdfr#define SMBHCTRL_SENDRECV	0x04
165178825Sdfr#define SMBHCTRL_BYTE		0x08
166178825Sdfr#define SMBHCTRL_WORD		0x0c
167178825Sdfr#define SMBHCTRL_BLOCK		0x14
168178825Sdfr#define SMBHCTRL_KILL		0x02	/* stop the current transaction */
169178825Sdfr#define SMBHCTRL_ENABLE		0x01	/* enable interrupts */
170178825Sdfr
171178825Sdfr#define SMBSCTRL_ENABLE		0x01	/* enable slave */
17255682Smarkm
173178825Sdfr
174178825Sdfr/*
175178825Sdfr * VIA8233 definitions
176178825Sdfr */
177178825Sdfr
178178825Sdfr#define VIAPM_8233_BASE		0xD0
179178825Sdfr
180178825Sdfrstatic int
181178825Sdfrviapm_586b_probe(device_t dev)
182178825Sdfr{
183233294Sstas	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
184178825Sdfr	u_int32_t l;
185233294Sstas	u_int16_t s;
186178825Sdfr	u_int8_t c;
187178825Sdfr
188178825Sdfr	switch (pci_get_devid(dev)) {
189178825Sdfr	case VIA_586B_PMU_ID:
190178825Sdfr
191178825Sdfr		bzero(viapm, sizeof(struct viapm_softc));
192178825Sdfr
193233294Sstas		l = pci_read_config(dev, VIAPM_586B_REVID, 1);
194178825Sdfr		switch (l) {
195178825Sdfr		case VIAPM_586B_OEM_REV_E:
19655682Smarkm			viapm->type = VIAPM_TYP_586B_3040E;
197178825Sdfr			viapm->iorid = VIAPM_586B_3040E_BASE;
198178825Sdfr
199178825Sdfr			/* Activate IO block access */
200178825Sdfr			s = pci_read_config(dev, VIAPM_586B_3040E_ACTIV, 2);
201178825Sdfr			pci_write_config(dev, VIAPM_586B_3040E_ACTIV, s | 0x1, 2);
202178825Sdfr			break;
203178825Sdfr
204178825Sdfr		case VIAPM_586B_OEM_REV_F:
205178825Sdfr		case VIAPM_586B_PROD_REV_A:
206178825Sdfr		default:
207178825Sdfr			viapm->type = VIAPM_TYP_586B_3040F;
208178825Sdfr			viapm->iorid = VIAPM_586B_3040F_BASE;
209178825Sdfr
210178825Sdfr			/* Activate IO block access */
211233294Sstas			c = pci_read_config(dev, VIAPM_586B_3040F_ACTIV, 1);
212178825Sdfr			pci_write_config(dev, VIAPM_586B_3040F_ACTIV, c | 0x80, 1);
213178825Sdfr			break;
214233294Sstas		}
215178825Sdfr
216178825Sdfr		viapm->base = pci_read_config(dev, viapm->iorid, 4) &
217233294Sstas				VIAPM_586B_BA_MASK;
218178825Sdfr
219178825Sdfr		/*
22055682Smarkm		 * We have to set the I/O resources by hand because it is
221178825Sdfr		 * described outside the viapmope of the traditional maps
222178825Sdfr		 */
223178825Sdfr		if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
224178825Sdfr							viapm->base, 256)) {
225178825Sdfr			device_printf(dev, "could not set bus resource\n");
226178825Sdfr			return ENXIO;
227178825Sdfr		}
228178825Sdfr		device_set_desc(dev, "VIA VT82C586B Power Management Unit");
229178825Sdfr		return 0;
230178825Sdfr
231178825Sdfr	default:
232178825Sdfr		break;
233178825Sdfr	}
234178825Sdfr
235178825Sdfr	return ENXIO;
236178825Sdfr}
237178825Sdfr
238178825Sdfr
239233294Sstasstatic int
240178825Sdfrviapm_pro_probe(device_t dev)
241178825Sdfr{
242178825Sdfr	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
243178825Sdfr#ifdef VIAPM_BASE_ADDR
244233294Sstas	u_int32_t l;
245178825Sdfr#endif
246178825Sdfr	u_int32_t base_cfgreg;
247178825Sdfr	char *desc;
248178825Sdfr
24955682Smarkm	switch (pci_get_devid(dev)) {
25055682Smarkm	case VIA_596A_PMU_ID:
25190926Snectar		desc = "VIA VT82C596A Power Management Unit";
25290926Snectar		viapm->type = VIAPM_TYP_596B;
25390926Snectar		base_cfgreg = VIAPM_PRO_BASE;
25490926Snectar		goto viapro;
255233294Sstas
25690926Snectar	case VIA_596B_PMU_ID:
25790926Snectar		desc = "VIA VT82C596B Power Management Unit";
25855682Smarkm		viapm->type = VIAPM_TYP_596B;
25955682Smarkm		base_cfgreg = VIAPM_PRO_BASE;
26055682Smarkm		goto viapro;
26155682Smarkm
262233294Sstas	case VIA_686A_PMU_ID:
26390926Snectar		desc = "VIA VT82C686A Power Management Unit";
26472445Sassar		viapm->type = VIAPM_TYP_686A;
26590926Snectar		base_cfgreg = VIAPM_PRO_BASE;
26672445Sassar		goto viapro;
26772445Sassar
26872445Sassar	case VIA_8233_PMU_ID:
26972445Sassar		desc = "VIA VT8233 Power Management Unit";
27090926Snectar		viapm->type = VIAPM_TYP_UNKNOWN;
27172445Sassar		base_cfgreg = VIAPM_8233_BASE;
272178825Sdfr		goto viapro;
273178825Sdfr
27472445Sassar	viapro:
27590926Snectar
27690926Snectar#ifdef VIAPM_BASE_ADDR
27790926Snectar		/* force VIAPM I/O base address */
27890926Snectar
27972445Sassar		/* enable the SMBus controller function */
28090926Snectar		l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
28190926Snectar		pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
28290926Snectar
28390926Snectar		/* write the base address */
28472445Sassar		pci_write_config(dev, base_cfgreg,
28572445Sassar				 VIAPM_BASE_ADDR & VIAPM_PRO_BA_MASK, 4);
286178825Sdfr#endif
28790926Snectar
28855682Smarkm		viapm->base = pci_read_config(dev, base_cfgreg, 4) & VIAPM_PRO_BA_MASK;
28955682Smarkm
29055682Smarkm		/*
29190926Snectar		 * We have to set the I/O resources by hand because it is
29255682Smarkm		 * described outside the viapmope of the traditional maps
29355682Smarkm		 */
29455682Smarkm		viapm->iorid = base_cfgreg;
29555682Smarkm		if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
296178825Sdfr				     viapm->base, 16)) {
29790926Snectar			device_printf(dev, "could not set bus resource 0x%x\n",
298178825Sdfr					viapm->base);
299178825Sdfr			return ENXIO;
300178825Sdfr		}
301178825Sdfr
302178825Sdfr		if (1 || bootverbose) {
30390926Snectar			device_printf(dev, "SMBus I/O base at 0x%x\n", viapm->base);
304178825Sdfr		}
305178825Sdfr
306178825Sdfr		device_set_desc(dev, desc);
307178825Sdfr		return 0;
308178825Sdfr
30955682Smarkm	default:
31055682Smarkm		break;
31155682Smarkm	}
31255682Smarkm
31355682Smarkm	return ENXIO;
31455682Smarkm}
31590926Snectar
31690926Snectarstatic int
31755682Smarkmviapm_pro_attach(device_t dev)
31890926Snectar{
31990926Snectar	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
32090926Snectar	u_int32_t l;
32190926Snectar
322178825Sdfr	if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT,
323178825Sdfr		&viapm->iorid, 0l, ~0l, 1, RF_ACTIVE))) {
32490926Snectar		device_printf(dev, "could not allocate bus space\n");
32590926Snectar		goto error;
32690926Snectar	}
32790926Snectar	viapm->st = rman_get_bustag(viapm->iores);
32890926Snectar	viapm->sh = rman_get_bushandle(viapm->iores);
32990926Snectar
33090926Snectar#if notyet
33190926Snectar	/* force irq 9 */
33255682Smarkm	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
33355682Smarkm	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 0x80, 1);
334178825Sdfr
335178825Sdfr	viapm->irqrid = 0;
33655682Smarkm	if (!(viapm->irqres = bus_alloc_resource(dev, SYS_RES_IRQ,
33757422Smarkm				&viapm->irqrid, 9, 9, 1,
33855682Smarkm				RF_SHAREABLE | RF_ACTIVE))) {
33955682Smarkm		device_printf(dev, "could not allocate irq\n");
34055682Smarkm		goto error;
34190926Snectar	}
34290926Snectar
34355682Smarkm	if (bus_setup_intr(dev, viapm->irqres, INTR_TYPE_MISC,
34490926Snectar			(driver_intr_t *) viasmb_intr, viapm, &viapm->irqih)) {
34555682Smarkm		device_printf(dev, "could not setup irq\n");
34655682Smarkm		goto error;
347233294Sstas	}
34890926Snectar#endif
34955682Smarkm
350178825Sdfr	if (1 | bootverbose) {
351178825Sdfr		l = pci_read_config(dev, VIAPM_PRO_REVID, 1);
352233294Sstas		device_printf(dev, "SMBus revision code 0x%x\n", l);
35355682Smarkm	}
354178825Sdfr
355178825Sdfr	viapm->smbus = device_add_child(dev, "smbus", -1);
356178825Sdfr
357178825Sdfr	/* probe and attach the smbus */
358178825Sdfr	bus_generic_attach(dev);
359178825Sdfr
360178825Sdfr	/* disable slave function */
361178825Sdfr	VIAPM_OUTB(SMBSCTRL, VIAPM_INB(SMBSCTRL) & ~SMBSCTRL_ENABLE);
362178825Sdfr
36355682Smarkm	/* enable the SMBus controller function */
364178825Sdfr	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
365178825Sdfr	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
366178825Sdfr
367178825Sdfr#if notyet
368178825Sdfr	/* enable interrupts */
369178825Sdfr	VIAPM_OUTB(SMBHCTRL, VIAPM_INB(SMBHCTRL) | SMBHCTRL_ENABLE);
370178825Sdfr#endif
371178825Sdfr
372178825Sdfr	return 0;
373178825Sdfr
374178825Sdfrerror:
375178825Sdfr	if (viapm->iores)
376178825Sdfr		bus_release_resource(dev, SYS_RES_IOPORT, viapm->iorid, viapm->iores);
377178825Sdfr#if notyet
378178825Sdfr	if (viapm->irqres)
379178825Sdfr		bus_release_resource(dev, SYS_RES_IRQ, viapm->irqrid, viapm->irqres);
380178825Sdfr#endif
381233294Sstas
382178825Sdfr	return ENXIO;
383178825Sdfr}
38455682Smarkm
385178825Sdfrstatic int
386178825Sdfrviapm_586b_attach(device_t dev)
38755682Smarkm{
388178825Sdfr	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
389178825Sdfr
390178825Sdfr	if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT,
391178825Sdfr		&viapm->iorid, 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE))) {
392178825Sdfr		device_printf(dev, "could not allocate bus resource\n");
393178825Sdfr		return ENXIO;
394178825Sdfr	}
395178825Sdfr	viapm->st = rman_get_bustag(viapm->iores);
396178825Sdfr	viapm->sh = rman_get_bushandle(viapm->iores);
397178825Sdfr
39855682Smarkm	VIAPM_OUTB(GPIO_DIR, VIAPM_INB(GPIO_DIR) | VIAPM_SCL | VIAPM_SDA);
399178825Sdfr
400178825Sdfr	/* add generic bit-banging code */
401178825Sdfr	if (!(viapm->iicbb = device_add_child(dev, "iicbb", -1)))
402178825Sdfr		goto error;
403178825Sdfr
404178825Sdfr	bus_generic_attach(dev);
405178825Sdfr
406178825Sdfr	return 0;
407178825Sdfr
408178825Sdfrerror:
409178825Sdfr	if (viapm->iores)
410178825Sdfr		bus_release_resource(dev, SYS_RES_IOPORT,
411178825Sdfr					viapm->iorid, viapm->iores);
412178825Sdfr	return ENXIO;
41355682Smarkm}
414178825Sdfr
41555682Smarkmstatic int
416178825Sdfrviapm_586b_detach(device_t dev)
417178825Sdfr{
418178825Sdfr	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
419178825Sdfr	int error;
420178825Sdfr
421178825Sdfr	bus_generic_detach(dev);
422178825Sdfr	if (viapm->iicbb) {
423178825Sdfr		device_delete_child(dev, viapm->iicbb);
424178825Sdfr	}
425178825Sdfr
426178825Sdfr	if (viapm->iores && (error = bus_release_resource(dev, SYS_RES_IOPORT,
427178825Sdfr						viapm->iorid, viapm->iores)))
428178825Sdfr		return (error);
429178825Sdfr
43055682Smarkm	return 0;
431178825Sdfr}
432178825Sdfr
43355682Smarkmstatic int
434178825Sdfrviapm_pro_detach(device_t dev)
435178825Sdfr{
436178825Sdfr	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
437178825Sdfr	int error;
438178825Sdfr
439178825Sdfr	bus_generic_detach(dev);
440178825Sdfr	if (viapm->smbus) {
441178825Sdfr		device_delete_child(dev, viapm->smbus);
442178825Sdfr	}
443233294Sstas
444178825Sdfr	if ((error = bus_release_resource(dev, SYS_RES_IOPORT,
445178825Sdfr				viapm->iorid, viapm->iores)))
446178825Sdfr		return (error);
447178825Sdfr
448178825Sdfr#if notyet
449178825Sdfr	if ((error = bus_release_resource(dev, SYS_RES_IRQ,
450178825Sdfr					viapm->irqrid, viapm->irqres))
45155682Smarkm		return (error);
452178825Sdfr#endif
45355682Smarkm
454178825Sdfr	return 0;
455178825Sdfr}
456178825Sdfr
457178825Sdfrstatic int
458178825Sdfrviabb_callback(device_t dev, int index, caddr_t *data)
45955682Smarkm{
460178825Sdfr	return 0;
461178825Sdfr}
462178825Sdfr
463178825Sdfrstatic void
464178825Sdfrviabb_setscl(device_t dev, int ctrl)
465178825Sdfr{
46655682Smarkm	struct viapm_softc *viapm = device_get_softc(dev);
467178825Sdfr	u_char val;
46855682Smarkm
469178825Sdfr	val = VIAPM_INB(GPIO_VAL);
470178825Sdfr
471178825Sdfr	if (ctrl)
472178825Sdfr		val |= VIAPM_SCL;
473178825Sdfr	else
474178825Sdfr		val &= ~VIAPM_SCL;
47555682Smarkm
476178825Sdfr	VIAPM_OUTB(GPIO_VAL, val);
47755682Smarkm
478178825Sdfr	return;
479178825Sdfr}
480178825Sdfr
481178825Sdfrstatic void
482178825Sdfrviabb_setsda(device_t dev, int data)
483178825Sdfr{
48455682Smarkm	struct viapm_softc *viapm = device_get_softc(dev);
485178825Sdfr	u_char val;
48655682Smarkm
487178825Sdfr	val = VIAPM_INB(GPIO_VAL);
488178825Sdfr
489178825Sdfr	if (data)
490178825Sdfr		val |= VIAPM_SDA;
491178825Sdfr	else
492178825Sdfr		val &= ~VIAPM_SDA;
49355682Smarkm
494178825Sdfr	VIAPM_OUTB(GPIO_VAL, val);
49555682Smarkm
496178825Sdfr	return;
497178825Sdfr}
498178825Sdfr
499178825Sdfrstatic int
500178825Sdfrviabb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
501233294Sstas{
502178825Sdfr	/* reset bus */
503178825Sdfr	viabb_setsda(dev, 1);
504178825Sdfr	viabb_setscl(dev, 1);
505178825Sdfr
506178825Sdfr	return (IIC_ENOADDR);
507178825Sdfr}
508178825Sdfr
509178825Sdfrstatic int
510178825Sdfrviabb_getscl(device_t dev)
511178825Sdfr{
512178825Sdfr	struct viapm_softc *viapm = device_get_softc(dev);
513178825Sdfr
514178825Sdfr	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SCL) != 0);
515178825Sdfr}
516178825Sdfr
517178825Sdfrstatic int
518178825Sdfrviabb_getsda(device_t dev)
519178825Sdfr{
520178825Sdfr	struct viapm_softc *viapm = device_get_softc(dev);
521178825Sdfr
522178825Sdfr	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SDA) != 0);
523178825Sdfr}
524178825Sdfr
525178825Sdfrstatic int
526178825Sdfrviapm_abort(struct viapm_softc *viapm)
527178825Sdfr{
528178825Sdfr	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_KILL);
52990926Snectar	DELAY(10);
530178825Sdfr
53155682Smarkm	return (0);
53255682Smarkm}
53390926Snectar
534233294Sstasstatic int
535233294Sstasviapm_clear(struct viapm_softc *viapm)
53655682Smarkm{
537178825Sdfr	VIAPM_OUTB(SMBHST, SMBHST_FAILED | SMBHST_COLLID |
538178825Sdfr		SMBHST_ERROR | SMBHST_INTR);
53955682Smarkm	DELAY(10);
540178825Sdfr
541178825Sdfr	return (0);
542178825Sdfr}
54355682Smarkm
54455682Smarkmstatic int
545178825Sdfrviapm_busy(struct viapm_softc *viapm)
546178825Sdfr{
547178825Sdfr	u_char sts;
54855682Smarkm
549178825Sdfr	sts = VIAPM_INB(SMBHST);
550178825Sdfr
551178825Sdfr	VIAPM_DEBUG(printf("viapm: idle? STS=0x%x\n", sts));
55255682Smarkm
553178825Sdfr	return (sts & SMBHST_BUSY);
554178825Sdfr}
555178825Sdfr
556178825Sdfr/*
557178825Sdfr * Poll the SMBus controller
558178825Sdfr */
559178825Sdfrstatic int
56055682Smarkmviapm_wait(struct viapm_softc *viapm)
56155682Smarkm{
562178825Sdfr	int count = 10000;
56355682Smarkm	u_char sts = 0;
56455682Smarkm	int error;
56555682Smarkm
56690926Snectar	/* wait for command to complete and SMBus controller is idle */
567233294Sstas	while(count--) {
568233294Sstas		DELAY(10);
56955682Smarkm		sts = VIAPM_INB(SMBHST);
570178825Sdfr
571178825Sdfr		/* check if the controller is processing a command */
57255682Smarkm		if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR))
573178825Sdfr			break;
574178825Sdfr	}
575178825Sdfr
57655682Smarkm	VIAPM_DEBUG(printf("viapm: SMBHST=0x%x\n", sts));
57755682Smarkm
578178825Sdfr	error = SMB_ENOERR;
579178825Sdfr
580178825Sdfr	if (!count)
581178825Sdfr		error |= SMB_ETIMEOUT;
582178825Sdfr
583178825Sdfr	if (sts & SMBHST_FAILED)
58455682Smarkm		error |= SMB_EABORT;
585178825Sdfr
586178825Sdfr	if (sts & SMBHST_COLLID)
587178825Sdfr		error |= SMB_ENOACK;
588178825Sdfr
589178825Sdfr	if (sts & SMBHST_ERROR)
590178825Sdfr		error |= SMB_EBUSERR;
591178825Sdfr
59255682Smarkm	if (error != SMB_ENOERR)
59355682Smarkm		viapm_abort(viapm);
594178825Sdfr
59555682Smarkm	viapm_clear(viapm);
59655682Smarkm
59755682Smarkm	return (error);
59890926Snectar}
599233294Sstas
600233294Sstasstatic int
60155682Smarkmviasmb_callback(device_t dev, int index, caddr_t *data)
602178825Sdfr{
603178825Sdfr	int error = 0;
60455682Smarkm
605178825Sdfr	switch (index) {
606178825Sdfr	case SMB_REQUEST_BUS:
60755682Smarkm	case SMB_RELEASE_BUS:
60855682Smarkm		/* ok, bus allocation accepted */
609178825Sdfr		break;
610178825Sdfr	default:
611178825Sdfr		error = EINVAL;
612178825Sdfr	}
613178825Sdfr
614178825Sdfr	return (error);
61555682Smarkm}
616178825Sdfr
617178825Sdfrstatic int
618178825Sdfrviasmb_quick(device_t dev, u_char slave, int how)
619178825Sdfr{
620178825Sdfr	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
62155682Smarkm	int error;
62255682Smarkm
623178825Sdfr	viapm_clear(viapm);
624178825Sdfr	if (viapm_busy(viapm))
62555682Smarkm		return (EBUSY);
62655682Smarkm
62755682Smarkm	switch (how) {
62890926Snectar	case SMB_QWRITE:
629233294Sstas		VIAPM_DEBUG(printf("viapm: QWRITE to 0x%x", slave));
630233294Sstas		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
63155682Smarkm		break;
632178825Sdfr	case SMB_QREAD:
63355682Smarkm		VIAPM_DEBUG(printf("viapm: QREAD to 0x%x", slave));
63455682Smarkm		VIAPM_OUTB(SMBHADDR, slave | LSB);
63555682Smarkm		break;
63655682Smarkm	default:
63790926Snectar		panic("%s: unknown QUICK command (%x)!", __FUNCTION__,
638233294Sstas			how);
639233294Sstas	}
64055682Smarkm
641233294Sstas	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_QUICK);
642178825Sdfr
64355682Smarkm	error = viapm_wait(viapm);
644178825Sdfr
645178825Sdfr	return (error);
646178825Sdfr}
647178825Sdfr
648178825Sdfrstatic int
649178825Sdfrviasmb_sendb(device_t dev, u_char slave, char byte)
650178825Sdfr{
651178825Sdfr	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
652178825Sdfr	int error;
653178825Sdfr
65455682Smarkm	viapm_clear(viapm);
655178825Sdfr	if (viapm_busy(viapm))
656178825Sdfr		return (EBUSY);
657178825Sdfr
658178825Sdfr	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
659178825Sdfr	VIAPM_OUTB(SMBHCMD, byte);
660178825Sdfr
661178825Sdfr	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
66255682Smarkm
663178825Sdfr	error = viapm_wait(viapm);
664178825Sdfr
665178825Sdfr	VIAPM_DEBUG(printf("viapm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
666178825Sdfr
667178825Sdfr	return (error);
668178825Sdfr}
669178825Sdfr
670178825Sdfrstatic int
671178825Sdfrviasmb_recvb(device_t dev, u_char slave, char *byte)
67255682Smarkm{
67355682Smarkm	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
67455682Smarkm	int error;
67555682Smarkm
67690926Snectar	viapm_clear(viapm);
677233294Sstas	if (viapm_busy(viapm))
678233294Sstas		return (EBUSY);
67955682Smarkm
680178825Sdfr	VIAPM_OUTB(SMBHADDR, slave | LSB);
681178825Sdfr
682178825Sdfr	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
68355682Smarkm
684178825Sdfr	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
685178825Sdfr		*byte = VIAPM_INB(SMBHDATA0);
686178825Sdfr
687178825Sdfr	VIAPM_DEBUG(printf("viapm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
688178825Sdfr
689178825Sdfr	return (error);
69055682Smarkm}
691178825Sdfr
692178825Sdfrstatic int
693178825Sdfrviasmb_writeb(device_t dev, u_char slave, char cmd, char byte)
694178825Sdfr{
69555682Smarkm	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
69655682Smarkm	int error;
697
698	viapm_clear(viapm);
699	if (viapm_busy(viapm))
700		return (EBUSY);
701
702	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
703	VIAPM_OUTB(SMBHCMD, cmd);
704	VIAPM_OUTB(SMBHDATA0, byte);
705
706	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
707
708	error = viapm_wait(viapm);
709
710	VIAPM_DEBUG(printf("viapm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
711
712	return (error);
713}
714
715static int
716viasmb_readb(device_t dev, u_char slave, char cmd, char *byte)
717{
718	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
719	int error;
720
721	viapm_clear(viapm);
722	if (viapm_busy(viapm))
723		return (EBUSY);
724
725	VIAPM_OUTB(SMBHADDR, slave | LSB);
726	VIAPM_OUTB(SMBHCMD, cmd);
727
728	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
729
730	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
731		*byte = VIAPM_INB(SMBHDATA0);
732
733	VIAPM_DEBUG(printf("viapm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
734
735	return (error);
736}
737
738static int
739viasmb_writew(device_t dev, u_char slave, char cmd, short word)
740{
741	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
742	int error;
743
744	viapm_clear(viapm);
745	if (viapm_busy(viapm))
746		return (EBUSY);
747
748	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
749	VIAPM_OUTB(SMBHCMD, cmd);
750	VIAPM_OUTB(SMBHDATA0, word & 0x00ff);
751	VIAPM_OUTB(SMBHDATA1, (word & 0xff00) >> 8);
752
753	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
754
755	error = viapm_wait(viapm);
756
757	VIAPM_DEBUG(printf("viapm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
758
759	return (error);
760}
761
762static int
763viasmb_readw(device_t dev, u_char slave, char cmd, short *word)
764{
765	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
766	int error;
767	u_char high, low;
768
769	viapm_clear(viapm);
770	if (viapm_busy(viapm))
771		return (EBUSY);
772
773	VIAPM_OUTB(SMBHADDR, slave | LSB);
774	VIAPM_OUTB(SMBHCMD, cmd);
775
776	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
777
778	if ((error = viapm_wait(viapm)) == SMB_ENOERR) {
779		low = VIAPM_INB(SMBHDATA0);
780		high = VIAPM_INB(SMBHDATA1);
781
782		*word = ((high & 0xff) << 8) | (low & 0xff);
783	}
784
785	VIAPM_DEBUG(printf("viapm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
786
787	return (error);
788}
789
790static int
791viasmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
792{
793	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
794	u_char remain, len, i;
795	int error = SMB_ENOERR;
796
797	viapm_clear(viapm);
798	if (viapm_busy(viapm))
799		return (EBUSY);
800
801	remain = count;
802	while (remain) {
803		len = min(remain, 32);
804
805		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
806		VIAPM_OUTB(SMBHCMD, cmd);
807		VIAPM_OUTB(SMBHDATA0, len);
808		i = VIAPM_INB(SMBHCTRL);
809
810		/* fill the 32-byte internal buffer */
811		for (i=0; i<len; i++) {
812			VIAPM_OUTB(SMBHBLOCK, buf[count-remain+i]);
813			DELAY(2);
814		}
815		VIAPM_OUTB(SMBHCMD, cmd);
816		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
817
818		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
819			goto error;
820
821		remain -= len;
822	}
823
824error:
825	VIAPM_DEBUG(printf("viapm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
826
827	return (error);
828
829}
830
831static int
832viasmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
833{
834	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
835	u_char remain, len, i;
836	int error = SMB_ENOERR;
837
838	viapm_clear(viapm);
839	if (viapm_busy(viapm))
840		return (EBUSY);
841
842	remain = count;
843	while (remain) {
844		VIAPM_OUTB(SMBHADDR, slave | LSB);
845		VIAPM_OUTB(SMBHCMD, cmd);
846		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
847
848		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
849			goto error;
850
851		len = VIAPM_INB(SMBHDATA0);
852		i = VIAPM_INB(SMBHCTRL); 		/* reset counter */
853
854		len = min(len, remain);
855
856		/* read the 32-byte internal buffer */
857		for (i=0; i<len; i++) {
858			buf[count-remain+i] = VIAPM_INB(SMBHBLOCK);
859			DELAY(2);
860		}
861
862		remain -= len;
863	}
864error:
865	VIAPM_DEBUG(printf("viapm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
866
867	return (error);
868}
869
870static device_method_t viapm_methods[] = {
871	/* device interface */
872	DEVMETHOD(device_probe,		viapm_586b_probe),
873	DEVMETHOD(device_attach,	viapm_586b_attach),
874	DEVMETHOD(device_detach,	viapm_586b_detach),
875
876	/* iicbb interface */
877	DEVMETHOD(iicbb_callback,	viabb_callback),
878	DEVMETHOD(iicbb_setscl,		viabb_setscl),
879	DEVMETHOD(iicbb_setsda,		viabb_setsda),
880	DEVMETHOD(iicbb_getscl,		viabb_getscl),
881	DEVMETHOD(iicbb_getsda,		viabb_getsda),
882	DEVMETHOD(iicbb_reset,		viabb_reset),
883
884	{ 0, 0 }
885};
886
887static driver_t viapm_driver = {
888	"viapm",
889	viapm_methods,
890	sizeof(struct viapm_softc),
891};
892
893static device_method_t viapropm_methods[] = {
894	/* device interface */
895	DEVMETHOD(device_probe,		viapm_pro_probe),
896	DEVMETHOD(device_attach,	viapm_pro_attach),
897	DEVMETHOD(device_detach,	viapm_pro_detach),
898
899	/* smbus interface */
900	DEVMETHOD(smbus_callback,	viasmb_callback),
901	DEVMETHOD(smbus_quick,		viasmb_quick),
902	DEVMETHOD(smbus_sendb,		viasmb_sendb),
903	DEVMETHOD(smbus_recvb,		viasmb_recvb),
904	DEVMETHOD(smbus_writeb,		viasmb_writeb),
905	DEVMETHOD(smbus_readb,		viasmb_readb),
906	DEVMETHOD(smbus_writew,		viasmb_writew),
907	DEVMETHOD(smbus_readw,		viasmb_readw),
908	DEVMETHOD(smbus_bwrite,		viasmb_bwrite),
909	DEVMETHOD(smbus_bread,		viasmb_bread),
910
911	{ 0, 0 }
912};
913
914static driver_t viapropm_driver = {
915	"viapropm",
916	viapropm_methods,
917	sizeof(struct viapm_softc),
918};
919
920DRIVER_MODULE(viapm, pci, viapm_driver, viapm_devclass, 0, 0);
921DRIVER_MODULE(viapropm, pci, viapropm_driver, viapropm_devclass, 0, 0);
922
923MODULE_DEPEND(viapm, pci, 1, 1, 1);
924MODULE_DEPEND(viaprom, pci, 1, 1, 1);
925MODULE_DEPEND(viapm, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
926MODULE_DEPEND(viapropm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
927MODULE_VERSION(viapm, 1);
928