alpm.c revision 70606
143973Snsouch/*-
270606Snsouch * Copyright (c) 1998, 1999, 2001 Nicolas Souchu
343973Snsouch * All rights reserved.
443973Snsouch *
543973Snsouch * Redistribution and use in source and binary forms, with or without
643973Snsouch * modification, are permitted provided that the following conditions
743973Snsouch * are met:
843973Snsouch * 1. Redistributions of source code must retain the above copyright
943973Snsouch *    notice, this list of conditions and the following disclaimer.
1043973Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1143973Snsouch *    notice, this list of conditions and the following disclaimer in the
1243973Snsouch *    documentation and/or other materials provided with the distribution.
1343973Snsouch *
1443973Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1543973Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1643973Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1743973Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1843973Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1943973Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2043973Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2143973Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2243973Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2343973Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2443973Snsouch * SUCH DAMAGE.
2543973Snsouch *
2650477Speter * $FreeBSD: head/sys/pci/alpm.c 70606 2001-01-02 21:19:32Z nsouch $
2743973Snsouch *
2843973Snsouch */
2943973Snsouch
3043973Snsouch/*
3143973Snsouch * Power Management support for the Acer M15x3 chipsets
3243973Snsouch */
3343973Snsouch#include <sys/param.h>
3443973Snsouch#include <sys/kernel.h>
3543973Snsouch#include <sys/systm.h>
3643973Snsouch#include <sys/module.h>
3743973Snsouch#include <sys/bus.h>
3843973Snsouch#include <sys/uio.h>
3943973Snsouch
4043973Snsouch
4143973Snsouch#include <machine/bus_pio.h>
4243973Snsouch#include <machine/bus_memio.h>
4343973Snsouch#include <machine/bus.h>
4470606Snsouch#include <machine/resource.h>
4570606Snsouch#include <sys/rman.h>
4643973Snsouch
4743973Snsouch#include <pci/pcivar.h>
4843973Snsouch#include <pci/pcireg.h>
4943973Snsouch
5043973Snsouch#include <dev/iicbus/iiconf.h>
5143973Snsouch#include <dev/smbus/smbconf.h>
5243973Snsouch#include "smbus_if.h"
5343973Snsouch
5443973Snsouch#include "alpm.h"
5543973Snsouch
5643973Snsouch#define ALPM_DEBUG(x)	if (alpm_debug) (x)
5743973Snsouch
5843973Snsouch#ifdef DEBUG
5943973Snsouchstatic int alpm_debug = 1;
6043973Snsouch#else
6143973Snsouchstatic int alpm_debug = 0;
6243973Snsouch#endif
6343973Snsouch
6443973Snsouch#define ACER_M1543_PMU_ID	0x710110b9
6543973Snsouch
6643973Snsouch/* Uncomment this line to force another I/O base address for SMB */
6743973Snsouch/* #define ALPM_SMBIO_BASE_ADDR	0x3a80 */
6843973Snsouch
6943973Snsouch/* I/O registers offsets - the base address is programmed via the
7043973Snsouch * SMBBA PCI configuration register
7143973Snsouch */
7243973Snsouch#define SMBSTS		0x0	/* SMBus host/slave status register */
7343973Snsouch#define SMBCMD		0x1	/* SMBus host/slave command register */
7443973Snsouch#define SMBSTART	0x2	/* start to generate programmed cycle */
7543973Snsouch#define SMBHADDR	0x3	/* host address register */
7643973Snsouch#define SMBHDATA	0x4	/* data A register for host controller */
7743973Snsouch#define SMBHDATB	0x5	/* data B register for host controller */
7843973Snsouch#define SMBHBLOCK	0x6	/* block register for host controller */
7943973Snsouch#define SMBHCMD		0x7	/* command register for host controller */
8043973Snsouch
8143973Snsouch/* SMBSTS masks */
8243973Snsouch#define TERMINATE	0x80
8343973Snsouch#define BUS_COLLI	0x40
8443973Snsouch#define DEVICE_ERR	0x20
8543973Snsouch#define SMI_I_STS	0x10
8643973Snsouch#define HST_BSY		0x08
8743973Snsouch#define IDL_STS		0x04
8843973Snsouch#define HSTSLV_STS	0x02
8943973Snsouch#define HSTSLV_BSY	0x01
9043973Snsouch
9143973Snsouch/* SMBCMD masks */
9243973Snsouch#define SMB_BLK_CLR	0x80
9343973Snsouch#define T_OUT_CMD	0x08
9443973Snsouch#define ABORT_HOST	0x04
9543973Snsouch
9643973Snsouch/* SMBus commands */
9743973Snsouch#define SMBQUICK	0x00
9843973Snsouch#define SMBSRBYTE	0x10		/* send/receive byte */
9943973Snsouch#define SMBWRBYTE	0x20		/* write/read byte */
10043973Snsouch#define SMBWRWORD	0x30		/* write/read word */
10143973Snsouch#define SMBWRBLOCK	0x40		/* write/read block */
10243973Snsouch
10343973Snsouch/* PCI configuration registers and masks
10443973Snsouch */
10543973Snsouch#define COM		0x4
10643973Snsouch#define COM_ENABLE_IO	0x1
10743973Snsouch
10843973Snsouch#define SMBBA		0x14
10943973Snsouch
11043973Snsouch#define ATPC		0x5b
11170606Snsouch#define ATPC_SMBCTRL	0x04 		/* XX linux has this as 0x6 */
11243973Snsouch
11343973Snsouch#define SMBHSI		0xe0
11443973Snsouch#define SMBHSI_SLAVE	0x2
11543973Snsouch#define SMBHSI_HOST	0x1
11643973Snsouch
11743973Snsouch#define SMBHCBC		0xe2
11843973Snsouch#define SMBHCBC_CLOCK	0x70
11943973Snsouch
12043973Snsouch#define SMBCLOCK_149K	0x0
12143973Snsouch#define SMBCLOCK_74K	0x20
12243973Snsouch#define SMBCLOCK_37K	0x40
12343973Snsouch#define SMBCLOCK_223K	0x80
12443973Snsouch#define SMBCLOCK_111K	0xa0
12543973Snsouch#define SMBCLOCK_55K	0xc0
12643973Snsouch
12743973Snsouchstruct alpm_data {
12843973Snsouch	int base;
12943973Snsouch        bus_space_tag_t smbst;
13043973Snsouch        bus_space_handle_t smbsh;
13143973Snsouch};
13243973Snsouch
13343973Snsouchstruct alsmb_softc {
13443973Snsouch	int base;
13543973Snsouch	device_t smbus;
13643973Snsouch	struct alpm_data *alpm;
13743973Snsouch};
13843973Snsouch
13943973Snsouch#define ALPM_SMBINB(alsmb,register) \
14043973Snsouch	(bus_space_read_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register))
14143973Snsouch#define ALPM_SMBOUTB(alsmb,register,value) \
14243973Snsouch	(bus_space_write_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register, value))
14343973Snsouch
14443973Snsouchstatic int alsmb_probe(device_t);
14543973Snsouchstatic int alsmb_attach(device_t);
14643973Snsouchstatic int alsmb_smb_callback(device_t, int, caddr_t *);
14743973Snsouchstatic int alsmb_smb_quick(device_t dev, u_char slave, int how);
14843973Snsouchstatic int alsmb_smb_sendb(device_t dev, u_char slave, char byte);
14943973Snsouchstatic int alsmb_smb_recvb(device_t dev, u_char slave, char *byte);
15043973Snsouchstatic int alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte);
15143973Snsouchstatic int alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte);
15243973Snsouchstatic int alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word);
15343973Snsouchstatic int alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word);
15443973Snsouchstatic int alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
15543973Snsouchstatic int alsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *byte);
15643973Snsouch
15743973Snsouchstatic devclass_t alsmb_devclass;
15843973Snsouch
15943973Snsouchstatic device_method_t alsmb_methods[] = {
16043973Snsouch	/* device interface */
16143973Snsouch	DEVMETHOD(device_probe,		alsmb_probe),
16243973Snsouch	DEVMETHOD(device_attach,	alsmb_attach),
16343973Snsouch
16443973Snsouch	/* bus interface */
16549195Smdodd	DEVMETHOD(bus_print_child,	bus_generic_print_child),
16643973Snsouch
16743973Snsouch	/* smbus interface */
16843973Snsouch	DEVMETHOD(smbus_callback,	alsmb_smb_callback),
16943973Snsouch	DEVMETHOD(smbus_quick,		alsmb_smb_quick),
17043973Snsouch	DEVMETHOD(smbus_sendb,		alsmb_smb_sendb),
17143973Snsouch	DEVMETHOD(smbus_recvb,		alsmb_smb_recvb),
17243973Snsouch	DEVMETHOD(smbus_writeb,		alsmb_smb_writeb),
17343973Snsouch	DEVMETHOD(smbus_readb,		alsmb_smb_readb),
17443973Snsouch	DEVMETHOD(smbus_writew,		alsmb_smb_writew),
17543973Snsouch	DEVMETHOD(smbus_readw,		alsmb_smb_readw),
17643973Snsouch	DEVMETHOD(smbus_bwrite,		alsmb_smb_bwrite),
17743973Snsouch	DEVMETHOD(smbus_bread,		alsmb_smb_bread),
17843973Snsouch
17943973Snsouch	{ 0, 0 }
18043973Snsouch};
18143973Snsouch
18243973Snsouchstatic driver_t alsmb_driver = {
18343973Snsouch	"alsmb",
18443973Snsouch	alsmb_methods,
18543973Snsouch	sizeof(struct alsmb_softc),
18643973Snsouch};
18743973Snsouch
18870606Snsouchstatic int alpm_pci_probe(device_t dev);
18970606Snsouchstatic int alpm_pci_attach(device_t dev);
19043973Snsouch
19170606Snsouchstatic devclass_t alpm_devclass;
19243973Snsouch
19370606Snsouchstatic device_method_t alpm_pci_methods[] = {
19470606Snsouch	/* device interface */
19570606Snsouch	DEVMETHOD(device_probe,		alpm_pci_probe),
19670606Snsouch	DEVMETHOD(device_attach,	alpm_pci_attach),
19770606Snsouch
19870606Snsouch	{ 0, 0 }
19970606Snsouch};
20070606Snsouch
20170606Snsouchstatic driver_t alpm_pci_driver = {
20243973Snsouch	"alpm",
20370606Snsouch	alpm_pci_methods,
20470606Snsouch	sizeof(struct alpm_data)
20543973Snsouch};
20643973Snsouch
20743973Snsouch
20870606Snsouchstatic int
20970606Snsouchalpm_pci_probe(device_t dev)
21043973Snsouch{
21170606Snsouch	if (pci_get_devid(dev) == ACER_M1543_PMU_ID) {
21270606Snsouch		device_set_desc(dev,
21370606Snsouch		    "AcerLabs M15x3 Power Management Unit");
21470606Snsouch		return 0;
21570606Snsouch	} else {
21670606Snsouch		return ENXIO;
21770606Snsouch	}
21843973Snsouch}
21943973Snsouch
22070606Snsouchstatic int
22170606Snsouchalpm_pci_attach(device_t dev)
22243973Snsouch{
22370606Snsouch	int rid, unit;
22470606Snsouch	u_int32_t l;
22543973Snsouch	struct alpm_data *alpm;
22670606Snsouch	struct resource *res;
22770606Snsouch	device_t smbinterface;
22843973Snsouch
22970606Snsouch	alpm = device_get_softc(dev);
23070606Snsouch	unit = device_get_unit(dev);
23143973Snsouch
23243973Snsouch	/* Unlock SMBIO base register access */
23370606Snsouch	l = pci_read_config(dev, ATPC, 1);
23470606Snsouch	pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1);
23543973Snsouch
23670606Snsouch	/*
23770606Snsouch	 * XX linux sets clock to 74k, should we?
23870606Snsouch	l = pci_read_config(dev, SMBHCBC, 1);
23970606Snsouch	l &= 0x1f;
24070606Snsouch	l |= SMBCLOCK_74K;
24170606Snsouch	pci_write_config(dev, SMBHCBC, l, 1);
24270606Snsouch	 */
24370606Snsouch
24443973Snsouch	if (bootverbose) {
24570606Snsouch		l = pci_read_config(dev, SMBHSI, 1);
24643973Snsouch		printf("alsmb%d: %s/%s", unit,
24743973Snsouch			(l & SMBHSI_HOST) ? "host":"nohost",
24843973Snsouch			(l & SMBHSI_SLAVE) ? "slave":"noslave");
24943973Snsouch
25070606Snsouch		l = pci_read_config(dev, SMBHCBC, 1);
25143973Snsouch		switch (l & SMBHCBC_CLOCK) {
25243973Snsouch		case SMBCLOCK_149K:
25343973Snsouch			printf(" 149K");
25443973Snsouch			break;
25543973Snsouch		case SMBCLOCK_74K:
25643973Snsouch			printf(" 74K");
25743973Snsouch			break;
25843973Snsouch		case SMBCLOCK_37K:
25943973Snsouch			printf(" 37K");
26043973Snsouch			break;
26143973Snsouch		case SMBCLOCK_223K:
26243973Snsouch			printf(" 223K");
26343973Snsouch			break;
26443973Snsouch		case SMBCLOCK_111K:
26543973Snsouch			printf(" 111K");
26643973Snsouch			break;
26743973Snsouch		case SMBCLOCK_55K:
26843973Snsouch			printf(" 55K");
26943973Snsouch			break;
27043973Snsouch		}
27143973Snsouch	}
27243973Snsouch
27343973Snsouch#ifdef ALPM_SMBIO_BASE_ADDR
27470606Snsouch	/* XX will this even work anymore? */
27543973Snsouch	/* disable I/O */
27670606Snsouch	l = pci_read_config(dev, COM, 2);
27770606Snsouch	pci_write_config(dev, COM, l & ~COM_ENABLE_IO, 2);
27843973Snsouch
27943973Snsouch	/* set the I/O base address */
28070606Snsouch	pci_write_config(dev, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4);
28143973Snsouch
28243973Snsouch	/* enable I/O */
28370606Snsouch	pci_write_config(dev, COM, l | COM_ENABLE_IO, 2);
28443973Snsouch
28543973Snsouch#endif
28670606Snsouch	rid = SMBBA;
28770606Snsouch	res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
28870606Snsouch	    0,~0,1,RF_ACTIVE);
28970606Snsouch	if (res == NULL) {
29070606Snsouch		device_printf(dev,"Could not allocate Bus space\n");
29170606Snsouch		return ENXIO;
29270606Snsouch	}
29370606Snsouch	alpm->smbst = rman_get_bustag(res);
29470606Snsouch	alpm->smbsh = rman_get_bushandle(res);
29570606Snsouch
29643973Snsouch	if (bootverbose)
29743973Snsouch		printf(" at 0x%x\n", alpm->smbsh);
29870606Snsouch
29970606Snsouch	smbinterface = device_add_child(dev, "alsmb", unit);
30070606Snsouch	if (!smbinterface)
30170606Snsouch		device_printf(dev, "could not add SMBus device\n");
30270606Snsouch	else
30370606Snsouch		device_probe_and_attach(smbinterface);
30470606Snsouch	return 0;
30543973Snsouch}
30643973Snsouch
30743973Snsouch/*
30843973Snsouch * Not a real probe, we know the device exists since the device has
30943973Snsouch * been added after the successfull pci probe.
31043973Snsouch */
31143973Snsouchstatic int
31243973Snsouchalsmb_probe(device_t dev)
31343973Snsouch{
31443973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
31543973Snsouch
31670606Snsouch	/* allocate a new smbus device */
31770606Snsouch	sc->smbus = smbus_alloc_bus(dev);
31870606Snsouch	if (!sc->smbus)
31970606Snsouch		return (EINVAL);
32043973Snsouch	device_set_desc(dev, "Aladdin IV/V/Pro2 SMBus controller");
32143973Snsouch
32243973Snsouch	return (0);
32343973Snsouch}
32443973Snsouch
32543973Snsouchstatic int
32643973Snsouchalsmb_attach(device_t dev)
32743973Snsouch{
32843973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
32943973Snsouch
33070606Snsouch	sc->alpm = device_get_softc(device_get_parent(dev));
33143973Snsouch
33243973Snsouch	/* probe and attach the smbus */
33343973Snsouch	device_probe_and_attach(sc->smbus);
33443973Snsouch
33543973Snsouch	return (0);
33643973Snsouch}
33743973Snsouch
33843973Snsouchstatic int
33943973Snsouchalsmb_smb_callback(device_t dev, int index, caddr_t *data)
34043973Snsouch{
34143973Snsouch	int error = 0;
34243973Snsouch
34343973Snsouch	switch (index) {
34443973Snsouch	case SMB_REQUEST_BUS:
34543973Snsouch	case SMB_RELEASE_BUS:
34643973Snsouch		/* ok, bus allocation accepted */
34743973Snsouch		break;
34843973Snsouch	default:
34943973Snsouch		error = EINVAL;
35043973Snsouch	}
35143973Snsouch
35243973Snsouch	return (error);
35343973Snsouch}
35443973Snsouch
35543973Snsouchstatic int
35643973Snsouchalsmb_clear(struct alsmb_softc *sc)
35743973Snsouch{
35843973Snsouch	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
35943973Snsouch	DELAY(10);
36043973Snsouch
36143973Snsouch	return (0);
36243973Snsouch}
36343973Snsouch
36443973Snsouch#if 0
36543973Snsouchstatic int
36643973Snsouchalsmb_abort(struct alsmb_softc *sc)
36743973Snsouch{
36843973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
36943973Snsouch
37043973Snsouch	return (0);
37143973Snsouch}
37243973Snsouch#endif
37343973Snsouch
37443973Snsouchstatic int
37543973Snsouchalsmb_idle(struct alsmb_softc *sc)
37643973Snsouch{
37743973Snsouch	u_char sts;
37843973Snsouch
37943973Snsouch	sts = ALPM_SMBINB(sc, SMBSTS);
38043973Snsouch
38143973Snsouch	ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts));
38243973Snsouch
38343973Snsouch	return (sts & IDL_STS);
38443973Snsouch}
38543973Snsouch
38643973Snsouch/*
38743973Snsouch * Poll the SMBus controller
38843973Snsouch */
38943973Snsouchstatic int
39043973Snsouchalsmb_wait(struct alsmb_softc *sc)
39143973Snsouch{
39243973Snsouch	int count = 10000;
39370606Snsouch	u_char sts = 0;
39443973Snsouch	int error;
39543973Snsouch
39643973Snsouch	/* wait for command to complete and SMBus controller is idle */
39743973Snsouch	while(count--) {
39843973Snsouch		DELAY(10);
39943973Snsouch		sts = ALPM_SMBINB(sc, SMBSTS);
40043973Snsouch		if (sts & SMI_I_STS)
40143973Snsouch			break;
40243973Snsouch	}
40343973Snsouch
40443973Snsouch	ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts));
40543973Snsouch
40643973Snsouch	error = SMB_ENOERR;
40743973Snsouch
40843973Snsouch	if (!count)
40943973Snsouch		error |= SMB_ETIMEOUT;
41043973Snsouch
41143973Snsouch	if (sts & TERMINATE)
41243973Snsouch		error |= SMB_EABORT;
41343973Snsouch
41443973Snsouch	if (sts & BUS_COLLI)
41543973Snsouch		error |= SMB_ENOACK;
41643973Snsouch
41743973Snsouch	if (sts & DEVICE_ERR)
41843973Snsouch		error |= SMB_EBUSERR;
41943973Snsouch
42043973Snsouch	if (error != SMB_ENOERR)
42143973Snsouch		alsmb_clear(sc);
42243973Snsouch
42343973Snsouch	return (error);
42443973Snsouch}
42543973Snsouch
42643973Snsouchstatic int
42743973Snsouchalsmb_smb_quick(device_t dev, u_char slave, int how)
42843973Snsouch{
42943973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
43043973Snsouch	int error;
43143973Snsouch
43243973Snsouch	alsmb_clear(sc);
43343973Snsouch	if (!alsmb_idle(sc))
43443973Snsouch		return (EBUSY);
43543973Snsouch
43643973Snsouch	switch (how) {
43743973Snsouch	case SMB_QWRITE:
43843973Snsouch		ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave));
43943973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
44043973Snsouch		break;
44143973Snsouch	case SMB_QREAD:
44243973Snsouch		ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave));
44343973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
44443973Snsouch		break;
44543973Snsouch	default:
44643973Snsouch		panic("%s: unknown QUICK command (%x)!", __FUNCTION__,
44743973Snsouch			how);
44843973Snsouch	}
44943973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
45043973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
45143973Snsouch
45243973Snsouch	error = alsmb_wait(sc);
45343973Snsouch
45443973Snsouch	ALPM_DEBUG(printf(", error=0x%x\n", error));
45543973Snsouch
45643973Snsouch	return (error);
45743973Snsouch}
45843973Snsouch
45943973Snsouchstatic int
46043973Snsouchalsmb_smb_sendb(device_t dev, u_char slave, char byte)
46143973Snsouch{
46243973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
46343973Snsouch	int error;
46443973Snsouch
46543973Snsouch	alsmb_clear(sc);
46643973Snsouch	if (!alsmb_idle(sc))
46743973Snsouch		return (SMB_EBUSY);
46843973Snsouch
46943973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
47043973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
47143973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
47243973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
47343973Snsouch
47443973Snsouch	error = alsmb_wait(sc);
47543973Snsouch
47643973Snsouch	ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
47743973Snsouch
47843973Snsouch	return (error);
47943973Snsouch}
48043973Snsouch
48143973Snsouchstatic int
48243973Snsouchalsmb_smb_recvb(device_t dev, u_char slave, char *byte)
48343973Snsouch{
48443973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
48543973Snsouch	int error;
48643973Snsouch
48743973Snsouch	alsmb_clear(sc);
48843973Snsouch	if (!alsmb_idle(sc))
48943973Snsouch		return (SMB_EBUSY);
49043973Snsouch
49143973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
49243973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
49343973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
49443973Snsouch
49543973Snsouch	if ((error = alsmb_wait(sc)) == SMB_ENOERR)
49643973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
49743973Snsouch
49843973Snsouch	ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
49943973Snsouch
50043973Snsouch	return (error);
50143973Snsouch}
50243973Snsouch
50343973Snsouchstatic int
50443973Snsouchalsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte)
50543973Snsouch{
50643973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
50743973Snsouch	int error;
50843973Snsouch
50943973Snsouch	alsmb_clear(sc);
51043973Snsouch	if (!alsmb_idle(sc))
51143973Snsouch		return (SMB_EBUSY);
51243973Snsouch
51343973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
51443973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
51543973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
51643973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
51743973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
51843973Snsouch
51943973Snsouch	error = alsmb_wait(sc);
52043973Snsouch
52143973Snsouch	ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
52243973Snsouch
52343973Snsouch	return (error);
52443973Snsouch}
52543973Snsouch
52643973Snsouchstatic int
52743973Snsouchalsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte)
52843973Snsouch{
52943973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
53043973Snsouch	int error;
53143973Snsouch
53243973Snsouch	alsmb_clear(sc);
53343973Snsouch	if (!alsmb_idle(sc))
53443973Snsouch		return (SMB_EBUSY);
53543973Snsouch
53643973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
53743973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
53843973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
53943973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
54043973Snsouch
54143973Snsouch	if ((error = alsmb_wait(sc)) == SMB_ENOERR)
54243973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
54343973Snsouch
54443973Snsouch	ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
54543973Snsouch
54643973Snsouch	return (error);
54743973Snsouch}
54843973Snsouch
54943973Snsouchstatic int
55043973Snsouchalsmb_smb_writew(device_t dev, u_char slave, char cmd, short word)
55143973Snsouch{
55243973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
55343973Snsouch	int error;
55443973Snsouch
55543973Snsouch	alsmb_clear(sc);
55643973Snsouch	if (!alsmb_idle(sc))
55743973Snsouch		return (SMB_EBUSY);
55843973Snsouch
55943973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
56043973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
56143973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
56243973Snsouch	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
56343973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
56443973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
56543973Snsouch
56643973Snsouch	error = alsmb_wait(sc);
56743973Snsouch
56843973Snsouch	ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
56943973Snsouch
57043973Snsouch	return (error);
57143973Snsouch}
57243973Snsouch
57343973Snsouchstatic int
57443973Snsouchalsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word)
57543973Snsouch{
57643973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
57743973Snsouch	int error;
57843973Snsouch	u_char high, low;
57943973Snsouch
58043973Snsouch	alsmb_clear(sc);
58143973Snsouch	if (!alsmb_idle(sc))
58243973Snsouch		return (SMB_EBUSY);
58343973Snsouch
58443973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
58543973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
58643973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
58743973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
58843973Snsouch
58943973Snsouch	if ((error = alsmb_wait(sc)) == SMB_ENOERR) {
59043973Snsouch		low = ALPM_SMBINB(sc, SMBHDATA);
59143973Snsouch		high = ALPM_SMBINB(sc, SMBHDATB);
59243973Snsouch
59343973Snsouch		*word = ((high & 0xff) << 8) | (low & 0xff);
59443973Snsouch	}
59543973Snsouch
59643973Snsouch	ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
59743973Snsouch
59843973Snsouch	return (error);
59943973Snsouch}
60043973Snsouch
60143973Snsouchstatic int
60243973Snsouchalsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
60343973Snsouch{
60443973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
60543973Snsouch	u_char remain, len, i;
60643973Snsouch	int error = SMB_ENOERR;
60743973Snsouch
60843973Snsouch	alsmb_clear(sc);
60943973Snsouch	if(!alsmb_idle(sc))
61043973Snsouch		return (SMB_EBUSY);
61143973Snsouch
61243973Snsouch	remain = count;
61343973Snsouch	while (remain) {
61443973Snsouch		len = min(remain, 32);
61543973Snsouch
61643973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
61743973Snsouch
61843973Snsouch		/* set the cmd and reset the
61943973Snsouch		 * 32-byte long internal buffer */
62043973Snsouch		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
62143973Snsouch
62243973Snsouch		ALPM_SMBOUTB(sc, SMBHDATA, len);
62343973Snsouch
62443973Snsouch		/* fill the 32-byte internal buffer */
62543973Snsouch		for (i=0; i<len; i++) {
62643973Snsouch			ALPM_SMBOUTB(sc, SMBHBLOCK, buf[count-remain+i]);
62743973Snsouch			DELAY(2);
62843973Snsouch		}
62943973Snsouch		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
63043973Snsouch		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
63143973Snsouch
63243973Snsouch		if ((error = alsmb_wait(sc)) != SMB_ENOERR)
63343973Snsouch			goto error;
63443973Snsouch
63543973Snsouch		remain -= len;
63643973Snsouch	}
63743973Snsouch
63843973Snsoucherror:
63943973Snsouch	ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
64043973Snsouch
64143973Snsouch	return (error);
64243973Snsouch}
64343973Snsouch
64443973Snsouchstatic int
64543973Snsouchalsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
64643973Snsouch{
64743973Snsouch	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
64843973Snsouch	u_char remain, len, i;
64943973Snsouch	int error = SMB_ENOERR;
65043973Snsouch
65143973Snsouch	alsmb_clear(sc);
65243973Snsouch	if (!alsmb_idle(sc))
65343973Snsouch		return (SMB_EBUSY);
65443973Snsouch
65543973Snsouch	remain = count;
65643973Snsouch	while (remain) {
65743973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
65843973Snsouch
65943973Snsouch		/* set the cmd and reset the
66043973Snsouch		 * 32-byte long internal buffer */
66143973Snsouch		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
66243973Snsouch
66343973Snsouch		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
66443973Snsouch		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
66543973Snsouch
66643973Snsouch		if ((error = alsmb_wait(sc)) != SMB_ENOERR)
66743973Snsouch			goto error;
66843973Snsouch
66943973Snsouch		len = ALPM_SMBINB(sc, SMBHDATA);
67043973Snsouch
67143973Snsouch		/* read the 32-byte internal buffer */
67243973Snsouch		for (i=0; i<len; i++) {
67343973Snsouch			buf[count-remain+i] = ALPM_SMBINB(sc, SMBHBLOCK);
67443973Snsouch			DELAY(2);
67543973Snsouch		}
67643973Snsouch
67743973Snsouch		remain -= len;
67843973Snsouch	}
67943973Snsoucherror:
68043973Snsouch	ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
68143973Snsouch
68243973Snsouch	return (error);
68343973Snsouch}
68443973Snsouch
68570606SnsouchDRIVER_MODULE(alpm, pci, alpm_pci_driver, alpm_devclass, 0, 0);
68670606SnsouchDRIVER_MODULE(alsmb, alpm, alsmb_driver, alsmb_devclass, 0, 0);
687