alpm.c revision 162234
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 */ 2643973Snsouch 2743973Snsouch/* 2843973Snsouch * Power Management support for the Acer M15x3 chipsets 2943973Snsouch */ 30116192Sobrien 31116192Sobrien#include <sys/cdefs.h> 32116192Sobrien__FBSDID("$FreeBSD: head/sys/pci/alpm.c 162234 2006-09-11 20:52:41Z jhb $"); 33116192Sobrien 3443973Snsouch#include <sys/param.h> 3543973Snsouch#include <sys/kernel.h> 3643973Snsouch#include <sys/systm.h> 3743973Snsouch#include <sys/module.h> 3843973Snsouch#include <sys/bus.h> 3943973Snsouch#include <sys/uio.h> 4043973Snsouch 4143973Snsouch#include <machine/bus.h> 4270606Snsouch#include <machine/resource.h> 4370606Snsouch#include <sys/rman.h> 4443973Snsouch 45119288Simp#include <dev/pci/pcivar.h> 46119288Simp#include <dev/pci/pcireg.h> 4743973Snsouch 4843973Snsouch#include <dev/iicbus/iiconf.h> 4943973Snsouch#include <dev/smbus/smbconf.h> 5043973Snsouch#include "smbus_if.h" 5143973Snsouch 5243973Snsouch#define ALPM_DEBUG(x) if (alpm_debug) (x) 5343973Snsouch 5443973Snsouch#ifdef DEBUG 5543973Snsouchstatic int alpm_debug = 1; 5643973Snsouch#else 5743973Snsouchstatic int alpm_debug = 0; 5843973Snsouch#endif 5943973Snsouch 6043973Snsouch#define ACER_M1543_PMU_ID 0x710110b9 6143973Snsouch 6243973Snsouch/* Uncomment this line to force another I/O base address for SMB */ 6343973Snsouch/* #define ALPM_SMBIO_BASE_ADDR 0x3a80 */ 6443973Snsouch 6543973Snsouch/* I/O registers offsets - the base address is programmed via the 6643973Snsouch * SMBBA PCI configuration register 6743973Snsouch */ 6843973Snsouch#define SMBSTS 0x0 /* SMBus host/slave status register */ 6943973Snsouch#define SMBCMD 0x1 /* SMBus host/slave command register */ 7043973Snsouch#define SMBSTART 0x2 /* start to generate programmed cycle */ 7143973Snsouch#define SMBHADDR 0x3 /* host address register */ 7243973Snsouch#define SMBHDATA 0x4 /* data A register for host controller */ 7343973Snsouch#define SMBHDATB 0x5 /* data B register for host controller */ 7443973Snsouch#define SMBHBLOCK 0x6 /* block register for host controller */ 7543973Snsouch#define SMBHCMD 0x7 /* command register for host controller */ 7643973Snsouch 7743973Snsouch/* SMBSTS masks */ 7843973Snsouch#define TERMINATE 0x80 7943973Snsouch#define BUS_COLLI 0x40 8043973Snsouch#define DEVICE_ERR 0x20 8143973Snsouch#define SMI_I_STS 0x10 8243973Snsouch#define HST_BSY 0x08 8343973Snsouch#define IDL_STS 0x04 8443973Snsouch#define HSTSLV_STS 0x02 8543973Snsouch#define HSTSLV_BSY 0x01 8643973Snsouch 8743973Snsouch/* SMBCMD masks */ 8843973Snsouch#define SMB_BLK_CLR 0x80 8943973Snsouch#define T_OUT_CMD 0x08 9043973Snsouch#define ABORT_HOST 0x04 9143973Snsouch 9243973Snsouch/* SMBus commands */ 9343973Snsouch#define SMBQUICK 0x00 9443973Snsouch#define SMBSRBYTE 0x10 /* send/receive byte */ 9543973Snsouch#define SMBWRBYTE 0x20 /* write/read byte */ 9643973Snsouch#define SMBWRWORD 0x30 /* write/read word */ 9743973Snsouch#define SMBWRBLOCK 0x40 /* write/read block */ 9843973Snsouch 9943973Snsouch/* PCI configuration registers and masks 10043973Snsouch */ 10143973Snsouch#define COM 0x4 10243973Snsouch#define COM_ENABLE_IO 0x1 10343973Snsouch 10443973Snsouch#define SMBBA 0x14 10543973Snsouch 10643973Snsouch#define ATPC 0x5b 10770606Snsouch#define ATPC_SMBCTRL 0x04 /* XX linux has this as 0x6 */ 10843973Snsouch 10943973Snsouch#define SMBHSI 0xe0 11043973Snsouch#define SMBHSI_SLAVE 0x2 11143973Snsouch#define SMBHSI_HOST 0x1 11243973Snsouch 11343973Snsouch#define SMBHCBC 0xe2 11443973Snsouch#define SMBHCBC_CLOCK 0x70 11543973Snsouch 11643973Snsouch#define SMBCLOCK_149K 0x0 11743973Snsouch#define SMBCLOCK_74K 0x20 11843973Snsouch#define SMBCLOCK_37K 0x40 11943973Snsouch#define SMBCLOCK_223K 0x80 12043973Snsouch#define SMBCLOCK_111K 0xa0 12143973Snsouch#define SMBCLOCK_55K 0xc0 12243973Snsouch 12393023Snsouchstruct alpm_softc { 12443973Snsouch int base; 12593023Snsouch struct resource *res; 12643973Snsouch bus_space_tag_t smbst; 12743973Snsouch bus_space_handle_t smbsh; 12843973Snsouch device_t smbus; 12943973Snsouch}; 13043973Snsouch 13193023Snsouch#define ALPM_SMBINB(alpm,register) \ 13293023Snsouch (bus_space_read_1(alpm->smbst, alpm->smbsh, register)) 13393023Snsouch#define ALPM_SMBOUTB(alpm,register,value) \ 13493023Snsouch (bus_space_write_1(alpm->smbst, alpm->smbsh, register, value)) 13543973Snsouch 13693023Snsouchstatic int 13793023Snsouchalpm_probe(device_t dev) 13893023Snsouch{ 13993023Snsouch#ifdef ALPM_SMBIO_BASE_ADDR 14093023Snsouch u_int32_t l; 14193023Snsouch#endif 14243973Snsouch 14393023Snsouch if (pci_get_devid(dev) == ACER_M1543_PMU_ID) { 14493023Snsouch device_set_desc(dev, "AcerLabs M15x3 Power Management Unit"); 14543973Snsouch 14693023Snsouch#ifdef ALPM_SMBIO_BASE_ADDR 14793023Snsouch if (bootverbose || alpm_debug) 14893023Snsouch device_printf(dev, "forcing base I/O at 0x%x\n", 14993023Snsouch ALPM_SMBIO_BASE_ADDR); 15043973Snsouch 15193023Snsouch /* disable I/O */ 15293023Snsouch l = pci_read_config(dev, COM, 2); 15393023Snsouch pci_write_config(dev, COM, l & ~COM_ENABLE_IO, 2); 15443973Snsouch 15593023Snsouch /* set the I/O base address */ 15693023Snsouch pci_write_config(dev, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4); 15743973Snsouch 15893023Snsouch /* enable I/O */ 15993023Snsouch pci_write_config(dev, COM, l | COM_ENABLE_IO, 2); 16043973Snsouch 16193023Snsouch if (bus_set_resource(dev, SYS_RES_IOPORT, SMBBA, 16293023Snsouch ALPM_SMBIO_BASE_ADDR, 256)) { 16393023Snsouch device_printf(dev, "could not set bus resource\n"); 16493023Snsouch return (ENXIO); 16593023Snsouch } 16693023Snsouch#endif 167142398Simp return (BUS_PROBE_DEFAULT); 16893023Snsouch } 16943973Snsouch 17093023Snsouch return (ENXIO); 17143973Snsouch} 17243973Snsouch 17370606Snsouchstatic int 17493023Snsouchalpm_attach(device_t dev) 17543973Snsouch{ 176115532Sphk int rid; 17770606Snsouch u_int32_t l; 17893023Snsouch struct alpm_softc *alpm; 17943973Snsouch 18070606Snsouch alpm = device_get_softc(dev); 18143973Snsouch 18243973Snsouch /* Unlock SMBIO base register access */ 18370606Snsouch l = pci_read_config(dev, ATPC, 1); 18470606Snsouch pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1); 18543973Snsouch 18670606Snsouch /* 18770606Snsouch * XX linux sets clock to 74k, should we? 18870606Snsouch l = pci_read_config(dev, SMBHCBC, 1); 18970606Snsouch l &= 0x1f; 19070606Snsouch l |= SMBCLOCK_74K; 19170606Snsouch pci_write_config(dev, SMBHCBC, l, 1); 19270606Snsouch */ 19370606Snsouch 19493023Snsouch if (bootverbose || alpm_debug) { 19570606Snsouch l = pci_read_config(dev, SMBHSI, 1); 19693023Snsouch device_printf(dev, "%s/%s", 19743973Snsouch (l & SMBHSI_HOST) ? "host":"nohost", 19843973Snsouch (l & SMBHSI_SLAVE) ? "slave":"noslave"); 19943973Snsouch 20070606Snsouch l = pci_read_config(dev, SMBHCBC, 1); 20143973Snsouch switch (l & SMBHCBC_CLOCK) { 20243973Snsouch case SMBCLOCK_149K: 20343973Snsouch printf(" 149K"); 20443973Snsouch break; 20543973Snsouch case SMBCLOCK_74K: 20643973Snsouch printf(" 74K"); 20743973Snsouch break; 20843973Snsouch case SMBCLOCK_37K: 20943973Snsouch printf(" 37K"); 21043973Snsouch break; 21143973Snsouch case SMBCLOCK_223K: 21243973Snsouch printf(" 223K"); 21343973Snsouch break; 21443973Snsouch case SMBCLOCK_111K: 21543973Snsouch printf(" 111K"); 21643973Snsouch break; 21743973Snsouch case SMBCLOCK_55K: 21843973Snsouch printf(" 55K"); 21943973Snsouch break; 22093023Snsouch default: 22193023Snsouch printf("unkown"); 22293023Snsouch break; 22343973Snsouch } 22493023Snsouch printf("\n"); 22543973Snsouch } 22643973Snsouch 22793023Snsouch rid = SMBBA; 228127135Snjl alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 229127135Snjl RF_ACTIVE); 23043973Snsouch 23193023Snsouch if (alpm->res == NULL) { 23270606Snsouch device_printf(dev,"Could not allocate Bus space\n"); 23393023Snsouch return (ENXIO); 23470606Snsouch } 23593023Snsouch alpm->smbst = rman_get_bustag(alpm->res); 23693023Snsouch alpm->smbsh = rman_get_bushandle(alpm->res); 23770606Snsouch 23893023Snsouch /* attach the smbus */ 23993023Snsouch alpm->smbus = device_add_child(dev, "smbus", -1); 24093023Snsouch bus_generic_attach(dev); 24143973Snsouch 24243973Snsouch return (0); 24343973Snsouch} 24443973Snsouch 24543973Snsouchstatic int 24693023Snsouchalpm_detach(device_t dev) 24743973Snsouch{ 24893023Snsouch struct alpm_softc *alpm = device_get_softc(dev); 24943973Snsouch 25093023Snsouch if (alpm->smbus) { 25193023Snsouch device_delete_child(dev, alpm->smbus); 25293023Snsouch alpm->smbus = NULL; 25393023Snsouch } 25443973Snsouch 25593023Snsouch if (alpm->res) 25693023Snsouch bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res); 25743973Snsouch 25843973Snsouch return (0); 25943973Snsouch} 26043973Snsouch 26143973Snsouchstatic int 262162234Sjhbalpm_callback(device_t dev, int index, void *data) 26343973Snsouch{ 26443973Snsouch int error = 0; 26543973Snsouch 26643973Snsouch switch (index) { 26743973Snsouch case SMB_REQUEST_BUS: 26843973Snsouch case SMB_RELEASE_BUS: 26943973Snsouch /* ok, bus allocation accepted */ 27043973Snsouch break; 27143973Snsouch default: 27243973Snsouch error = EINVAL; 27343973Snsouch } 27443973Snsouch 27543973Snsouch return (error); 27643973Snsouch} 27743973Snsouch 27843973Snsouchstatic int 27993023Snsouchalpm_clear(struct alpm_softc *sc) 28043973Snsouch{ 28143973Snsouch ALPM_SMBOUTB(sc, SMBSTS, 0xff); 28243973Snsouch DELAY(10); 28343973Snsouch 28443973Snsouch return (0); 28543973Snsouch} 28643973Snsouch 28743973Snsouch#if 0 28843973Snsouchstatic int 28993023Snsouchalpm_abort(struct alpm_softc *sc) 29043973Snsouch{ 29143973Snsouch ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); 29243973Snsouch 29343973Snsouch return (0); 29443973Snsouch} 29543973Snsouch#endif 29643973Snsouch 29743973Snsouchstatic int 29893023Snsouchalpm_idle(struct alpm_softc *sc) 29943973Snsouch{ 30043973Snsouch u_char sts; 30143973Snsouch 30243973Snsouch sts = ALPM_SMBINB(sc, SMBSTS); 30343973Snsouch 30443973Snsouch ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts)); 30543973Snsouch 30643973Snsouch return (sts & IDL_STS); 30743973Snsouch} 30843973Snsouch 30943973Snsouch/* 31043973Snsouch * Poll the SMBus controller 31143973Snsouch */ 31243973Snsouchstatic int 31393023Snsouchalpm_wait(struct alpm_softc *sc) 31443973Snsouch{ 31543973Snsouch int count = 10000; 31670606Snsouch u_char sts = 0; 31743973Snsouch int error; 31843973Snsouch 31943973Snsouch /* wait for command to complete and SMBus controller is idle */ 32043973Snsouch while(count--) { 32143973Snsouch DELAY(10); 32243973Snsouch sts = ALPM_SMBINB(sc, SMBSTS); 32343973Snsouch if (sts & SMI_I_STS) 32443973Snsouch break; 32543973Snsouch } 32643973Snsouch 32743973Snsouch ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts)); 32843973Snsouch 32943973Snsouch error = SMB_ENOERR; 33043973Snsouch 33143973Snsouch if (!count) 33243973Snsouch error |= SMB_ETIMEOUT; 33343973Snsouch 33443973Snsouch if (sts & TERMINATE) 33543973Snsouch error |= SMB_EABORT; 33643973Snsouch 33743973Snsouch if (sts & BUS_COLLI) 33843973Snsouch error |= SMB_ENOACK; 33943973Snsouch 34043973Snsouch if (sts & DEVICE_ERR) 34143973Snsouch error |= SMB_EBUSERR; 34243973Snsouch 34343973Snsouch if (error != SMB_ENOERR) 34493023Snsouch alpm_clear(sc); 34543973Snsouch 34643973Snsouch return (error); 34743973Snsouch} 34843973Snsouch 34943973Snsouchstatic int 35093023Snsouchalpm_quick(device_t dev, u_char slave, int how) 35143973Snsouch{ 35293023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 35343973Snsouch int error; 35443973Snsouch 35593023Snsouch alpm_clear(sc); 35693023Snsouch if (!alpm_idle(sc)) 35743973Snsouch return (EBUSY); 35843973Snsouch 35943973Snsouch switch (how) { 36043973Snsouch case SMB_QWRITE: 36143973Snsouch ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave)); 36243973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 36343973Snsouch break; 36443973Snsouch case SMB_QREAD: 36543973Snsouch ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave)); 36643973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 36743973Snsouch break; 36843973Snsouch default: 36987599Sobrien panic("%s: unknown QUICK command (%x)!", __func__, 37043973Snsouch how); 37143973Snsouch } 37243973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK); 37343973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 37443973Snsouch 37593023Snsouch error = alpm_wait(sc); 37643973Snsouch 37743973Snsouch ALPM_DEBUG(printf(", error=0x%x\n", error)); 37843973Snsouch 37943973Snsouch return (error); 38043973Snsouch} 38143973Snsouch 38243973Snsouchstatic int 38393023Snsouchalpm_sendb(device_t dev, u_char slave, char byte) 38443973Snsouch{ 38593023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 38643973Snsouch int error; 38743973Snsouch 38893023Snsouch alpm_clear(sc); 38993023Snsouch if (!alpm_idle(sc)) 39043973Snsouch return (SMB_EBUSY); 39143973Snsouch 39243973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 39343973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 39443973Snsouch ALPM_SMBOUTB(sc, SMBHDATA, byte); 39543973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 39643973Snsouch 39793023Snsouch error = alpm_wait(sc); 39843973Snsouch 39943973Snsouch ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); 40043973Snsouch 40143973Snsouch return (error); 40243973Snsouch} 40343973Snsouch 40443973Snsouchstatic int 40593023Snsouchalpm_recvb(device_t dev, u_char slave, char *byte) 40643973Snsouch{ 40793023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 40843973Snsouch int error; 40943973Snsouch 41093023Snsouch alpm_clear(sc); 41193023Snsouch if (!alpm_idle(sc)) 41243973Snsouch return (SMB_EBUSY); 41343973Snsouch 41443973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 41543973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 41643973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 41743973Snsouch 41893023Snsouch if ((error = alpm_wait(sc)) == SMB_ENOERR) 41943973Snsouch *byte = ALPM_SMBINB(sc, SMBHDATA); 42043973Snsouch 42143973Snsouch ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); 42243973Snsouch 42343973Snsouch return (error); 42443973Snsouch} 42543973Snsouch 42643973Snsouchstatic int 42793023Snsouchalpm_writeb(device_t dev, u_char slave, char cmd, char byte) 42843973Snsouch{ 42993023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 43043973Snsouch int error; 43143973Snsouch 43293023Snsouch alpm_clear(sc); 43393023Snsouch if (!alpm_idle(sc)) 43443973Snsouch return (SMB_EBUSY); 43543973Snsouch 43643973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 43743973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 43843973Snsouch ALPM_SMBOUTB(sc, SMBHDATA, byte); 43943973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 44043973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 44143973Snsouch 44293023Snsouch error = alpm_wait(sc); 44343973Snsouch 44443973Snsouch ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); 44543973Snsouch 44643973Snsouch return (error); 44743973Snsouch} 44843973Snsouch 44943973Snsouchstatic int 45093023Snsouchalpm_readb(device_t dev, u_char slave, char cmd, char *byte) 45143973Snsouch{ 45293023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 45343973Snsouch int error; 45443973Snsouch 45593023Snsouch alpm_clear(sc); 45693023Snsouch if (!alpm_idle(sc)) 45743973Snsouch return (SMB_EBUSY); 45843973Snsouch 45943973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 46043973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 46143973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 46243973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 46343973Snsouch 46493023Snsouch if ((error = alpm_wait(sc)) == SMB_ENOERR) 46543973Snsouch *byte = ALPM_SMBINB(sc, SMBHDATA); 46643973Snsouch 46743973Snsouch ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); 46843973Snsouch 46943973Snsouch return (error); 47043973Snsouch} 47143973Snsouch 47243973Snsouchstatic int 47393023Snsouchalpm_writew(device_t dev, u_char slave, char cmd, short word) 47443973Snsouch{ 47593023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 47643973Snsouch int error; 47743973Snsouch 47893023Snsouch alpm_clear(sc); 47993023Snsouch if (!alpm_idle(sc)) 48043973Snsouch return (SMB_EBUSY); 48143973Snsouch 48243973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 48343973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 48443973Snsouch ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff); 48543973Snsouch ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8); 48643973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 48743973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 48843973Snsouch 48993023Snsouch error = alpm_wait(sc); 49043973Snsouch 49143973Snsouch ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); 49243973Snsouch 49343973Snsouch return (error); 49443973Snsouch} 49543973Snsouch 49643973Snsouchstatic int 49793023Snsouchalpm_readw(device_t dev, u_char slave, char cmd, short *word) 49843973Snsouch{ 49993023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 50043973Snsouch int error; 50143973Snsouch u_char high, low; 50243973Snsouch 50393023Snsouch alpm_clear(sc); 50493023Snsouch if (!alpm_idle(sc)) 50543973Snsouch return (SMB_EBUSY); 50643973Snsouch 50743973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 50843973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 50943973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 51043973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 51143973Snsouch 51293023Snsouch if ((error = alpm_wait(sc)) == SMB_ENOERR) { 51343973Snsouch low = ALPM_SMBINB(sc, SMBHDATA); 51443973Snsouch high = ALPM_SMBINB(sc, SMBHDATB); 51543973Snsouch 51643973Snsouch *word = ((high & 0xff) << 8) | (low & 0xff); 51743973Snsouch } 51843973Snsouch 51943973Snsouch ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); 52043973Snsouch 52143973Snsouch return (error); 52243973Snsouch} 52343973Snsouch 52443973Snsouchstatic int 52593023Snsouchalpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 52643973Snsouch{ 52793023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 528162234Sjhb u_char i; 529162234Sjhb int error; 53043973Snsouch 531162234Sjhb if (count < 1 || count > 32) 532162234Sjhb return (SMB_EINVAL); 53393023Snsouch alpm_clear(sc); 53493023Snsouch if(!alpm_idle(sc)) 53543973Snsouch return (SMB_EBUSY); 53643973Snsouch 537162234Sjhb ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 53843973Snsouch 539162234Sjhb /* set the cmd and reset the 540162234Sjhb * 32-byte long internal buffer */ 541162234Sjhb ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 54243973Snsouch 543162234Sjhb ALPM_SMBOUTB(sc, SMBHDATA, count); 54443973Snsouch 545162234Sjhb /* fill the 32-byte internal buffer */ 546162234Sjhb for (i = 0; i < count; i++) { 547162234Sjhb ALPM_SMBOUTB(sc, SMBHBLOCK, buf[i]); 548162234Sjhb DELAY(2); 549162234Sjhb } 550162234Sjhb ALPM_SMBOUTB(sc, SMBHCMD, cmd); 551162234Sjhb ALPM_SMBOUTB(sc, SMBSTART, 0xff); 55243973Snsouch 553162234Sjhb error = alpm_wait(sc); 55443973Snsouch 55543973Snsouch ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 55643973Snsouch 55743973Snsouch return (error); 55843973Snsouch} 55943973Snsouch 56043973Snsouchstatic int 561162234Sjhbalpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 56243973Snsouch{ 56393023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 564162234Sjhb u_char data, len, i; 565162234Sjhb int error; 56643973Snsouch 567162234Sjhb if (*count < 1 || *count > 32) 568162234Sjhb return (SMB_EINVAL); 56993023Snsouch alpm_clear(sc); 57093023Snsouch if (!alpm_idle(sc)) 57143973Snsouch return (SMB_EBUSY); 57243973Snsouch 573162234Sjhb ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 57443973Snsouch 575162234Sjhb /* set the cmd and reset the 576162234Sjhb * 32-byte long internal buffer */ 577162234Sjhb ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 57843973Snsouch 579162234Sjhb ALPM_SMBOUTB(sc, SMBHCMD, cmd); 580162234Sjhb ALPM_SMBOUTB(sc, SMBSTART, 0xff); 58143973Snsouch 582162234Sjhb if ((error = alpm_wait(sc)) != SMB_ENOERR) 58343973Snsouch goto error; 58443973Snsouch 585162234Sjhb len = ALPM_SMBINB(sc, SMBHDATA); 58643973Snsouch 587162234Sjhb /* read the 32-byte internal buffer */ 588162234Sjhb for (i = 0; i < len; i++) { 589162234Sjhb data = ALPM_SMBINB(sc, SMBHBLOCK); 590162234Sjhb if (i < *count) 591162234Sjhb buf[i] = data; 592162234Sjhb DELAY(2); 593162234Sjhb } 594162234Sjhb *count = len; 59543973Snsouch 59643973Snsoucherror: 597162234Sjhb ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error)); 59843973Snsouch 59943973Snsouch return (error); 60043973Snsouch} 60143973Snsouch 60293023Snsouchstatic devclass_t alpm_devclass; 60393023Snsouch 60493023Snsouchstatic device_method_t alpm_methods[] = { 60593023Snsouch /* device interface */ 60693023Snsouch DEVMETHOD(device_probe, alpm_probe), 60793023Snsouch DEVMETHOD(device_attach, alpm_attach), 60893023Snsouch DEVMETHOD(device_detach, alpm_detach), 60993023Snsouch 61093023Snsouch /* smbus interface */ 61193023Snsouch DEVMETHOD(smbus_callback, alpm_callback), 61293023Snsouch DEVMETHOD(smbus_quick, alpm_quick), 61393023Snsouch DEVMETHOD(smbus_sendb, alpm_sendb), 61493023Snsouch DEVMETHOD(smbus_recvb, alpm_recvb), 61593023Snsouch DEVMETHOD(smbus_writeb, alpm_writeb), 61693023Snsouch DEVMETHOD(smbus_readb, alpm_readb), 61793023Snsouch DEVMETHOD(smbus_writew, alpm_writew), 61893023Snsouch DEVMETHOD(smbus_readw, alpm_readw), 61993023Snsouch DEVMETHOD(smbus_bwrite, alpm_bwrite), 62093023Snsouch DEVMETHOD(smbus_bread, alpm_bread), 62193023Snsouch 62293023Snsouch { 0, 0 } 62393023Snsouch}; 62493023Snsouch 62593023Snsouchstatic driver_t alpm_driver = { 62693023Snsouch "alpm", 62793023Snsouch alpm_methods, 62893023Snsouch sizeof(struct alpm_softc) 62993023Snsouch}; 63093023Snsouch 63193023SnsouchDRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0); 632162234SjhbDRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, 0, 0); 633113506SmdoddMODULE_DEPEND(alpm, pci, 1, 1, 1); 63493023SnsouchMODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 63593023SnsouchMODULE_VERSION(alpm, 1); 636