viapm.c revision 116671
10SN/A/*-
20SN/A * Copyright (c) 2001 Alcove - Nicolas Souchu
30SN/A * All rights reserved.
40SN/A *
50SN/A * Redistribution and use in source and binary forms, with or without
60SN/A * modification, are permitted provided that the following conditions
70SN/A * are met:
80SN/A * 1. Redistributions of source code must retain the above copyright
90SN/A *    notice, this list of conditions and the following disclaimer.
100SN/A * 2. Redistributions in binary form must reproduce the above copyright
119980SDana.Myers@Sun.COM *    notice, this list of conditions and the following disclaimer in the
120SN/A *    documentation and/or other materials provided with the distribution.
130SN/A *
140SN/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
150SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
160SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
170SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
180SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
190SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
200SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
210SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
220SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
230SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
240SN/A * SUCH DAMAGE.
250SN/A */
260SN/A
270SN/A#include <sys/cdefs.h>
280SN/A__FBSDID("$FreeBSD: head/sys/pci/viapm.c 116671 2003-06-22 06:50:02Z mdodd $");
290SN/A
300SN/A#include <sys/param.h>
310SN/A#include <sys/kernel.h>
320SN/A#include <sys/systm.h>
330SN/A#include <sys/module.h>
340SN/A#include <sys/bus.h>
350SN/A#include <sys/uio.h>
360SN/A
370SN/A#include <machine/bus_pio.h>
380SN/A#include <machine/bus_memio.h>
390SN/A#include <machine/bus.h>
400SN/A#include <machine/clock.h>		/* for DELAY */
410SN/A#include <machine/resource.h>
420SN/A#include <sys/rman.h>
430SN/A
440SN/A#include <pci/pcivar.h>
450SN/A#include <pci/pcireg.h>
460SN/A
470SN/A#include <dev/iicbus/iiconf.h>
480SN/A#include <dev/iicbus/iicbus.h>
490SN/A
500SN/A#include <dev/smbus/smbconf.h>
510SN/A#include <dev/smbus/smbus.h>
520SN/A
530SN/A#include "iicbb_if.h"
540SN/A#include "smbus_if.h"
550SN/A
560SN/A#define VIAPM_DEBUG(x)	if (viapm_debug) (x)
570SN/A
580SN/A#ifdef DEBUG
590SN/Astatic int viapm_debug = 1;
600SN/A#else
610SN/Astatic int viapm_debug = 0;
620SN/A#endif
630SN/A
640SN/A#define VIA_586B_PMU_ID		0x30401106
650SN/A#define VIA_596A_PMU_ID		0x30501106
660SN/A#define VIA_596B_PMU_ID		0x30511106
670SN/A#define VIA_686A_PMU_ID		0x30571106
680SN/A#define VIA_8233_PMU_ID		0x30741106
690SN/A#define	VIA_8233A_PMU_ID	0x31471106
700SN/A
710SN/A#define VIAPM_INB(port) \
720SN/A	((u_char)bus_space_read_1(viapm->st, viapm->sh, port))
730SN/A#define VIAPM_OUTB(port,val) \
740SN/A	(bus_space_write_1(viapm->st, viapm->sh, port, (u_char)(val)))
750SN/A
760SN/A#define VIAPM_TYP_UNKNOWN	0
770SN/A#define VIAPM_TYP_586B_3040E	1
780SN/A#define VIAPM_TYP_586B_3040F	2
790SN/A#define VIAPM_TYP_596B		3
800SN/A#define VIAPM_TYP_686A		4
810SN/A#define VIAPM_TYP_8233		5
820SN/A
830SN/Astruct viapm_softc {
840SN/A	int type;
850SN/A	u_int32_t base;
860SN/A        bus_space_tag_t st;
870SN/A        bus_space_handle_t sh;
880SN/A	int iorid;
890SN/A	int irqrid;
900SN/A	struct resource *iores;
910SN/A	struct resource *irqres;
920SN/A	void *irqih;
930SN/A
940SN/A	device_t iicbb;
950SN/A	device_t smbus;
960SN/A};
970SN/A
980SN/Astatic devclass_t viapm_devclass;
990SN/Astatic devclass_t viapropm_devclass;
1000SN/A
1010SN/A/*
1020SN/A * VT82C586B definitions
1030SN/A */
1040SN/A
1050SN/A#define VIAPM_586B_REVID	0x08
1060SN/A
1070SN/A#define VIAPM_586B_3040E_BASE	0x20
1080SN/A#define VIAPM_586B_3040E_ACTIV	0x4		/* 16 bits */
1090SN/A
1100SN/A#define VIAPM_586B_3040F_BASE	0x48
1110SN/A#define VIAPM_586B_3040F_ACTIV	0x41		/* 8 bits */
1120SN/A
1130SN/A#define VIAPM_586B_OEM_REV_E	0x00
1140SN/A#define VIAPM_586B_OEM_REV_F	0x01
1150SN/A#define VIAPM_586B_PROD_REV_A	0x10
1160SN/A
1170SN/A#define VIAPM_586B_BA_MASK	0x0000ff00
1180SN/A
1190SN/A#define GPIO_DIR	0x40
1209980SDana.Myers@Sun.COM#define GPIO_VAL	0x42
1210SN/A#define EXTSMI_VAL	0x44
1220SN/A
1230SN/A#define VIAPM_SCL	0x02			/* GPIO1_VAL */
1240SN/A#define VIAPM_SDA	0x04			/* GPIO2_VAL */
1250SN/A
1260SN/A/*
1270SN/A * VIAPRO common definitions
1280SN/A */
1290SN/A
1300SN/A#define VIAPM_PRO_BA_MASK	0x0000fff0
1310SN/A#define VIAPM_PRO_SMBCTRL	0xd2
1329980SDana.Myers@Sun.COM#define VIAPM_PRO_REVID		0xd6
1339980SDana.Myers@Sun.COM
1349980SDana.Myers@Sun.COM/*
1359980SDana.Myers@Sun.COM * VT82C686A definitions
1369980SDana.Myers@Sun.COM */
1379980SDana.Myers@Sun.COM
1389980SDana.Myers@Sun.COM#define VIAPM_PRO_BASE		0x90
1399980SDana.Myers@Sun.COM
1409980SDana.Myers@Sun.COM#define SMBHST			0x0
1419980SDana.Myers@Sun.COM#define SMBHSL			0x1
1429980SDana.Myers@Sun.COM#define SMBHCTRL		0x2
1439980SDana.Myers@Sun.COM#define SMBHCMD			0x3
1449980SDana.Myers@Sun.COM#define SMBHADDR		0x4
1459980SDana.Myers@Sun.COM#define SMBHDATA0		0x5
1469980SDana.Myers@Sun.COM#define SMBHDATA1		0x6
1479980SDana.Myers@Sun.COM#define SMBHBLOCK		0x7
1489980SDana.Myers@Sun.COM
1499980SDana.Myers@Sun.COM#define SMBSST			0x1
1509980SDana.Myers@Sun.COM#define SMBSCTRL		0x8
1519980SDana.Myers@Sun.COM#define SMBSSDWCMD		0x9
1529980SDana.Myers@Sun.COM#define SMBSEVENT		0xa
1539980SDana.Myers@Sun.COM#define SMBSDATA		0xc
1549980SDana.Myers@Sun.COM
1559980SDana.Myers@Sun.COM#define SMBHST_RESERVED		0xef	/* reserved bits */
1569980SDana.Myers@Sun.COM#define SMBHST_FAILED		0x10	/* failed bus transaction */
1579980SDana.Myers@Sun.COM#define SMBHST_COLLID		0x08	/* bus collision */
1589980SDana.Myers@Sun.COM#define SMBHST_ERROR		0x04	/* device error */
1599980SDana.Myers@Sun.COM#define SMBHST_INTR		0x02	/* command completed */
1609980SDana.Myers@Sun.COM#define SMBHST_BUSY		0x01	/* host busy */
1619980SDana.Myers@Sun.COM
1629980SDana.Myers@Sun.COM#define SMBHCTRL_START		0x40	/* start command */
1639980SDana.Myers@Sun.COM#define SMBHCTRL_PROTO		0x1c	/* command protocol mask */
1649980SDana.Myers@Sun.COM#define SMBHCTRL_QUICK		0x00
1659980SDana.Myers@Sun.COM#define SMBHCTRL_SENDRECV	0x04
1669980SDana.Myers@Sun.COM#define SMBHCTRL_BYTE		0x08
1679980SDana.Myers@Sun.COM#define SMBHCTRL_WORD		0x0c
1689980SDana.Myers@Sun.COM#define SMBHCTRL_BLOCK		0x14
1699980SDana.Myers@Sun.COM#define SMBHCTRL_KILL		0x02	/* stop the current transaction */
1709980SDana.Myers@Sun.COM#define SMBHCTRL_ENABLE		0x01	/* enable interrupts */
1719980SDana.Myers@Sun.COM
1729980SDana.Myers@Sun.COM#define SMBSCTRL_ENABLE		0x01	/* enable slave */
1739980SDana.Myers@Sun.COM
1749980SDana.Myers@Sun.COM
1759980SDana.Myers@Sun.COM/*
1769980SDana.Myers@Sun.COM * VIA8233 definitions
1779980SDana.Myers@Sun.COM */
1789980SDana.Myers@Sun.COM
1799980SDana.Myers@Sun.COM#define VIAPM_8233_BASE		0xD0
1809980SDana.Myers@Sun.COM
1810SN/Astatic int
1820SN/Aviapm_586b_probe(device_t dev)
1830SN/A{
1840SN/A	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
1850SN/A	u_int32_t l;
1860SN/A	u_int16_t s;
1870SN/A	u_int8_t c;
1880SN/A
1890SN/A	switch (pci_get_devid(dev)) {
1900SN/A	case VIA_586B_PMU_ID:
1910SN/A
1920SN/A		bzero(viapm, sizeof(struct viapm_softc));
1930SN/A
1940SN/A		l = pci_read_config(dev, VIAPM_586B_REVID, 1);
1950SN/A		switch (l) {
1960SN/A		case VIAPM_586B_OEM_REV_E:
1979980SDana.Myers@Sun.COM			viapm->type = VIAPM_TYP_586B_3040E;
1980SN/A			viapm->iorid = VIAPM_586B_3040E_BASE;
1990SN/A
2000SN/A			/* Activate IO block access */
2010SN/A			s = pci_read_config(dev, VIAPM_586B_3040E_ACTIV, 2);
2020SN/A			pci_write_config(dev, VIAPM_586B_3040E_ACTIV, s | 0x1, 2);
2030SN/A			break;
2040SN/A
2050SN/A		case VIAPM_586B_OEM_REV_F:
2060SN/A		case VIAPM_586B_PROD_REV_A:
2070SN/A		default:
2089980SDana.Myers@Sun.COM			viapm->type = VIAPM_TYP_586B_3040F;
2090SN/A			viapm->iorid = VIAPM_586B_3040F_BASE;
2100SN/A
2110SN/A			/* Activate IO block access */
2120SN/A			c = pci_read_config(dev, VIAPM_586B_3040F_ACTIV, 1);
2130SN/A			pci_write_config(dev, VIAPM_586B_3040F_ACTIV, c | 0x80, 1);
2140SN/A			break;
2150SN/A		}
2160SN/A
2170SN/A		viapm->base = pci_read_config(dev, viapm->iorid, 4) &
2180SN/A				VIAPM_586B_BA_MASK;
2190SN/A
2200SN/A		/*
2210SN/A		 * We have to set the I/O resources by hand because it is
2220SN/A		 * described outside the viapmope of the traditional maps
2230SN/A		 */
2240SN/A		if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
2250SN/A							viapm->base, 256)) {
2260SN/A			device_printf(dev, "could not set bus resource\n");
2270SN/A			return ENXIO;
2280SN/A		}
2290SN/A		device_set_desc(dev, "VIA VT82C586B Power Management Unit");
2300SN/A		return 0;
2310SN/A
2320SN/A	default:
2330SN/A		break;
2340SN/A	}
2350SN/A
2360SN/A	return ENXIO;
2370SN/A}
2380SN/A
2390SN/A
2400SN/Astatic int
2410SN/Aviapm_pro_probe(device_t dev)
2420SN/A{
2430SN/A	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
2440SN/A#ifdef VIAPM_BASE_ADDR
2450SN/A	u_int32_t l;
2460SN/A#endif
2470SN/A	u_int32_t base_cfgreg;
2487851SDana.Myers@Sun.COM	char *desc;
2490SN/A
25011225SDana.Myers@Sun.COM	switch (pci_get_devid(dev)) {
25111225SDana.Myers@Sun.COM	case VIA_596A_PMU_ID:
25211225SDana.Myers@Sun.COM		desc = "VIA VT82C596A Power Management Unit";
25311225SDana.Myers@Sun.COM		viapm->type = VIAPM_TYP_596B;
25411225SDana.Myers@Sun.COM		base_cfgreg = VIAPM_PRO_BASE;
25511225SDana.Myers@Sun.COM		goto viapro;
25611225SDana.Myers@Sun.COM
2570SN/A	case VIA_596B_PMU_ID:
2580SN/A		desc = "VIA VT82C596B Power Management Unit";
2590SN/A		viapm->type = VIAPM_TYP_596B;
2600SN/A		base_cfgreg = VIAPM_PRO_BASE;
26111225SDana.Myers@Sun.COM		goto viapro;
26211225SDana.Myers@Sun.COM
2639980SDana.Myers@Sun.COM	case VIA_686A_PMU_ID:
2649980SDana.Myers@Sun.COM		desc = "VIA VT82C686A Power Management Unit";
2650SN/A		viapm->type = VIAPM_TYP_686A;
2660SN/A		base_cfgreg = VIAPM_PRO_BASE;
2670SN/A		goto viapro;
26811225SDana.Myers@Sun.COM
26911225SDana.Myers@Sun.COM	case VIA_8233_PMU_ID:
27011225SDana.Myers@Sun.COM	case VIA_8233A_PMU_ID:
2710SN/A		desc = "VIA VT8233 Power Management Unit";
2720SN/A		viapm->type = VIAPM_TYP_UNKNOWN;
2730SN/A		base_cfgreg = VIAPM_8233_BASE;
2740SN/A		goto viapro;
2750SN/A
2760SN/A	viapro:
2770SN/A
2780SN/A#ifdef VIAPM_BASE_ADDR
2797851SDana.Myers@Sun.COM		/* force VIAPM I/O base address */
28011225SDana.Myers@Sun.COM
28111225SDana.Myers@Sun.COM		/* enable the SMBus controller function */
2820SN/A		l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
2830SN/A		pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
2840SN/A
2850SN/A		/* write the base address */
2860SN/A		pci_write_config(dev, base_cfgreg,
2870SN/A				 VIAPM_BASE_ADDR & VIAPM_PRO_BA_MASK, 4);
2880SN/A#endif
2890SN/A
2900SN/A		viapm->base = pci_read_config(dev, base_cfgreg, 4) & VIAPM_PRO_BA_MASK;
29111225SDana.Myers@Sun.COM
2920SN/A		/*
2930SN/A		 * We have to set the I/O resources by hand because it is
2942623SN/A		 * described outside the viapmope of the traditional maps
2950SN/A		 */
2960SN/A		viapm->iorid = base_cfgreg;
2970SN/A		if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
2980SN/A				     viapm->base, 16)) {
2990SN/A			device_printf(dev, "could not set bus resource 0x%x\n",
3000SN/A					viapm->base);
3010SN/A			return ENXIO;
3020SN/A		}
3030SN/A
3040SN/A		if (1 || bootverbose) {
3050SN/A			device_printf(dev, "SMBus I/O base at 0x%x\n", viapm->base);
3060SN/A		}
30711225SDana.Myers@Sun.COM
3080SN/A		device_set_desc(dev, desc);
3090SN/A		return 0;
3100SN/A
3110SN/A	default:
3120SN/A		break;
3130SN/A	}
3140SN/A
3150SN/A	return ENXIO;
31611225SDana.Myers@Sun.COM}
3170SN/A
31811225SDana.Myers@Sun.COMstatic int
3190SN/Aviapm_pro_attach(device_t dev)
32011225SDana.Myers@Sun.COM{
32111225SDana.Myers@Sun.COM	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
32211225SDana.Myers@Sun.COM	u_int32_t l;
3230SN/A
32411225SDana.Myers@Sun.COM	if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT,
32511225SDana.Myers@Sun.COM		&viapm->iorid, 0l, ~0l, 1, RF_ACTIVE))) {
3267851SDana.Myers@Sun.COM		device_printf(dev, "could not allocate bus space\n");
32711225SDana.Myers@Sun.COM		goto error;
32811225SDana.Myers@Sun.COM	}
32911225SDana.Myers@Sun.COM	viapm->st = rman_get_bustag(viapm->iores);
33011225SDana.Myers@Sun.COM	viapm->sh = rman_get_bushandle(viapm->iores);
33111225SDana.Myers@Sun.COM
33211225SDana.Myers@Sun.COM#if notyet
33311225SDana.Myers@Sun.COM	/* force irq 9 */
33411225SDana.Myers@Sun.COM	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
33511225SDana.Myers@Sun.COM	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 0x80, 1);
33611225SDana.Myers@Sun.COM
33711225SDana.Myers@Sun.COM	viapm->irqrid = 0;
33811225SDana.Myers@Sun.COM	if (!(viapm->irqres = bus_alloc_resource(dev, SYS_RES_IRQ,
33911225SDana.Myers@Sun.COM				&viapm->irqrid, 9, 9, 1,
3400SN/A				RF_SHAREABLE | RF_ACTIVE))) {
34111225SDana.Myers@Sun.COM		device_printf(dev, "could not allocate irq\n");
34211225SDana.Myers@Sun.COM		goto error;
34311225SDana.Myers@Sun.COM	}
34411225SDana.Myers@Sun.COM
3457851SDana.Myers@Sun.COM	if (bus_setup_intr(dev, viapm->irqres, INTR_TYPE_MISC,
34611225SDana.Myers@Sun.COM			(driver_intr_t *) viasmb_intr, viapm, &viapm->irqih)) {
34711225SDana.Myers@Sun.COM		device_printf(dev, "could not setup irq\n");
3487851SDana.Myers@Sun.COM		goto error;
34911225SDana.Myers@Sun.COM	}
3500SN/A#endif
35111225SDana.Myers@Sun.COM
35211225SDana.Myers@Sun.COM	if (1 | bootverbose) {
3530SN/A		l = pci_read_config(dev, VIAPM_PRO_REVID, 1);
35411225SDana.Myers@Sun.COM		device_printf(dev, "SMBus revision code 0x%x\n", l);
3550SN/A	}
3560SN/A
3570SN/A	viapm->smbus = device_add_child(dev, "smbus", -1);
3580SN/A
35911225SDana.Myers@Sun.COM	/* probe and attach the smbus */
36011225SDana.Myers@Sun.COM	bus_generic_attach(dev);
3610SN/A
36211225SDana.Myers@Sun.COM	/* disable slave function */
36311225SDana.Myers@Sun.COM	VIAPM_OUTB(SMBSCTRL, VIAPM_INB(SMBSCTRL) & ~SMBSCTRL_ENABLE);
36411225SDana.Myers@Sun.COM
36511225SDana.Myers@Sun.COM	/* enable the SMBus controller function */
36611225SDana.Myers@Sun.COM	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
36711225SDana.Myers@Sun.COM	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
36811225SDana.Myers@Sun.COM
36911225SDana.Myers@Sun.COM#if notyet
37011225SDana.Myers@Sun.COM	/* enable interrupts */
3710SN/A	VIAPM_OUTB(SMBHCTRL, VIAPM_INB(SMBHCTRL) | SMBHCTRL_ENABLE);
37211225SDana.Myers@Sun.COM#endif
3730SN/A
37411225SDana.Myers@Sun.COM	return 0;
37511225SDana.Myers@Sun.COM
37611225SDana.Myers@Sun.COMerror:
37711225SDana.Myers@Sun.COM	if (viapm->iores)
3787851SDana.Myers@Sun.COM		bus_release_resource(dev, SYS_RES_IOPORT, viapm->iorid, viapm->iores);
37911225SDana.Myers@Sun.COM#if notyet
38011225SDana.Myers@Sun.COM	if (viapm->irqres)
38111225SDana.Myers@Sun.COM		bus_release_resource(dev, SYS_RES_IRQ, viapm->irqrid, viapm->irqres);
38211225SDana.Myers@Sun.COM#endif
38311225SDana.Myers@Sun.COM
38411225SDana.Myers@Sun.COM	return ENXIO;
3850SN/A}
3860SN/A
38711225SDana.Myers@Sun.COMstatic int
38811225SDana.Myers@Sun.COMviapm_586b_attach(device_t dev)
38911225SDana.Myers@Sun.COM{
39011225SDana.Myers@Sun.COM	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
39111225SDana.Myers@Sun.COM
39211225SDana.Myers@Sun.COM	if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT,
39311225SDana.Myers@Sun.COM		&viapm->iorid, 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE))) {
39411225SDana.Myers@Sun.COM		device_printf(dev, "could not allocate bus resource\n");
39511225SDana.Myers@Sun.COM		return ENXIO;
39611225SDana.Myers@Sun.COM	}
39711225SDana.Myers@Sun.COM	viapm->st = rman_get_bustag(viapm->iores);
39811225SDana.Myers@Sun.COM	viapm->sh = rman_get_bushandle(viapm->iores);
39911225SDana.Myers@Sun.COM
40011225SDana.Myers@Sun.COM	VIAPM_OUTB(GPIO_DIR, VIAPM_INB(GPIO_DIR) | VIAPM_SCL | VIAPM_SDA);
40111225SDana.Myers@Sun.COM
40211225SDana.Myers@Sun.COM	/* add generic bit-banging code */
40311225SDana.Myers@Sun.COM	if (!(viapm->iicbb = device_add_child(dev, "iicbb", -1)))
40411225SDana.Myers@Sun.COM		goto error;
40511225SDana.Myers@Sun.COM
40611225SDana.Myers@Sun.COM	bus_generic_attach(dev);
40711225SDana.Myers@Sun.COM
4080SN/A	return 0;
40911225SDana.Myers@Sun.COM
41011225SDana.Myers@Sun.COMerror:
41111225SDana.Myers@Sun.COM	if (viapm->iores)
41211225SDana.Myers@Sun.COM		bus_release_resource(dev, SYS_RES_IOPORT,
41311225SDana.Myers@Sun.COM					viapm->iorid, viapm->iores);
41411225SDana.Myers@Sun.COM	return ENXIO;
41511225SDana.Myers@Sun.COM}
41611225SDana.Myers@Sun.COM
41711225SDana.Myers@Sun.COMstatic int
41811225SDana.Myers@Sun.COMviapm_586b_detach(device_t dev)
41911225SDana.Myers@Sun.COM{
42011225SDana.Myers@Sun.COM	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
42111225SDana.Myers@Sun.COM	int error;
42211225SDana.Myers@Sun.COM
42311225SDana.Myers@Sun.COM	bus_generic_detach(dev);
42411225SDana.Myers@Sun.COM	if (viapm->iicbb) {
42511225SDana.Myers@Sun.COM		device_delete_child(dev, viapm->iicbb);
42611225SDana.Myers@Sun.COM	}
42711225SDana.Myers@Sun.COM
42811225SDana.Myers@Sun.COM	if (viapm->iores && (error = bus_release_resource(dev, SYS_RES_IOPORT,
42911225SDana.Myers@Sun.COM						viapm->iorid, viapm->iores)))
43011225SDana.Myers@Sun.COM		return (error);
43111225SDana.Myers@Sun.COM
43211225SDana.Myers@Sun.COM	return 0;
43311225SDana.Myers@Sun.COM}
43411225SDana.Myers@Sun.COM
43511225SDana.Myers@Sun.COMstatic int
43611225SDana.Myers@Sun.COMviapm_pro_detach(device_t dev)
43711225SDana.Myers@Sun.COM{
43811225SDana.Myers@Sun.COM	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
43911225SDana.Myers@Sun.COM	int error;
44011225SDana.Myers@Sun.COM
44111225SDana.Myers@Sun.COM	bus_generic_detach(dev);
44211225SDana.Myers@Sun.COM	if (viapm->smbus) {
44311225SDana.Myers@Sun.COM		device_delete_child(dev, viapm->smbus);
44411225SDana.Myers@Sun.COM	}
44511225SDana.Myers@Sun.COM
44611225SDana.Myers@Sun.COM	if ((error = bus_release_resource(dev, SYS_RES_IOPORT,
44711225SDana.Myers@Sun.COM				viapm->iorid, viapm->iores)))
44811225SDana.Myers@Sun.COM		return (error);
4490SN/A
4500SN/A#if notyet
4510SN/A	if ((error = bus_release_resource(dev, SYS_RES_IRQ,
4527851SDana.Myers@Sun.COM					viapm->irqrid, viapm->irqres))
4537851SDana.Myers@Sun.COM		return (error);
4540SN/A#endif
4550SN/A
4560SN/A	return 0;
4570SN/A}
45811225SDana.Myers@Sun.COM
45911225SDana.Myers@Sun.COMstatic int
4600SN/Aviabb_callback(device_t dev, int index, caddr_t *data)
4610SN/A{
4620SN/A	return 0;
4630SN/A}
4640SN/A
4650SN/Astatic void
4660SN/Aviabb_setscl(device_t dev, int ctrl)
4670SN/A{
4680SN/A	struct viapm_softc *viapm = device_get_softc(dev);
469	u_char val;
470
471	val = VIAPM_INB(GPIO_VAL);
472
473	if (ctrl)
474		val |= VIAPM_SCL;
475	else
476		val &= ~VIAPM_SCL;
477
478	VIAPM_OUTB(GPIO_VAL, val);
479
480	return;
481}
482
483static void
484viabb_setsda(device_t dev, int data)
485{
486	struct viapm_softc *viapm = device_get_softc(dev);
487	u_char val;
488
489	val = VIAPM_INB(GPIO_VAL);
490
491	if (data)
492		val |= VIAPM_SDA;
493	else
494		val &= ~VIAPM_SDA;
495
496	VIAPM_OUTB(GPIO_VAL, val);
497
498	return;
499}
500
501static int
502viabb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
503{
504	/* reset bus */
505	viabb_setsda(dev, 1);
506	viabb_setscl(dev, 1);
507
508	return (IIC_ENOADDR);
509}
510
511static int
512viabb_getscl(device_t dev)
513{
514	struct viapm_softc *viapm = device_get_softc(dev);
515
516	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SCL) != 0);
517}
518
519static int
520viabb_getsda(device_t dev)
521{
522	struct viapm_softc *viapm = device_get_softc(dev);
523
524	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SDA) != 0);
525}
526
527static int
528viapm_abort(struct viapm_softc *viapm)
529{
530	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_KILL);
531	DELAY(10);
532
533	return (0);
534}
535
536static int
537viapm_clear(struct viapm_softc *viapm)
538{
539	VIAPM_OUTB(SMBHST, SMBHST_FAILED | SMBHST_COLLID |
540		SMBHST_ERROR | SMBHST_INTR);
541	DELAY(10);
542
543	return (0);
544}
545
546static int
547viapm_busy(struct viapm_softc *viapm)
548{
549	u_char sts;
550
551	sts = VIAPM_INB(SMBHST);
552
553	VIAPM_DEBUG(printf("viapm: idle? STS=0x%x\n", sts));
554
555	return (sts & SMBHST_BUSY);
556}
557
558/*
559 * Poll the SMBus controller
560 */
561static int
562viapm_wait(struct viapm_softc *viapm)
563{
564	int count = 10000;
565	u_char sts = 0;
566	int error;
567
568	/* wait for command to complete and SMBus controller is idle */
569	while(count--) {
570		DELAY(10);
571		sts = VIAPM_INB(SMBHST);
572
573		/* check if the controller is processing a command */
574		if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR))
575			break;
576	}
577
578	VIAPM_DEBUG(printf("viapm: SMBHST=0x%x\n", sts));
579
580	error = SMB_ENOERR;
581
582	if (!count)
583		error |= SMB_ETIMEOUT;
584
585	if (sts & SMBHST_FAILED)
586		error |= SMB_EABORT;
587
588	if (sts & SMBHST_COLLID)
589		error |= SMB_ENOACK;
590
591	if (sts & SMBHST_ERROR)
592		error |= SMB_EBUSERR;
593
594	if (error != SMB_ENOERR)
595		viapm_abort(viapm);
596
597	viapm_clear(viapm);
598
599	return (error);
600}
601
602static int
603viasmb_callback(device_t dev, int index, caddr_t *data)
604{
605	int error = 0;
606
607	switch (index) {
608	case SMB_REQUEST_BUS:
609	case SMB_RELEASE_BUS:
610		/* ok, bus allocation accepted */
611		break;
612	default:
613		error = EINVAL;
614	}
615
616	return (error);
617}
618
619static int
620viasmb_quick(device_t dev, u_char slave, int how)
621{
622	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
623	int error;
624
625	viapm_clear(viapm);
626	if (viapm_busy(viapm))
627		return (EBUSY);
628
629	switch (how) {
630	case SMB_QWRITE:
631		VIAPM_DEBUG(printf("viapm: QWRITE to 0x%x", slave));
632		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
633		break;
634	case SMB_QREAD:
635		VIAPM_DEBUG(printf("viapm: QREAD to 0x%x", slave));
636		VIAPM_OUTB(SMBHADDR, slave | LSB);
637		break;
638	default:
639		panic("%s: unknown QUICK command (%x)!", __FUNCTION__,
640			how);
641	}
642
643	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_QUICK);
644
645	error = viapm_wait(viapm);
646
647	return (error);
648}
649
650static int
651viasmb_sendb(device_t dev, u_char slave, char byte)
652{
653	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
654	int error;
655
656	viapm_clear(viapm);
657	if (viapm_busy(viapm))
658		return (EBUSY);
659
660	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
661	VIAPM_OUTB(SMBHCMD, byte);
662
663	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
664
665	error = viapm_wait(viapm);
666
667	VIAPM_DEBUG(printf("viapm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
668
669	return (error);
670}
671
672static int
673viasmb_recvb(device_t dev, u_char slave, char *byte)
674{
675	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
676	int error;
677
678	viapm_clear(viapm);
679	if (viapm_busy(viapm))
680		return (EBUSY);
681
682	VIAPM_OUTB(SMBHADDR, slave | LSB);
683
684	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
685
686	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
687		*byte = VIAPM_INB(SMBHDATA0);
688
689	VIAPM_DEBUG(printf("viapm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
690
691	return (error);
692}
693
694static int
695viasmb_writeb(device_t dev, u_char slave, char cmd, char byte)
696{
697	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
698	int error;
699
700	viapm_clear(viapm);
701	if (viapm_busy(viapm))
702		return (EBUSY);
703
704	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
705	VIAPM_OUTB(SMBHCMD, cmd);
706	VIAPM_OUTB(SMBHDATA0, byte);
707
708	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
709
710	error = viapm_wait(viapm);
711
712	VIAPM_DEBUG(printf("viapm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
713
714	return (error);
715}
716
717static int
718viasmb_readb(device_t dev, u_char slave, char cmd, char *byte)
719{
720	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
721	int error;
722
723	viapm_clear(viapm);
724	if (viapm_busy(viapm))
725		return (EBUSY);
726
727	VIAPM_OUTB(SMBHADDR, slave | LSB);
728	VIAPM_OUTB(SMBHCMD, cmd);
729
730	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
731
732	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
733		*byte = VIAPM_INB(SMBHDATA0);
734
735	VIAPM_DEBUG(printf("viapm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
736
737	return (error);
738}
739
740static int
741viasmb_writew(device_t dev, u_char slave, char cmd, short word)
742{
743	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
744	int error;
745
746	viapm_clear(viapm);
747	if (viapm_busy(viapm))
748		return (EBUSY);
749
750	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
751	VIAPM_OUTB(SMBHCMD, cmd);
752	VIAPM_OUTB(SMBHDATA0, word & 0x00ff);
753	VIAPM_OUTB(SMBHDATA1, (word & 0xff00) >> 8);
754
755	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
756
757	error = viapm_wait(viapm);
758
759	VIAPM_DEBUG(printf("viapm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
760
761	return (error);
762}
763
764static int
765viasmb_readw(device_t dev, u_char slave, char cmd, short *word)
766{
767	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
768	int error;
769	u_char high, low;
770
771	viapm_clear(viapm);
772	if (viapm_busy(viapm))
773		return (EBUSY);
774
775	VIAPM_OUTB(SMBHADDR, slave | LSB);
776	VIAPM_OUTB(SMBHCMD, cmd);
777
778	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
779
780	if ((error = viapm_wait(viapm)) == SMB_ENOERR) {
781		low = VIAPM_INB(SMBHDATA0);
782		high = VIAPM_INB(SMBHDATA1);
783
784		*word = ((high & 0xff) << 8) | (low & 0xff);
785	}
786
787	VIAPM_DEBUG(printf("viapm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
788
789	return (error);
790}
791
792static int
793viasmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
794{
795	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
796	u_char remain, len, i;
797	int error = SMB_ENOERR;
798
799	viapm_clear(viapm);
800	if (viapm_busy(viapm))
801		return (EBUSY);
802
803	remain = count;
804	while (remain) {
805		len = min(remain, 32);
806
807		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
808		VIAPM_OUTB(SMBHCMD, cmd);
809		VIAPM_OUTB(SMBHDATA0, len);
810		i = VIAPM_INB(SMBHCTRL);
811
812		/* fill the 32-byte internal buffer */
813		for (i=0; i<len; i++) {
814			VIAPM_OUTB(SMBHBLOCK, buf[count-remain+i]);
815			DELAY(2);
816		}
817		VIAPM_OUTB(SMBHCMD, cmd);
818		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
819
820		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
821			goto error;
822
823		remain -= len;
824	}
825
826error:
827	VIAPM_DEBUG(printf("viapm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
828
829	return (error);
830
831}
832
833static int
834viasmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
835{
836	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
837	u_char remain, len, i;
838	int error = SMB_ENOERR;
839
840	viapm_clear(viapm);
841	if (viapm_busy(viapm))
842		return (EBUSY);
843
844	remain = count;
845	while (remain) {
846		VIAPM_OUTB(SMBHADDR, slave | LSB);
847		VIAPM_OUTB(SMBHCMD, cmd);
848		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
849
850		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
851			goto error;
852
853		len = VIAPM_INB(SMBHDATA0);
854		i = VIAPM_INB(SMBHCTRL); 		/* reset counter */
855
856		len = min(len, remain);
857
858		/* read the 32-byte internal buffer */
859		for (i=0; i<len; i++) {
860			buf[count-remain+i] = VIAPM_INB(SMBHBLOCK);
861			DELAY(2);
862		}
863
864		remain -= len;
865	}
866error:
867	VIAPM_DEBUG(printf("viapm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
868
869	return (error);
870}
871
872static device_method_t viapm_methods[] = {
873	/* device interface */
874	DEVMETHOD(device_probe,		viapm_586b_probe),
875	DEVMETHOD(device_attach,	viapm_586b_attach),
876	DEVMETHOD(device_detach,	viapm_586b_detach),
877
878	/* iicbb interface */
879	DEVMETHOD(iicbb_callback,	viabb_callback),
880	DEVMETHOD(iicbb_setscl,		viabb_setscl),
881	DEVMETHOD(iicbb_setsda,		viabb_setsda),
882	DEVMETHOD(iicbb_getscl,		viabb_getscl),
883	DEVMETHOD(iicbb_getsda,		viabb_getsda),
884	DEVMETHOD(iicbb_reset,		viabb_reset),
885
886	{ 0, 0 }
887};
888
889static driver_t viapm_driver = {
890	"viapm",
891	viapm_methods,
892	sizeof(struct viapm_softc),
893};
894
895static device_method_t viapropm_methods[] = {
896	/* device interface */
897	DEVMETHOD(device_probe,		viapm_pro_probe),
898	DEVMETHOD(device_attach,	viapm_pro_attach),
899	DEVMETHOD(device_detach,	viapm_pro_detach),
900
901	/* smbus interface */
902	DEVMETHOD(smbus_callback,	viasmb_callback),
903	DEVMETHOD(smbus_quick,		viasmb_quick),
904	DEVMETHOD(smbus_sendb,		viasmb_sendb),
905	DEVMETHOD(smbus_recvb,		viasmb_recvb),
906	DEVMETHOD(smbus_writeb,		viasmb_writeb),
907	DEVMETHOD(smbus_readb,		viasmb_readb),
908	DEVMETHOD(smbus_writew,		viasmb_writew),
909	DEVMETHOD(smbus_readw,		viasmb_readw),
910	DEVMETHOD(smbus_bwrite,		viasmb_bwrite),
911	DEVMETHOD(smbus_bread,		viasmb_bread),
912
913	{ 0, 0 }
914};
915
916static driver_t viapropm_driver = {
917	"viapropm",
918	viapropm_methods,
919	sizeof(struct viapm_softc),
920};
921
922DRIVER_MODULE(viapm, pci, viapm_driver, viapm_devclass, 0, 0);
923DRIVER_MODULE(viapropm, pci, viapropm_driver, viapropm_devclass, 0, 0);
924
925MODULE_DEPEND(viapm, pci, 1, 1, 1);
926MODULE_DEPEND(viaprom, pci, 1, 1, 1);
927MODULE_DEPEND(viapm, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
928MODULE_DEPEND(viapropm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
929MODULE_VERSION(viapm, 1);
930