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$"); 33116192Sobrien 3443973Snsouch#include <sys/param.h> 35165951Sjhb#include <sys/bus.h> 3643973Snsouch#include <sys/kernel.h> 37165951Sjhb#include <sys/lock.h> 38165951Sjhb#include <sys/module.h> 39165951Sjhb#include <sys/mutex.h> 4043973Snsouch#include <sys/systm.h> 4143973Snsouch 4243973Snsouch#include <machine/bus.h> 4370606Snsouch#include <machine/resource.h> 4470606Snsouch#include <sys/rman.h> 4543973Snsouch 46119288Simp#include <dev/pci/pcivar.h> 47119288Simp#include <dev/pci/pcireg.h> 4843973Snsouch 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 62165951Sjhb/* 63165951Sjhb * I/O registers offsets - the base address is programmed via the 6443973Snsouch * SMBBA PCI configuration register 6543973Snsouch */ 6643973Snsouch#define SMBSTS 0x0 /* SMBus host/slave status register */ 6743973Snsouch#define SMBCMD 0x1 /* SMBus host/slave command register */ 6843973Snsouch#define SMBSTART 0x2 /* start to generate programmed cycle */ 6943973Snsouch#define SMBHADDR 0x3 /* host address register */ 7043973Snsouch#define SMBHDATA 0x4 /* data A register for host controller */ 7143973Snsouch#define SMBHDATB 0x5 /* data B register for host controller */ 7243973Snsouch#define SMBHBLOCK 0x6 /* block register for host controller */ 7343973Snsouch#define SMBHCMD 0x7 /* command register for host controller */ 7443973Snsouch 75165951Sjhb/* SMBHADDR mask. */ 76165951Sjhb#define LSB 0x1 /* XXX: Better name: Read/Write? */ 77165951Sjhb 7843973Snsouch/* SMBSTS masks */ 7943973Snsouch#define TERMINATE 0x80 8043973Snsouch#define BUS_COLLI 0x40 8143973Snsouch#define DEVICE_ERR 0x20 8243973Snsouch#define SMI_I_STS 0x10 8343973Snsouch#define HST_BSY 0x08 8443973Snsouch#define IDL_STS 0x04 8543973Snsouch#define HSTSLV_STS 0x02 8643973Snsouch#define HSTSLV_BSY 0x01 8743973Snsouch 8843973Snsouch/* SMBCMD masks */ 8943973Snsouch#define SMB_BLK_CLR 0x80 9043973Snsouch#define T_OUT_CMD 0x08 9143973Snsouch#define ABORT_HOST 0x04 9243973Snsouch 9343973Snsouch/* SMBus commands */ 9443973Snsouch#define SMBQUICK 0x00 9543973Snsouch#define SMBSRBYTE 0x10 /* send/receive byte */ 9643973Snsouch#define SMBWRBYTE 0x20 /* write/read byte */ 9743973Snsouch#define SMBWRWORD 0x30 /* write/read word */ 9843973Snsouch#define SMBWRBLOCK 0x40 /* write/read block */ 9943973Snsouch 10043973Snsouch/* PCI configuration registers and masks 10143973Snsouch */ 10243973Snsouch#define COM 0x4 10343973Snsouch#define COM_ENABLE_IO 0x1 10443973Snsouch 105165951Sjhb#define SMBBA PCIR_BAR(1) 10643973Snsouch 10743973Snsouch#define ATPC 0x5b 10870606Snsouch#define ATPC_SMBCTRL 0x04 /* XX linux has this as 0x6 */ 10943973Snsouch 11043973Snsouch#define SMBHSI 0xe0 11143973Snsouch#define SMBHSI_SLAVE 0x2 11243973Snsouch#define SMBHSI_HOST 0x1 11343973Snsouch 11443973Snsouch#define SMBHCBC 0xe2 11543973Snsouch#define SMBHCBC_CLOCK 0x70 11643973Snsouch 11743973Snsouch#define SMBCLOCK_149K 0x0 11843973Snsouch#define SMBCLOCK_74K 0x20 11943973Snsouch#define SMBCLOCK_37K 0x40 12043973Snsouch#define SMBCLOCK_223K 0x80 12143973Snsouch#define SMBCLOCK_111K 0xa0 12243973Snsouch#define SMBCLOCK_55K 0xc0 12343973Snsouch 12493023Snsouchstruct alpm_softc { 12543973Snsouch int base; 12693023Snsouch struct resource *res; 12743973Snsouch bus_space_tag_t smbst; 12843973Snsouch bus_space_handle_t smbsh; 12943973Snsouch device_t smbus; 130165951Sjhb struct mtx lock; 13143973Snsouch}; 13243973Snsouch 133165951Sjhb#define ALPM_LOCK(alpm) mtx_lock(&(alpm)->lock) 134165951Sjhb#define ALPM_UNLOCK(alpm) mtx_unlock(&(alpm)->lock) 135165951Sjhb#define ALPM_LOCK_ASSERT(alpm) mtx_assert(&(alpm)->lock, MA_OWNED) 136165951Sjhb 13793023Snsouch#define ALPM_SMBINB(alpm,register) \ 13893023Snsouch (bus_space_read_1(alpm->smbst, alpm->smbsh, register)) 13993023Snsouch#define ALPM_SMBOUTB(alpm,register,value) \ 14093023Snsouch (bus_space_write_1(alpm->smbst, alpm->smbsh, register, value)) 14143973Snsouch 142165951Sjhbstatic int alpm_detach(device_t dev); 143165951Sjhb 14493023Snsouchstatic int 14593023Snsouchalpm_probe(device_t dev) 14693023Snsouch{ 14743973Snsouch 14893023Snsouch if (pci_get_devid(dev) == ACER_M1543_PMU_ID) { 14993023Snsouch device_set_desc(dev, "AcerLabs M15x3 Power Management Unit"); 15043973Snsouch 151142398Simp return (BUS_PROBE_DEFAULT); 15293023Snsouch } 15343973Snsouch 15493023Snsouch return (ENXIO); 15543973Snsouch} 15643973Snsouch 15770606Snsouchstatic int 15893023Snsouchalpm_attach(device_t dev) 15943973Snsouch{ 160115532Sphk int rid; 16170606Snsouch u_int32_t l; 16293023Snsouch struct alpm_softc *alpm; 16343973Snsouch 16470606Snsouch alpm = device_get_softc(dev); 16543973Snsouch 16643973Snsouch /* Unlock SMBIO base register access */ 16770606Snsouch l = pci_read_config(dev, ATPC, 1); 16870606Snsouch pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1); 16943973Snsouch 17070606Snsouch /* 17170606Snsouch * XX linux sets clock to 74k, should we? 17270606Snsouch l = pci_read_config(dev, SMBHCBC, 1); 17370606Snsouch l &= 0x1f; 17470606Snsouch l |= SMBCLOCK_74K; 17570606Snsouch pci_write_config(dev, SMBHCBC, l, 1); 17670606Snsouch */ 17770606Snsouch 17893023Snsouch if (bootverbose || alpm_debug) { 17970606Snsouch l = pci_read_config(dev, SMBHSI, 1); 18093023Snsouch device_printf(dev, "%s/%s", 18143973Snsouch (l & SMBHSI_HOST) ? "host":"nohost", 18243973Snsouch (l & SMBHSI_SLAVE) ? "slave":"noslave"); 18343973Snsouch 18470606Snsouch l = pci_read_config(dev, SMBHCBC, 1); 18543973Snsouch switch (l & SMBHCBC_CLOCK) { 18643973Snsouch case SMBCLOCK_149K: 18743973Snsouch printf(" 149K"); 18843973Snsouch break; 18943973Snsouch case SMBCLOCK_74K: 19043973Snsouch printf(" 74K"); 19143973Snsouch break; 19243973Snsouch case SMBCLOCK_37K: 19343973Snsouch printf(" 37K"); 19443973Snsouch break; 19543973Snsouch case SMBCLOCK_223K: 19643973Snsouch printf(" 223K"); 19743973Snsouch break; 19843973Snsouch case SMBCLOCK_111K: 19943973Snsouch printf(" 111K"); 20043973Snsouch break; 20143973Snsouch case SMBCLOCK_55K: 20243973Snsouch printf(" 55K"); 20343973Snsouch break; 20493023Snsouch default: 20593023Snsouch printf("unkown"); 20693023Snsouch break; 20743973Snsouch } 20893023Snsouch printf("\n"); 20943973Snsouch } 21043973Snsouch 21193023Snsouch rid = SMBBA; 212127135Snjl alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 213127135Snjl RF_ACTIVE); 21443973Snsouch 21593023Snsouch if (alpm->res == NULL) { 21670606Snsouch device_printf(dev,"Could not allocate Bus space\n"); 21793023Snsouch return (ENXIO); 21870606Snsouch } 21993023Snsouch alpm->smbst = rman_get_bustag(alpm->res); 22093023Snsouch alpm->smbsh = rman_get_bushandle(alpm->res); 221165951Sjhb mtx_init(&alpm->lock, device_get_nameunit(dev), "alpm", MTX_DEF); 22270606Snsouch 22393023Snsouch /* attach the smbus */ 22493023Snsouch alpm->smbus = device_add_child(dev, "smbus", -1); 225165951Sjhb if (alpm->smbus == NULL) { 226165951Sjhb alpm_detach(dev); 227165951Sjhb return (EINVAL); 228165951Sjhb } 22993023Snsouch bus_generic_attach(dev); 23043973Snsouch 23143973Snsouch return (0); 23243973Snsouch} 23343973Snsouch 23443973Snsouchstatic int 23593023Snsouchalpm_detach(device_t dev) 23643973Snsouch{ 23793023Snsouch struct alpm_softc *alpm = device_get_softc(dev); 23843973Snsouch 23993023Snsouch if (alpm->smbus) { 24093023Snsouch device_delete_child(dev, alpm->smbus); 24193023Snsouch alpm->smbus = NULL; 24293023Snsouch } 243165951Sjhb mtx_destroy(&alpm->lock); 24443973Snsouch 24593023Snsouch if (alpm->res) 24693023Snsouch bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res); 24743973Snsouch 24843973Snsouch return (0); 24943973Snsouch} 25043973Snsouch 25143973Snsouchstatic int 252162234Sjhbalpm_callback(device_t dev, int index, void *data) 25343973Snsouch{ 25443973Snsouch int error = 0; 25543973Snsouch 25643973Snsouch switch (index) { 25743973Snsouch case SMB_REQUEST_BUS: 25843973Snsouch case SMB_RELEASE_BUS: 25943973Snsouch /* ok, bus allocation accepted */ 26043973Snsouch break; 26143973Snsouch default: 26243973Snsouch error = EINVAL; 26343973Snsouch } 26443973Snsouch 26543973Snsouch return (error); 26643973Snsouch} 26743973Snsouch 26843973Snsouchstatic int 26993023Snsouchalpm_clear(struct alpm_softc *sc) 27043973Snsouch{ 27143973Snsouch ALPM_SMBOUTB(sc, SMBSTS, 0xff); 27243973Snsouch DELAY(10); 27343973Snsouch 27443973Snsouch return (0); 27543973Snsouch} 27643973Snsouch 27743973Snsouch#if 0 27843973Snsouchstatic int 27993023Snsouchalpm_abort(struct alpm_softc *sc) 28043973Snsouch{ 28143973Snsouch ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); 28243973Snsouch 28343973Snsouch return (0); 28443973Snsouch} 28543973Snsouch#endif 28643973Snsouch 28743973Snsouchstatic int 28893023Snsouchalpm_idle(struct alpm_softc *sc) 28943973Snsouch{ 29043973Snsouch u_char sts; 29143973Snsouch 29243973Snsouch sts = ALPM_SMBINB(sc, SMBSTS); 29343973Snsouch 29443973Snsouch ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts)); 29543973Snsouch 29643973Snsouch return (sts & IDL_STS); 29743973Snsouch} 29843973Snsouch 29943973Snsouch/* 30043973Snsouch * Poll the SMBus controller 30143973Snsouch */ 30243973Snsouchstatic int 30393023Snsouchalpm_wait(struct alpm_softc *sc) 30443973Snsouch{ 30543973Snsouch int count = 10000; 30670606Snsouch u_char sts = 0; 30743973Snsouch int error; 30843973Snsouch 30943973Snsouch /* wait for command to complete and SMBus controller is idle */ 310165951Sjhb while (count--) { 31143973Snsouch DELAY(10); 31243973Snsouch sts = ALPM_SMBINB(sc, SMBSTS); 31343973Snsouch if (sts & SMI_I_STS) 31443973Snsouch break; 31543973Snsouch } 31643973Snsouch 31743973Snsouch ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts)); 31843973Snsouch 31943973Snsouch error = SMB_ENOERR; 32043973Snsouch 32143973Snsouch if (!count) 32243973Snsouch error |= SMB_ETIMEOUT; 32343973Snsouch 32443973Snsouch if (sts & TERMINATE) 32543973Snsouch error |= SMB_EABORT; 32643973Snsouch 32743973Snsouch if (sts & BUS_COLLI) 32843973Snsouch error |= SMB_ENOACK; 32943973Snsouch 33043973Snsouch if (sts & DEVICE_ERR) 33143973Snsouch error |= SMB_EBUSERR; 33243973Snsouch 33343973Snsouch if (error != SMB_ENOERR) 33493023Snsouch alpm_clear(sc); 33543973Snsouch 33643973Snsouch return (error); 33743973Snsouch} 33843973Snsouch 33943973Snsouchstatic int 34093023Snsouchalpm_quick(device_t dev, u_char slave, int how) 34143973Snsouch{ 34293023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 34343973Snsouch int error; 34443973Snsouch 345165951Sjhb ALPM_LOCK(sc); 34693023Snsouch alpm_clear(sc); 347165951Sjhb if (!alpm_idle(sc)) { 348165951Sjhb ALPM_UNLOCK(sc); 34943973Snsouch return (EBUSY); 350165951Sjhb } 35143973Snsouch 35243973Snsouch switch (how) { 35343973Snsouch case SMB_QWRITE: 35443973Snsouch ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave)); 35543973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 35643973Snsouch break; 35743973Snsouch case SMB_QREAD: 35843973Snsouch ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave)); 35943973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 36043973Snsouch break; 36143973Snsouch default: 36287599Sobrien panic("%s: unknown QUICK command (%x)!", __func__, 36343973Snsouch how); 36443973Snsouch } 36543973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK); 36643973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 36743973Snsouch 36893023Snsouch error = alpm_wait(sc); 36943973Snsouch 37043973Snsouch ALPM_DEBUG(printf(", error=0x%x\n", error)); 371165951Sjhb ALPM_UNLOCK(sc); 37243973Snsouch 37343973Snsouch return (error); 37443973Snsouch} 37543973Snsouch 37643973Snsouchstatic int 37793023Snsouchalpm_sendb(device_t dev, u_char slave, char byte) 37843973Snsouch{ 37993023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 38043973Snsouch int error; 38143973Snsouch 382165951Sjhb ALPM_LOCK(sc); 38393023Snsouch alpm_clear(sc); 384165951Sjhb if (!alpm_idle(sc)) { 385165951Sjhb ALPM_UNLOCK(sc); 38643973Snsouch return (SMB_EBUSY); 387165951Sjhb } 38843973Snsouch 38943973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 39043973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 39143973Snsouch ALPM_SMBOUTB(sc, SMBHDATA, byte); 39243973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 39343973Snsouch 39493023Snsouch error = alpm_wait(sc); 39543973Snsouch 39643973Snsouch ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); 397165951Sjhb ALPM_UNLOCK(sc); 39843973Snsouch 39943973Snsouch return (error); 40043973Snsouch} 40143973Snsouch 40243973Snsouchstatic int 40393023Snsouchalpm_recvb(device_t dev, u_char slave, char *byte) 40443973Snsouch{ 40593023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 40643973Snsouch int error; 40743973Snsouch 408165951Sjhb ALPM_LOCK(sc); 40993023Snsouch alpm_clear(sc); 410165951Sjhb if (!alpm_idle(sc)) { 411165951Sjhb ALPM_UNLOCK(sc); 41243973Snsouch return (SMB_EBUSY); 413165951Sjhb } 41443973Snsouch 41543973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 41643973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 41743973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 41843973Snsouch 41993023Snsouch if ((error = alpm_wait(sc)) == SMB_ENOERR) 42043973Snsouch *byte = ALPM_SMBINB(sc, SMBHDATA); 42143973Snsouch 42243973Snsouch ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); 423165951Sjhb ALPM_UNLOCK(sc); 42443973Snsouch 42543973Snsouch return (error); 42643973Snsouch} 42743973Snsouch 42843973Snsouchstatic int 42993023Snsouchalpm_writeb(device_t dev, u_char slave, char cmd, char byte) 43043973Snsouch{ 43193023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 43243973Snsouch int error; 43343973Snsouch 434165951Sjhb ALPM_LOCK(sc); 43593023Snsouch alpm_clear(sc); 436165951Sjhb if (!alpm_idle(sc)) { 437165951Sjhb ALPM_UNLOCK(sc); 43843973Snsouch return (SMB_EBUSY); 439165951Sjhb } 44043973Snsouch 44143973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 44243973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 44343973Snsouch ALPM_SMBOUTB(sc, SMBHDATA, byte); 44443973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 44543973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 44643973Snsouch 44793023Snsouch error = alpm_wait(sc); 44843973Snsouch 44943973Snsouch ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); 450165951Sjhb ALPM_UNLOCK(sc); 45143973Snsouch 45243973Snsouch return (error); 45343973Snsouch} 45443973Snsouch 45543973Snsouchstatic int 45693023Snsouchalpm_readb(device_t dev, u_char slave, char cmd, char *byte) 45743973Snsouch{ 45893023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 45943973Snsouch int error; 46043973Snsouch 461165951Sjhb ALPM_LOCK(sc); 46293023Snsouch alpm_clear(sc); 463165951Sjhb if (!alpm_idle(sc)) { 464165951Sjhb ALPM_UNLOCK(sc); 46543973Snsouch return (SMB_EBUSY); 466165951Sjhb } 46743973Snsouch 46843973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 46943973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 47043973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 47143973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 47243973Snsouch 47393023Snsouch if ((error = alpm_wait(sc)) == SMB_ENOERR) 47443973Snsouch *byte = ALPM_SMBINB(sc, SMBHDATA); 47543973Snsouch 47643973Snsouch ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); 477165951Sjhb ALPM_UNLOCK(sc); 47843973Snsouch 47943973Snsouch return (error); 48043973Snsouch} 48143973Snsouch 48243973Snsouchstatic int 48393023Snsouchalpm_writew(device_t dev, u_char slave, char cmd, short word) 48443973Snsouch{ 48593023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 48643973Snsouch int error; 48743973Snsouch 488165951Sjhb ALPM_LOCK(sc); 48993023Snsouch alpm_clear(sc); 490165951Sjhb if (!alpm_idle(sc)) { 491165951Sjhb ALPM_UNLOCK(sc); 49243973Snsouch return (SMB_EBUSY); 493165951Sjhb } 49443973Snsouch 49543973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 49643973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 49743973Snsouch ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff); 49843973Snsouch ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8); 49943973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 50043973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 50143973Snsouch 50293023Snsouch error = alpm_wait(sc); 50343973Snsouch 50443973Snsouch ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); 505165951Sjhb ALPM_UNLOCK(sc); 50643973Snsouch 50743973Snsouch return (error); 50843973Snsouch} 50943973Snsouch 51043973Snsouchstatic int 51193023Snsouchalpm_readw(device_t dev, u_char slave, char cmd, short *word) 51243973Snsouch{ 51393023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 51443973Snsouch int error; 51543973Snsouch u_char high, low; 51643973Snsouch 517165951Sjhb ALPM_LOCK(sc); 51893023Snsouch alpm_clear(sc); 519165951Sjhb if (!alpm_idle(sc)) { 520165951Sjhb ALPM_UNLOCK(sc); 52143973Snsouch return (SMB_EBUSY); 522165951Sjhb } 52343973Snsouch 52443973Snsouch ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 52543973Snsouch ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 52643973Snsouch ALPM_SMBOUTB(sc, SMBHCMD, cmd); 52743973Snsouch ALPM_SMBOUTB(sc, SMBSTART, 0xff); 52843973Snsouch 52993023Snsouch if ((error = alpm_wait(sc)) == SMB_ENOERR) { 53043973Snsouch low = ALPM_SMBINB(sc, SMBHDATA); 53143973Snsouch high = ALPM_SMBINB(sc, SMBHDATB); 53243973Snsouch 53343973Snsouch *word = ((high & 0xff) << 8) | (low & 0xff); 53443973Snsouch } 53543973Snsouch 53643973Snsouch ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); 537165951Sjhb ALPM_UNLOCK(sc); 53843973Snsouch 53943973Snsouch return (error); 54043973Snsouch} 54143973Snsouch 54243973Snsouchstatic int 54393023Snsouchalpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 54443973Snsouch{ 54593023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 546162234Sjhb u_char i; 547162234Sjhb int error; 54843973Snsouch 549162234Sjhb if (count < 1 || count > 32) 550162234Sjhb return (SMB_EINVAL); 551165951Sjhb 552165951Sjhb ALPM_LOCK(sc); 55393023Snsouch alpm_clear(sc); 554165951Sjhb if(!alpm_idle(sc)) { 555165951Sjhb ALPM_UNLOCK(sc); 55643973Snsouch return (SMB_EBUSY); 557165951Sjhb } 55843973Snsouch 559162234Sjhb ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 56043973Snsouch 561162234Sjhb /* set the cmd and reset the 562162234Sjhb * 32-byte long internal buffer */ 563162234Sjhb ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 56443973Snsouch 565162234Sjhb ALPM_SMBOUTB(sc, SMBHDATA, count); 56643973Snsouch 567162234Sjhb /* fill the 32-byte internal buffer */ 568162234Sjhb for (i = 0; i < count; i++) { 569162234Sjhb ALPM_SMBOUTB(sc, SMBHBLOCK, buf[i]); 570162234Sjhb DELAY(2); 571162234Sjhb } 572162234Sjhb ALPM_SMBOUTB(sc, SMBHCMD, cmd); 573162234Sjhb ALPM_SMBOUTB(sc, SMBSTART, 0xff); 57443973Snsouch 575162234Sjhb error = alpm_wait(sc); 57643973Snsouch 57743973Snsouch ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 578165951Sjhb ALPM_UNLOCK(sc); 57943973Snsouch 58043973Snsouch return (error); 58143973Snsouch} 58243973Snsouch 58343973Snsouchstatic int 584162234Sjhbalpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 58543973Snsouch{ 58693023Snsouch struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 587162234Sjhb u_char data, len, i; 588162234Sjhb int error; 58943973Snsouch 590162234Sjhb if (*count < 1 || *count > 32) 591162234Sjhb return (SMB_EINVAL); 592165951Sjhb 593165951Sjhb ALPM_LOCK(sc); 59493023Snsouch alpm_clear(sc); 595165951Sjhb if (!alpm_idle(sc)) { 596165951Sjhb ALPM_UNLOCK(sc); 59743973Snsouch return (SMB_EBUSY); 598165951Sjhb } 59943973Snsouch 600162234Sjhb ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 60143973Snsouch 602162234Sjhb /* set the cmd and reset the 603162234Sjhb * 32-byte long internal buffer */ 604162234Sjhb ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 60543973Snsouch 606162234Sjhb ALPM_SMBOUTB(sc, SMBHCMD, cmd); 607162234Sjhb ALPM_SMBOUTB(sc, SMBSTART, 0xff); 60843973Snsouch 609162234Sjhb if ((error = alpm_wait(sc)) != SMB_ENOERR) 61043973Snsouch goto error; 61143973Snsouch 612162234Sjhb len = ALPM_SMBINB(sc, SMBHDATA); 61343973Snsouch 614162234Sjhb /* read the 32-byte internal buffer */ 615162234Sjhb for (i = 0; i < len; i++) { 616162234Sjhb data = ALPM_SMBINB(sc, SMBHBLOCK); 617162234Sjhb if (i < *count) 618162234Sjhb buf[i] = data; 619162234Sjhb DELAY(2); 620162234Sjhb } 621162234Sjhb *count = len; 62243973Snsouch 62343973Snsoucherror: 624162234Sjhb ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error)); 625165951Sjhb ALPM_UNLOCK(sc); 62643973Snsouch 62743973Snsouch return (error); 62843973Snsouch} 62943973Snsouch 63093023Snsouchstatic devclass_t alpm_devclass; 63193023Snsouch 63293023Snsouchstatic device_method_t alpm_methods[] = { 63393023Snsouch /* device interface */ 63493023Snsouch DEVMETHOD(device_probe, alpm_probe), 63593023Snsouch DEVMETHOD(device_attach, alpm_attach), 63693023Snsouch DEVMETHOD(device_detach, alpm_detach), 63793023Snsouch 63893023Snsouch /* smbus interface */ 63993023Snsouch DEVMETHOD(smbus_callback, alpm_callback), 64093023Snsouch DEVMETHOD(smbus_quick, alpm_quick), 64193023Snsouch DEVMETHOD(smbus_sendb, alpm_sendb), 64293023Snsouch DEVMETHOD(smbus_recvb, alpm_recvb), 64393023Snsouch DEVMETHOD(smbus_writeb, alpm_writeb), 64493023Snsouch DEVMETHOD(smbus_readb, alpm_readb), 64593023Snsouch DEVMETHOD(smbus_writew, alpm_writew), 64693023Snsouch DEVMETHOD(smbus_readw, alpm_readw), 64793023Snsouch DEVMETHOD(smbus_bwrite, alpm_bwrite), 64893023Snsouch DEVMETHOD(smbus_bread, alpm_bread), 64993023Snsouch 65093023Snsouch { 0, 0 } 65193023Snsouch}; 65293023Snsouch 65393023Snsouchstatic driver_t alpm_driver = { 65493023Snsouch "alpm", 65593023Snsouch alpm_methods, 65693023Snsouch sizeof(struct alpm_softc) 65793023Snsouch}; 65893023Snsouch 65993023SnsouchDRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0); 660162234SjhbDRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, 0, 0); 661113506SmdoddMODULE_DEPEND(alpm, pci, 1, 1, 1); 66293023SnsouchMODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 66393023SnsouchMODULE_VERSION(alpm, 1); 664