1*a9656fbcSSascha Wildner /*- 2*a9656fbcSSascha Wildner * Copyright (c) 1998, 1999, 2001 Nicolas Souchu 3*a9656fbcSSascha Wildner * All rights reserved. 4*a9656fbcSSascha Wildner * 5*a9656fbcSSascha Wildner * Redistribution and use in source and binary forms, with or without 6*a9656fbcSSascha Wildner * modification, are permitted provided that the following conditions 7*a9656fbcSSascha Wildner * are met: 8*a9656fbcSSascha Wildner * 1. Redistributions of source code must retain the above copyright 9*a9656fbcSSascha Wildner * notice, this list of conditions and the following disclaimer. 10*a9656fbcSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 11*a9656fbcSSascha Wildner * notice, this list of conditions and the following disclaimer in the 12*a9656fbcSSascha Wildner * documentation and/or other materials provided with the distribution. 13*a9656fbcSSascha Wildner * 14*a9656fbcSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*a9656fbcSSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*a9656fbcSSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*a9656fbcSSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*a9656fbcSSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*a9656fbcSSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*a9656fbcSSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*a9656fbcSSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*a9656fbcSSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*a9656fbcSSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*a9656fbcSSascha Wildner * SUCH DAMAGE. 25*a9656fbcSSascha Wildner * 26*a9656fbcSSascha Wildner * $FreeBSD: src/sys/pci/alpm.c,v 1.24 2005/05/29 04:42:29 nyan Exp $ 27*a9656fbcSSascha Wildner * 28*a9656fbcSSascha Wildner */ 29*a9656fbcSSascha Wildner 30*a9656fbcSSascha Wildner /* 31*a9656fbcSSascha Wildner * Power Management support for the Acer M15x3 chipsets 32*a9656fbcSSascha Wildner */ 33*a9656fbcSSascha Wildner #include <sys/param.h> 34*a9656fbcSSascha Wildner #include <sys/kernel.h> 35*a9656fbcSSascha Wildner #include <sys/systm.h> 36*a9656fbcSSascha Wildner #include <sys/module.h> 37*a9656fbcSSascha Wildner #include <sys/bus.h> 38*a9656fbcSSascha Wildner #include <sys/uio.h> 39*a9656fbcSSascha Wildner #include <sys/rman.h> 40*a9656fbcSSascha Wildner 41*a9656fbcSSascha Wildner #include <bus/pci/pcivar.h> 42*a9656fbcSSascha Wildner #include <bus/pci/pcireg.h> 43*a9656fbcSSascha Wildner 44*a9656fbcSSascha Wildner #include <bus/iicbus/iiconf.h> 45*a9656fbcSSascha Wildner #include <bus/smbus/smbconf.h> 46*a9656fbcSSascha Wildner #include "smbus_if.h" 47*a9656fbcSSascha Wildner 48*a9656fbcSSascha Wildner #define ALPM_DEBUG(x) if (alpm_debug) (x) 49*a9656fbcSSascha Wildner 50*a9656fbcSSascha Wildner #ifdef DEBUG 51*a9656fbcSSascha Wildner static int alpm_debug = 1; 52*a9656fbcSSascha Wildner #else 53*a9656fbcSSascha Wildner static int alpm_debug = 0; 54*a9656fbcSSascha Wildner #endif 55*a9656fbcSSascha Wildner 56*a9656fbcSSascha Wildner #define ACER_M1543_PMU_ID 0x710110b9 57*a9656fbcSSascha Wildner 58*a9656fbcSSascha Wildner /* Uncomment this line to force another I/O base address for SMB */ 59*a9656fbcSSascha Wildner /* #define ALPM_SMBIO_BASE_ADDR 0x3a80 */ 60*a9656fbcSSascha Wildner 61*a9656fbcSSascha Wildner /* I/O registers offsets - the base address is programmed via the 62*a9656fbcSSascha Wildner * SMBBA PCI configuration register 63*a9656fbcSSascha Wildner */ 64*a9656fbcSSascha Wildner #define SMBSTS 0x0 /* SMBus host/slave status register */ 65*a9656fbcSSascha Wildner #define SMBCMD 0x1 /* SMBus host/slave command register */ 66*a9656fbcSSascha Wildner #define SMBSTART 0x2 /* start to generate programmed cycle */ 67*a9656fbcSSascha Wildner #define SMBHADDR 0x3 /* host address register */ 68*a9656fbcSSascha Wildner #define SMBHDATA 0x4 /* data A register for host controller */ 69*a9656fbcSSascha Wildner #define SMBHDATB 0x5 /* data B register for host controller */ 70*a9656fbcSSascha Wildner #define SMBHBLOCK 0x6 /* block register for host controller */ 71*a9656fbcSSascha Wildner #define SMBHCMD 0x7 /* command register for host controller */ 72*a9656fbcSSascha Wildner 73*a9656fbcSSascha Wildner /* SMBSTS masks */ 74*a9656fbcSSascha Wildner #define TERMINATE 0x80 75*a9656fbcSSascha Wildner #define BUS_COLLI 0x40 76*a9656fbcSSascha Wildner #define DEVICE_ERR 0x20 77*a9656fbcSSascha Wildner #define SMI_I_STS 0x10 78*a9656fbcSSascha Wildner #define HST_BSY 0x08 79*a9656fbcSSascha Wildner #define IDL_STS 0x04 80*a9656fbcSSascha Wildner #define HSTSLV_STS 0x02 81*a9656fbcSSascha Wildner #define HSTSLV_BSY 0x01 82*a9656fbcSSascha Wildner 83*a9656fbcSSascha Wildner /* SMBCMD masks */ 84*a9656fbcSSascha Wildner #define SMB_BLK_CLR 0x80 85*a9656fbcSSascha Wildner #define T_OUT_CMD 0x08 86*a9656fbcSSascha Wildner #define ABORT_HOST 0x04 87*a9656fbcSSascha Wildner 88*a9656fbcSSascha Wildner /* SMBus commands */ 89*a9656fbcSSascha Wildner #define SMBQUICK 0x00 90*a9656fbcSSascha Wildner #define SMBSRBYTE 0x10 /* send/receive byte */ 91*a9656fbcSSascha Wildner #define SMBWRBYTE 0x20 /* write/read byte */ 92*a9656fbcSSascha Wildner #define SMBWRWORD 0x30 /* write/read word */ 93*a9656fbcSSascha Wildner #define SMBWRBLOCK 0x40 /* write/read block */ 94*a9656fbcSSascha Wildner 95*a9656fbcSSascha Wildner /* PCI configuration registers and masks 96*a9656fbcSSascha Wildner */ 97*a9656fbcSSascha Wildner #define COM 0x4 98*a9656fbcSSascha Wildner #define COM_ENABLE_IO 0x1 99*a9656fbcSSascha Wildner 100*a9656fbcSSascha Wildner #define SMBBA 0x14 101*a9656fbcSSascha Wildner 102*a9656fbcSSascha Wildner #define ATPC 0x5b 103*a9656fbcSSascha Wildner #define ATPC_SMBCTRL 0x04 /* XX linux has this as 0x6 */ 104*a9656fbcSSascha Wildner 105*a9656fbcSSascha Wildner #define SMBHSI 0xe0 106*a9656fbcSSascha Wildner #define SMBHSI_SLAVE 0x2 107*a9656fbcSSascha Wildner #define SMBHSI_HOST 0x1 108*a9656fbcSSascha Wildner 109*a9656fbcSSascha Wildner #define SMBHCBC 0xe2 110*a9656fbcSSascha Wildner #define SMBHCBC_CLOCK 0x70 111*a9656fbcSSascha Wildner 112*a9656fbcSSascha Wildner #define SMBCLOCK_149K 0x0 113*a9656fbcSSascha Wildner #define SMBCLOCK_74K 0x20 114*a9656fbcSSascha Wildner #define SMBCLOCK_37K 0x40 115*a9656fbcSSascha Wildner #define SMBCLOCK_223K 0x80 116*a9656fbcSSascha Wildner #define SMBCLOCK_111K 0xa0 117*a9656fbcSSascha Wildner #define SMBCLOCK_55K 0xc0 118*a9656fbcSSascha Wildner 119*a9656fbcSSascha Wildner struct alpm_softc { 120*a9656fbcSSascha Wildner int base; 121*a9656fbcSSascha Wildner struct resource *res; 122*a9656fbcSSascha Wildner bus_space_tag_t smbst; 123*a9656fbcSSascha Wildner bus_space_handle_t smbsh; 124*a9656fbcSSascha Wildner device_t smbus; 125*a9656fbcSSascha Wildner }; 126*a9656fbcSSascha Wildner 127*a9656fbcSSascha Wildner #define ALPM_SMBINB(alpm,register) \ 128*a9656fbcSSascha Wildner (bus_space_read_1(alpm->smbst, alpm->smbsh, register)) 129*a9656fbcSSascha Wildner #define ALPM_SMBOUTB(alpm,register,value) \ 130*a9656fbcSSascha Wildner (bus_space_write_1(alpm->smbst, alpm->smbsh, register, value)) 131*a9656fbcSSascha Wildner 132*a9656fbcSSascha Wildner static int 133*a9656fbcSSascha Wildner alpm_probe(device_t dev) 134*a9656fbcSSascha Wildner { 135*a9656fbcSSascha Wildner #ifdef ALPM_SMBIO_BASE_ADDR 136*a9656fbcSSascha Wildner u_int32_t l; 137*a9656fbcSSascha Wildner #endif 138*a9656fbcSSascha Wildner 139*a9656fbcSSascha Wildner if(pci_get_devid(dev) == ACER_M1543_PMU_ID) { 140*a9656fbcSSascha Wildner device_set_desc(dev, "AcerLabs M15x3 Power Management Unit"); 141*a9656fbcSSascha Wildner 142*a9656fbcSSascha Wildner #ifdef ALPM_SMBIO_BASE_ADDR 143*a9656fbcSSascha Wildner if (bootverbose || alpm_debug) 144*a9656fbcSSascha Wildner device_printf(dev, "forcing base I/O at 0x%x\n", 145*a9656fbcSSascha Wildner ALPM_SMBIO_BASE_ADDR); 146*a9656fbcSSascha Wildner 147*a9656fbcSSascha Wildner /* disable I/O */ 148*a9656fbcSSascha Wildner l = pci_read_config(dev, COM, 2); 149*a9656fbcSSascha Wildner pci_write_config(dev, COM, l & ~COM_ENABLE_IO, 2); 150*a9656fbcSSascha Wildner 151*a9656fbcSSascha Wildner /* set the I/O base address */ 152*a9656fbcSSascha Wildner pci_write_config(dev, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4); 153*a9656fbcSSascha Wildner 154*a9656fbcSSascha Wildner /* enable I/O */ 155*a9656fbcSSascha Wildner pci_write_config(dev, COM, l | COM_ENABLE_IO, 2); 156*a9656fbcSSascha Wildner 157*a9656fbcSSascha Wildner if (bus_set_resource(dev, SYS_RES_IOPORT, SMBBA, 158*a9656fbcSSascha Wildner ALPM_SMBIO_BASE_ADDR, 256)) { 159*a9656fbcSSascha Wildner device_printf(dev, "could not set bus resource\n"); 160*a9656fbcSSascha Wildner return (ENXIO); 161*a9656fbcSSascha Wildner } 162*a9656fbcSSascha Wildner #endif 163*a9656fbcSSascha Wildner return (BUS_PROBE_DEFAULT); 164*a9656fbcSSascha Wildner } 165*a9656fbcSSascha Wildner 166*a9656fbcSSascha Wildner return (ENXIO); 167*a9656fbcSSascha Wildner } 168*a9656fbcSSascha Wildner 169*a9656fbcSSascha Wildner static int 170*a9656fbcSSascha Wildner alpm_attach(device_t dev) 171*a9656fbcSSascha Wildner { 172*a9656fbcSSascha Wildner int rid; 173*a9656fbcSSascha Wildner u_int32_t l; 174*a9656fbcSSascha Wildner struct alpm_softc *alpm; 175*a9656fbcSSascha Wildner 176*a9656fbcSSascha Wildner alpm = device_get_softc(dev); 177*a9656fbcSSascha Wildner 178*a9656fbcSSascha Wildner /* Unlock SMBIO base register access */ 179*a9656fbcSSascha Wildner l = pci_read_config(dev, ATPC, 1); 180*a9656fbcSSascha Wildner pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1); 181*a9656fbcSSascha Wildner 182*a9656fbcSSascha Wildner /* 183*a9656fbcSSascha Wildner * XX linux sets clock to 74k, should we? 184*a9656fbcSSascha Wildner l = pci_read_config(dev, SMBHCBC, 1); 185*a9656fbcSSascha Wildner l &= 0x1f; 186*a9656fbcSSascha Wildner l |= SMBCLOCK_74K; 187*a9656fbcSSascha Wildner pci_write_config(dev, SMBHCBC, l, 1) 188*a9656fbcSSascha Wildner */ 189*a9656fbcSSascha Wildner 190*a9656fbcSSascha Wildner if (bootverbose || alpm_debug) { 191*a9656fbcSSascha Wildner l = pci_read_config(dev, SMBHSI, 1); 192*a9656fbcSSascha Wildner device_printf(dev, "%s/%s", 193*a9656fbcSSascha Wildner (l & SMBHSI_HOST) ? "host":"nohost", 194*a9656fbcSSascha Wildner (l & SMBHSI_SLAVE) ? "slave":"noslave"); 195*a9656fbcSSascha Wildner 196*a9656fbcSSascha Wildner l = pci_read_config(dev, SMBHCBC, 1); 197*a9656fbcSSascha Wildner switch (l & SMBHCBC_CLOCK) { 198*a9656fbcSSascha Wildner case SMBCLOCK_149K: 199*a9656fbcSSascha Wildner kprintf(" 149K"); 200*a9656fbcSSascha Wildner break; 201*a9656fbcSSascha Wildner case SMBCLOCK_74K: 202*a9656fbcSSascha Wildner kprintf(" 74K"); 203*a9656fbcSSascha Wildner break; 204*a9656fbcSSascha Wildner case SMBCLOCK_37K: 205*a9656fbcSSascha Wildner kprintf(" 37K"); 206*a9656fbcSSascha Wildner break; 207*a9656fbcSSascha Wildner case SMBCLOCK_223K: 208*a9656fbcSSascha Wildner kprintf(" 223K"); 209*a9656fbcSSascha Wildner break; 210*a9656fbcSSascha Wildner case SMBCLOCK_111K: 211*a9656fbcSSascha Wildner kprintf(" 111K"); 212*a9656fbcSSascha Wildner break; 213*a9656fbcSSascha Wildner case SMBCLOCK_55K: 214*a9656fbcSSascha Wildner kprintf(" 55K"); 215*a9656fbcSSascha Wildner break; 216*a9656fbcSSascha Wildner default: 217*a9656fbcSSascha Wildner kprintf("unkown"); 218*a9656fbcSSascha Wildner break; 219*a9656fbcSSascha Wildner } 220*a9656fbcSSascha Wildner kprintf("\n"); 221*a9656fbcSSascha Wildner } 222*a9656fbcSSascha Wildner 223*a9656fbcSSascha Wildner rid = SMBBA; 224*a9656fbcSSascha Wildner alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 225*a9656fbcSSascha Wildner RF_ACTIVE); 226*a9656fbcSSascha Wildner 227*a9656fbcSSascha Wildner if (alpm->res == NULL) { 228*a9656fbcSSascha Wildner device_printf(dev, "Could not allocate Bus space\n"); 229*a9656fbcSSascha Wildner return (ENXIO); 230*a9656fbcSSascha Wildner } 231*a9656fbcSSascha Wildner alpm->smbst = rman_get_bustag(alpm->res); 232*a9656fbcSSascha Wildner alpm->smbsh = rman_get_bushandle(alpm->res); 233*a9656fbcSSascha Wildner 234*a9656fbcSSascha Wildner /* attach the smbus */ 235*a9656fbcSSascha Wildner alpm->smbus = device_add_child(dev, "smbus", -1); 236*a9656fbcSSascha Wildner bus_generic_attach(dev); 237*a9656fbcSSascha Wildner 238*a9656fbcSSascha Wildner return (0); 239*a9656fbcSSascha Wildner } 240*a9656fbcSSascha Wildner 241*a9656fbcSSascha Wildner static int 242*a9656fbcSSascha Wildner alpm_detach(device_t dev) 243*a9656fbcSSascha Wildner { 244*a9656fbcSSascha Wildner struct alpm_softc *alpm = device_get_softc(dev); 245*a9656fbcSSascha Wildner 246*a9656fbcSSascha Wildner if (alpm->smbus) { 247*a9656fbcSSascha Wildner device_delete_child(dev, alpm->smbus); 248*a9656fbcSSascha Wildner alpm->smbus = NULL; 249*a9656fbcSSascha Wildner } 250*a9656fbcSSascha Wildner 251*a9656fbcSSascha Wildner if (alpm->res) 252*a9656fbcSSascha Wildner bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res); 253*a9656fbcSSascha Wildner 254*a9656fbcSSascha Wildner return (0); 255*a9656fbcSSascha Wildner } 256*a9656fbcSSascha Wildner 257*a9656fbcSSascha Wildner static int 258*a9656fbcSSascha Wildner alpm_callback(device_t dev, int index, caddr_t *data) 259*a9656fbcSSascha Wildner { 260*a9656fbcSSascha Wildner int error = 0; 261*a9656fbcSSascha Wildner 262*a9656fbcSSascha Wildner switch (index) { 263*a9656fbcSSascha Wildner case SMB_REQUEST_BUS: 264*a9656fbcSSascha Wildner case SMB_RELEASE_BUS: 265*a9656fbcSSascha Wildner /* ok, bus allocation accepted */ 266*a9656fbcSSascha Wildner break; 267*a9656fbcSSascha Wildner default: 268*a9656fbcSSascha Wildner error = EINVAL; 269*a9656fbcSSascha Wildner } 270*a9656fbcSSascha Wildner 271*a9656fbcSSascha Wildner return (error); 272*a9656fbcSSascha Wildner } 273*a9656fbcSSascha Wildner 274*a9656fbcSSascha Wildner static int 275*a9656fbcSSascha Wildner alpm_clear(struct alpm_softc *sc) 276*a9656fbcSSascha Wildner { 277*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTS, 0xff); 278*a9656fbcSSascha Wildner DELAY(10); 279*a9656fbcSSascha Wildner 280*a9656fbcSSascha Wildner return (0); 281*a9656fbcSSascha Wildner } 282*a9656fbcSSascha Wildner 283*a9656fbcSSascha Wildner #if 0 284*a9656fbcSSascha Wildner static int 285*a9656fbcSSascha Wildner alpm_abort(struct alpm_softc *sc) 286*a9656fbcSSascha Wildner { 287*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); 288*a9656fbcSSascha Wildner 289*a9656fbcSSascha Wildner return (0); 290*a9656fbcSSascha Wildner } 291*a9656fbcSSascha Wildner #endif 292*a9656fbcSSascha Wildner 293*a9656fbcSSascha Wildner static int 294*a9656fbcSSascha Wildner alpm_idle(struct alpm_softc *sc) 295*a9656fbcSSascha Wildner { 296*a9656fbcSSascha Wildner u_char sts; 297*a9656fbcSSascha Wildner 298*a9656fbcSSascha Wildner sts = ALPM_SMBINB(sc, SMBSTS); 299*a9656fbcSSascha Wildner 300*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: idle? STS=0x%x\n", sts)); 301*a9656fbcSSascha Wildner 302*a9656fbcSSascha Wildner return (sts & IDL_STS); 303*a9656fbcSSascha Wildner } 304*a9656fbcSSascha Wildner 305*a9656fbcSSascha Wildner /* 306*a9656fbcSSascha Wildner * Poll the SMBus controller 307*a9656fbcSSascha Wildner */ 308*a9656fbcSSascha Wildner static int 309*a9656fbcSSascha Wildner alpm_wait(struct alpm_softc *sc) 310*a9656fbcSSascha Wildner { 311*a9656fbcSSascha Wildner int count = 10000; 312*a9656fbcSSascha Wildner u_char sts = 0; 313*a9656fbcSSascha Wildner int error; 314*a9656fbcSSascha Wildner 315*a9656fbcSSascha Wildner /* wait for command to complete and SMBus controller is idle */ 316*a9656fbcSSascha Wildner while(count--) { 317*a9656fbcSSascha Wildner DELAY(10); 318*a9656fbcSSascha Wildner sts = ALPM_SMBINB(sc, SMBSTS); 319*a9656fbcSSascha Wildner if (sts & SMI_I_STS) 320*a9656fbcSSascha Wildner break; 321*a9656fbcSSascha Wildner } 322*a9656fbcSSascha Wildner 323*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: STS=0x%x\n", sts)); 324*a9656fbcSSascha Wildner 325*a9656fbcSSascha Wildner error = SMB_ENOERR; 326*a9656fbcSSascha Wildner 327*a9656fbcSSascha Wildner if (!count) 328*a9656fbcSSascha Wildner error |= SMB_ETIMEOUT; 329*a9656fbcSSascha Wildner 330*a9656fbcSSascha Wildner if (sts & TERMINATE) 331*a9656fbcSSascha Wildner error |= SMB_EABORT; 332*a9656fbcSSascha Wildner 333*a9656fbcSSascha Wildner if (sts & BUS_COLLI) 334*a9656fbcSSascha Wildner error |= SMB_ENOACK; 335*a9656fbcSSascha Wildner 336*a9656fbcSSascha Wildner if (sts & DEVICE_ERR) 337*a9656fbcSSascha Wildner error |= SMB_EBUSERR; 338*a9656fbcSSascha Wildner 339*a9656fbcSSascha Wildner if (error != SMB_ENOERR) 340*a9656fbcSSascha Wildner alpm_clear(sc); 341*a9656fbcSSascha Wildner 342*a9656fbcSSascha Wildner return (error); 343*a9656fbcSSascha Wildner } 344*a9656fbcSSascha Wildner 345*a9656fbcSSascha Wildner static int 346*a9656fbcSSascha Wildner alpm_quick(device_t dev, u_char slave, int how) 347*a9656fbcSSascha Wildner { 348*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 349*a9656fbcSSascha Wildner int error; 350*a9656fbcSSascha Wildner 351*a9656fbcSSascha Wildner alpm_clear(sc); 352*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 353*a9656fbcSSascha Wildner return (EBUSY); 354*a9656fbcSSascha Wildner 355*a9656fbcSSascha Wildner switch (how) { 356*a9656fbcSSascha Wildner case SMB_QWRITE: 357*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: QWRITE to 0x%x", slave)); 358*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 359*a9656fbcSSascha Wildner break; 360*a9656fbcSSascha Wildner case SMB_QREAD: 361*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: QREAD to 0x%x", slave)); 362*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 363*a9656fbcSSascha Wildner break; 364*a9656fbcSSascha Wildner default: 365*a9656fbcSSascha Wildner panic("%s: unknown QUICK command (%x)!", __func__, how); 366*a9656fbcSSascha Wildner } 367*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK); 368*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 369*a9656fbcSSascha Wildner 370*a9656fbcSSascha Wildner error = alpm_wait(sc); 371*a9656fbcSSascha Wildner 372*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf(", error=0x%x\n", error)); 373*a9656fbcSSascha Wildner 374*a9656fbcSSascha Wildner return (error); 375*a9656fbcSSascha Wildner } 376*a9656fbcSSascha Wildner 377*a9656fbcSSascha Wildner static int 378*a9656fbcSSascha Wildner alpm_sendb(device_t dev, u_char slave, char byte) 379*a9656fbcSSascha Wildner { 380*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 381*a9656fbcSSascha Wildner int error; 382*a9656fbcSSascha Wildner 383*a9656fbcSSascha Wildner alpm_clear(sc); 384*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 385*a9656fbcSSascha Wildner return (SMB_EBUSY); 386*a9656fbcSSascha Wildner 387*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 388*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 389*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHDATA, byte); 390*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 391*a9656fbcSSascha Wildner 392*a9656fbcSSascha Wildner error = alpm_wait(sc); 393*a9656fbcSSascha Wildner 394*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); 395*a9656fbcSSascha Wildner 396*a9656fbcSSascha Wildner return (error); 397*a9656fbcSSascha Wildner } 398*a9656fbcSSascha Wildner 399*a9656fbcSSascha Wildner static int 400*a9656fbcSSascha Wildner alpm_recvb(device_t dev, u_char slave, char *byte) 401*a9656fbcSSascha Wildner { 402*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 403*a9656fbcSSascha Wildner int error; 404*a9656fbcSSascha Wildner 405*a9656fbcSSascha Wildner alpm_clear(sc); 406*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 407*a9656fbcSSascha Wildner return (SMB_EBUSY); 408*a9656fbcSSascha Wildner 409*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 410*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 411*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 412*a9656fbcSSascha Wildner 413*a9656fbcSSascha Wildner if ((error = alpm_wait(sc)) == SMB_ENOERR) 414*a9656fbcSSascha Wildner *byte = ALPM_SMBINB(sc, SMBHDATA); 415*a9656fbcSSascha Wildner 416*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); 417*a9656fbcSSascha Wildner 418*a9656fbcSSascha Wildner return (error); 419*a9656fbcSSascha Wildner } 420*a9656fbcSSascha Wildner 421*a9656fbcSSascha Wildner static int 422*a9656fbcSSascha Wildner alpm_writeb(device_t dev, u_char slave, char cmd, char byte) 423*a9656fbcSSascha Wildner { 424*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 425*a9656fbcSSascha Wildner int error; 426*a9656fbcSSascha Wildner 427*a9656fbcSSascha Wildner alpm_clear(sc); 428*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 429*a9656fbcSSascha Wildner return (SMB_EBUSY); 430*a9656fbcSSascha Wildner 431*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 432*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 433*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHDATA, byte); 434*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHCMD, cmd); 435*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 436*a9656fbcSSascha Wildner 437*a9656fbcSSascha Wildner error = alpm_wait(sc); 438*a9656fbcSSascha Wildner 439*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); 440*a9656fbcSSascha Wildner 441*a9656fbcSSascha Wildner return (error); 442*a9656fbcSSascha Wildner } 443*a9656fbcSSascha Wildner 444*a9656fbcSSascha Wildner static int 445*a9656fbcSSascha Wildner alpm_readb(device_t dev, u_char slave, char cmd, char *byte) 446*a9656fbcSSascha Wildner { 447*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 448*a9656fbcSSascha Wildner int error; 449*a9656fbcSSascha Wildner 450*a9656fbcSSascha Wildner alpm_clear(sc); 451*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 452*a9656fbcSSascha Wildner return (SMB_EBUSY); 453*a9656fbcSSascha Wildner 454*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 455*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 456*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHCMD, cmd); 457*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 458*a9656fbcSSascha Wildner 459*a9656fbcSSascha Wildner if ((error = alpm_wait(sc)) == SMB_ENOERR) 460*a9656fbcSSascha Wildner *byte = ALPM_SMBINB(sc, SMBHDATA); 461*a9656fbcSSascha Wildner 462*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); 463*a9656fbcSSascha Wildner 464*a9656fbcSSascha Wildner return (error); 465*a9656fbcSSascha Wildner } 466*a9656fbcSSascha Wildner 467*a9656fbcSSascha Wildner static int 468*a9656fbcSSascha Wildner alpm_writew(device_t dev, u_char slave, char cmd, short word) 469*a9656fbcSSascha Wildner { 470*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 471*a9656fbcSSascha Wildner int error; 472*a9656fbcSSascha Wildner 473*a9656fbcSSascha Wildner alpm_clear(sc); 474*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 475*a9656fbcSSascha Wildner return (SMB_EBUSY); 476*a9656fbcSSascha Wildner 477*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 478*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 479*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff); 480*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8); 481*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHCMD, cmd); 482*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 483*a9656fbcSSascha Wildner 484*a9656fbcSSascha Wildner error = alpm_wait(sc); 485*a9656fbcSSascha Wildner 486*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); 487*a9656fbcSSascha Wildner 488*a9656fbcSSascha Wildner return (error); 489*a9656fbcSSascha Wildner } 490*a9656fbcSSascha Wildner 491*a9656fbcSSascha Wildner static int 492*a9656fbcSSascha Wildner alpm_readw(device_t dev, u_char slave, char cmd, short *word) 493*a9656fbcSSascha Wildner { 494*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 495*a9656fbcSSascha Wildner int error; 496*a9656fbcSSascha Wildner u_char high, low; 497*a9656fbcSSascha Wildner 498*a9656fbcSSascha Wildner alpm_clear(sc); 499*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 500*a9656fbcSSascha Wildner return (SMB_EBUSY); 501*a9656fbcSSascha Wildner 502*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 503*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 504*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHCMD, cmd); 505*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 506*a9656fbcSSascha Wildner 507*a9656fbcSSascha Wildner if ((error = alpm_wait(sc)) == SMB_ENOERR) { 508*a9656fbcSSascha Wildner low = ALPM_SMBINB(sc, SMBHDATA); 509*a9656fbcSSascha Wildner high = ALPM_SMBINB(sc, SMBHDATB); 510*a9656fbcSSascha Wildner 511*a9656fbcSSascha Wildner *word = ((high & 0xff) << 8) | (low & 0xff); 512*a9656fbcSSascha Wildner } 513*a9656fbcSSascha Wildner 514*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); 515*a9656fbcSSascha Wildner 516*a9656fbcSSascha Wildner return (error); 517*a9656fbcSSascha Wildner } 518*a9656fbcSSascha Wildner 519*a9656fbcSSascha Wildner static int 520*a9656fbcSSascha Wildner alpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 521*a9656fbcSSascha Wildner { 522*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 523*a9656fbcSSascha Wildner u_char remain, len, i; 524*a9656fbcSSascha Wildner int error = SMB_ENOERR; 525*a9656fbcSSascha Wildner 526*a9656fbcSSascha Wildner alpm_clear(sc); 527*a9656fbcSSascha Wildner if(!alpm_idle(sc)) 528*a9656fbcSSascha Wildner return (SMB_EBUSY); 529*a9656fbcSSascha Wildner 530*a9656fbcSSascha Wildner remain = count; 531*a9656fbcSSascha Wildner while (remain) { 532*a9656fbcSSascha Wildner len = min(remain, 32); 533*a9656fbcSSascha Wildner 534*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 535*a9656fbcSSascha Wildner 536*a9656fbcSSascha Wildner /* set the cmd and reset the 537*a9656fbcSSascha Wildner * 32-byte long internal buffer */ 538*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 539*a9656fbcSSascha Wildner 540*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHDATA, len); 541*a9656fbcSSascha Wildner 542*a9656fbcSSascha Wildner /* fill the 32-byte internal buffer */ 543*a9656fbcSSascha Wildner for (i=0; i<len; i++) { 544*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHBLOCK, buf[count-remain+i]); 545*a9656fbcSSascha Wildner DELAY(2); 546*a9656fbcSSascha Wildner } 547*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHCMD, cmd); 548*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 549*a9656fbcSSascha Wildner 550*a9656fbcSSascha Wildner if ((error = alpm_wait(sc)) != SMB_ENOERR) 551*a9656fbcSSascha Wildner goto error; 552*a9656fbcSSascha Wildner 553*a9656fbcSSascha Wildner remain -= len; 554*a9656fbcSSascha Wildner } 555*a9656fbcSSascha Wildner 556*a9656fbcSSascha Wildner error: 557*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 558*a9656fbcSSascha Wildner 559*a9656fbcSSascha Wildner return (error); 560*a9656fbcSSascha Wildner } 561*a9656fbcSSascha Wildner 562*a9656fbcSSascha Wildner static int 563*a9656fbcSSascha Wildner alpm_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) 564*a9656fbcSSascha Wildner { 565*a9656fbcSSascha Wildner struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev); 566*a9656fbcSSascha Wildner u_char remain, len, i; 567*a9656fbcSSascha Wildner int error = SMB_ENOERR; 568*a9656fbcSSascha Wildner 569*a9656fbcSSascha Wildner alpm_clear(sc); 570*a9656fbcSSascha Wildner if (!alpm_idle(sc)) 571*a9656fbcSSascha Wildner return (SMB_EBUSY); 572*a9656fbcSSascha Wildner 573*a9656fbcSSascha Wildner remain = count; 574*a9656fbcSSascha Wildner while (remain) { 575*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 576*a9656fbcSSascha Wildner 577*a9656fbcSSascha Wildner /* set the cmd and reset the 578*a9656fbcSSascha Wildner * 32-byte long internal buffer */ 579*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 580*a9656fbcSSascha Wildner 581*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBHCMD, cmd); 582*a9656fbcSSascha Wildner ALPM_SMBOUTB(sc, SMBSTART, 0xff); 583*a9656fbcSSascha Wildner 584*a9656fbcSSascha Wildner if ((error = alpm_wait(sc)) != SMB_ENOERR) 585*a9656fbcSSascha Wildner goto error; 586*a9656fbcSSascha Wildner 587*a9656fbcSSascha Wildner len = ALPM_SMBINB(sc, SMBHDATA); 588*a9656fbcSSascha Wildner 589*a9656fbcSSascha Wildner /* read the 32-byte internal buffer */ 590*a9656fbcSSascha Wildner for (i=0; i<len; i++) { 591*a9656fbcSSascha Wildner buf[count-remain+i] = ALPM_SMBINB(sc, SMBHBLOCK); 592*a9656fbcSSascha Wildner DELAY(2); 593*a9656fbcSSascha Wildner } 594*a9656fbcSSascha Wildner 595*a9656fbcSSascha Wildner remain -= len; 596*a9656fbcSSascha Wildner } 597*a9656fbcSSascha Wildner error: 598*a9656fbcSSascha Wildner ALPM_DEBUG(kprintf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 599*a9656fbcSSascha Wildner 600*a9656fbcSSascha Wildner return (error); 601*a9656fbcSSascha Wildner } 602*a9656fbcSSascha Wildner 603*a9656fbcSSascha Wildner static devclass_t alpm_devclass; 604*a9656fbcSSascha Wildner 605*a9656fbcSSascha Wildner static device_method_t alpm_methods[] = { 606*a9656fbcSSascha Wildner /* device interface */ 607*a9656fbcSSascha Wildner DEVMETHOD(device_probe, alpm_probe), 608*a9656fbcSSascha Wildner DEVMETHOD(device_attach, alpm_attach), 609*a9656fbcSSascha Wildner DEVMETHOD(device_detach, alpm_detach), 610*a9656fbcSSascha Wildner 611*a9656fbcSSascha Wildner /* smbus interface */ 612*a9656fbcSSascha Wildner DEVMETHOD(smbus_callback, alpm_callback), 613*a9656fbcSSascha Wildner DEVMETHOD(smbus_quick, alpm_quick), 614*a9656fbcSSascha Wildner DEVMETHOD(smbus_sendb, alpm_sendb), 615*a9656fbcSSascha Wildner DEVMETHOD(smbus_recvb, alpm_recvb), 616*a9656fbcSSascha Wildner DEVMETHOD(smbus_writeb, alpm_writeb), 617*a9656fbcSSascha Wildner DEVMETHOD(smbus_readb, alpm_readb), 618*a9656fbcSSascha Wildner DEVMETHOD(smbus_writew, alpm_writew), 619*a9656fbcSSascha Wildner DEVMETHOD(smbus_readw, alpm_readw), 620*a9656fbcSSascha Wildner DEVMETHOD(smbus_bwrite, alpm_bwrite), 621*a9656fbcSSascha Wildner DEVMETHOD(smbus_bread, alpm_bread), 622*a9656fbcSSascha Wildner 623*a9656fbcSSascha Wildner { 0, 0 } 624*a9656fbcSSascha Wildner }; 625*a9656fbcSSascha Wildner 626*a9656fbcSSascha Wildner static driver_t alpm_driver = { 627*a9656fbcSSascha Wildner "alpm", 628*a9656fbcSSascha Wildner alpm_methods, 629*a9656fbcSSascha Wildner sizeof(struct alpm_softc) 630*a9656fbcSSascha Wildner }; 631*a9656fbcSSascha Wildner 632*a9656fbcSSascha Wildner DRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0); 633*a9656fbcSSascha Wildner MODULE_DEPEND(alpm, pci, 1, 1, 1); 634*a9656fbcSSascha Wildner MODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 635*a9656fbcSSascha Wildner MODULE_VERSION(alpm, 1); 636