1*f495ec29SRui Paulo /*- 2*f495ec29SRui Paulo * Copyright (c) 2000 Matthew C. Forman 3*f495ec29SRui Paulo * 4*f495ec29SRui Paulo * Based (heavily) on alpm.c which is: 5*f495ec29SRui Paulo * 6*f495ec29SRui Paulo * Copyright (c) 1998, 1999 Nicolas Souchu 7*f495ec29SRui Paulo * All rights reserved. 8*f495ec29SRui Paulo * 9*f495ec29SRui Paulo * Redistribution and use in source and binary forms, with or without 10*f495ec29SRui Paulo * modification, are permitted provided that the following conditions 11*f495ec29SRui Paulo * are met: 12*f495ec29SRui Paulo * 1. Redistributions of source code must retain the above copyright 13*f495ec29SRui Paulo * notice, this list of conditions and the following disclaimer. 14*f495ec29SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 15*f495ec29SRui Paulo * notice, this list of conditions and the following disclaimer in the 16*f495ec29SRui Paulo * documentation and/or other materials provided with the distribution. 17*f495ec29SRui Paulo * 18*f495ec29SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19*f495ec29SRui Paulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*f495ec29SRui Paulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*f495ec29SRui Paulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22*f495ec29SRui Paulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23*f495ec29SRui Paulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24*f495ec29SRui Paulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25*f495ec29SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26*f495ec29SRui Paulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27*f495ec29SRui Paulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28*f495ec29SRui Paulo * SUCH DAMAGE. 29*f495ec29SRui Paulo */ 30*f495ec29SRui Paulo 31*f495ec29SRui Paulo /* 32*f495ec29SRui Paulo * Power management function/SMBus function support for the AMD 756 chip. 33*f495ec29SRui Paulo */ 34*f495ec29SRui Paulo 35*f495ec29SRui Paulo #include <sys/cdefs.h> 36*f495ec29SRui Paulo __FBSDID("$FreeBSD$"); 37*f495ec29SRui Paulo 38*f495ec29SRui Paulo #include <sys/param.h> 39*f495ec29SRui Paulo #include <sys/bus.h> 40*f495ec29SRui Paulo #include <sys/kernel.h> 41*f495ec29SRui Paulo #include <sys/lock.h> 42*f495ec29SRui Paulo #include <sys/module.h> 43*f495ec29SRui Paulo #include <sys/mutex.h> 44*f495ec29SRui Paulo #include <sys/systm.h> 45*f495ec29SRui Paulo 46*f495ec29SRui Paulo #include <machine/bus.h> 47*f495ec29SRui Paulo #include <machine/resource.h> 48*f495ec29SRui Paulo #include <sys/rman.h> 49*f495ec29SRui Paulo 50*f495ec29SRui Paulo #include <dev/pci/pcivar.h> 51*f495ec29SRui Paulo #include <dev/pci/pcireg.h> 52*f495ec29SRui Paulo 53*f495ec29SRui Paulo #include <dev/smbus/smbconf.h> 54*f495ec29SRui Paulo #include "smbus_if.h" 55*f495ec29SRui Paulo 56*f495ec29SRui Paulo #define AMDPM_DEBUG(x) if (amdpm_debug) (x) 57*f495ec29SRui Paulo 58*f495ec29SRui Paulo #ifdef DEBUG 59*f495ec29SRui Paulo static int amdpm_debug = 1; 60*f495ec29SRui Paulo #else 61*f495ec29SRui Paulo static int amdpm_debug = 0; 62*f495ec29SRui Paulo #endif 63*f495ec29SRui Paulo 64*f495ec29SRui Paulo #define AMDPM_VENDORID_AMD 0x1022 65*f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD756PM 0x740b 66*f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD766PM 0x7413 67*f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD768PM 0x7443 68*f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD8111PM 0x746B 69*f495ec29SRui Paulo 70*f495ec29SRui Paulo /* nVidia nForce chipset */ 71*f495ec29SRui Paulo #define AMDPM_VENDORID_NVIDIA 0x10de 72*f495ec29SRui Paulo #define AMDPM_DEVICEID_NF_SMB 0x01b4 73*f495ec29SRui Paulo 74*f495ec29SRui Paulo /* PCI Configuration space registers */ 75*f495ec29SRui Paulo #define AMDPCI_PMBASE 0x58 76*f495ec29SRui Paulo #define NFPCI_PMBASE 0x14 77*f495ec29SRui Paulo 78*f495ec29SRui Paulo #define AMDPCI_GEN_CONFIG_PM 0x41 79*f495ec29SRui Paulo #define AMDPCI_PMIOEN (1<<7) 80*f495ec29SRui Paulo 81*f495ec29SRui Paulo #define AMDPCI_SCIINT_CONFIG_PM 0x42 82*f495ec29SRui Paulo #define AMDPCI_SCISEL_IRQ11 11 83*f495ec29SRui Paulo 84*f495ec29SRui Paulo #define AMDPCI_REVID 0x08 85*f495ec29SRui Paulo 86*f495ec29SRui Paulo /* 87*f495ec29SRui Paulo * I/O registers. 88*f495ec29SRui Paulo * Base address programmed via AMDPCI_PMBASE. 89*f495ec29SRui Paulo */ 90*f495ec29SRui Paulo 91*f495ec29SRui Paulo #define AMDSMB_GLOBAL_STATUS (0x00) 92*f495ec29SRui Paulo #define AMDSMB_GS_TO_STS (1<<5) 93*f495ec29SRui Paulo #define AMDSMB_GS_HCYC_STS (1<<4) 94*f495ec29SRui Paulo #define AMDSMB_GS_HST_STS (1<<3) 95*f495ec29SRui Paulo #define AMDSMB_GS_PRERR_STS (1<<2) 96*f495ec29SRui Paulo #define AMDSMB_GS_COL_STS (1<<1) 97*f495ec29SRui Paulo #define AMDSMB_GS_ABRT_STS (1<<0) 98*f495ec29SRui Paulo #define AMDSMB_GS_CLEAR_STS (AMDSMB_GS_TO_STS|AMDSMB_GS_HCYC_STS|AMDSMB_GS_PRERR_STS|AMDSMB_GS_COL_STS|AMDSMB_GS_ABRT_STS) 99*f495ec29SRui Paulo 100*f495ec29SRui Paulo #define AMDSMB_GLOBAL_ENABLE (0x02) 101*f495ec29SRui Paulo #define AMDSMB_GE_ABORT (1<<5) 102*f495ec29SRui Paulo #define AMDSMB_GE_HCYC_EN (1<<4) 103*f495ec29SRui Paulo #define AMDSMB_GE_HOST_STC (1<<3) 104*f495ec29SRui Paulo #define AMDSMB_GE_CYC_QUICK 0 105*f495ec29SRui Paulo #define AMDSMB_GE_CYC_BYTE 1 106*f495ec29SRui Paulo #define AMDSMB_GE_CYC_BDATA 2 107*f495ec29SRui Paulo #define AMDSMB_GE_CYC_WDATA 3 108*f495ec29SRui Paulo #define AMDSMB_GE_CYC_PROCCALL 4 109*f495ec29SRui Paulo #define AMDSMB_GE_CYC_BLOCK 5 110*f495ec29SRui Paulo 111*f495ec29SRui Paulo #define LSB 0x1 /* XXX: Better name: Read/Write? */ 112*f495ec29SRui Paulo 113*f495ec29SRui Paulo #define AMDSMB_HSTADDR (0x04) 114*f495ec29SRui Paulo #define AMDSMB_HSTDATA (0x06) 115*f495ec29SRui Paulo #define AMDSMB_HSTCMD (0x08) 116*f495ec29SRui Paulo #define AMDSMB_HSTDFIFO (0x09) 117*f495ec29SRui Paulo #define AMDSMB_HSLVDATA (0x0A) 118*f495ec29SRui Paulo #define AMDSMB_HSLVDA (0x0C) 119*f495ec29SRui Paulo #define AMDSMB_HSLVDDR (0x0E) 120*f495ec29SRui Paulo #define AMDSMB_SNPADDR (0x0F) 121*f495ec29SRui Paulo 122*f495ec29SRui Paulo struct amdpm_softc { 123*f495ec29SRui Paulo int base; 124*f495ec29SRui Paulo int rid; 125*f495ec29SRui Paulo struct resource *res; 126*f495ec29SRui Paulo device_t smbus; 127*f495ec29SRui Paulo struct mtx lock; 128*f495ec29SRui Paulo }; 129*f495ec29SRui Paulo 130*f495ec29SRui Paulo #define AMDPM_LOCK(amdpm) mtx_lock(&(amdpm)->lock) 131*f495ec29SRui Paulo #define AMDPM_UNLOCK(amdpm) mtx_unlock(&(amdpm)->lock) 132*f495ec29SRui Paulo #define AMDPM_LOCK_ASSERT(amdpm) mtx_assert(&(amdpm)->lock, MA_OWNED) 133*f495ec29SRui Paulo 134*f495ec29SRui Paulo #define AMDPM_SMBINB(amdpm,register) \ 135*f495ec29SRui Paulo (bus_read_1(amdpm->res, register)) 136*f495ec29SRui Paulo #define AMDPM_SMBOUTB(amdpm,register,value) \ 137*f495ec29SRui Paulo (bus_write_1(amdpm->res, register, value)) 138*f495ec29SRui Paulo #define AMDPM_SMBINW(amdpm,register) \ 139*f495ec29SRui Paulo (bus_read_2(amdpm->res, register)) 140*f495ec29SRui Paulo #define AMDPM_SMBOUTW(amdpm,register,value) \ 141*f495ec29SRui Paulo (bus_write_2(amdpm->res, register, value)) 142*f495ec29SRui Paulo 143*f495ec29SRui Paulo static int amdpm_detach(device_t dev); 144*f495ec29SRui Paulo 145*f495ec29SRui Paulo static int 146*f495ec29SRui Paulo amdpm_probe(device_t dev) 147*f495ec29SRui Paulo { 148*f495ec29SRui Paulo u_long base; 149*f495ec29SRui Paulo u_int16_t vid; 150*f495ec29SRui Paulo u_int16_t did; 151*f495ec29SRui Paulo 152*f495ec29SRui Paulo vid = pci_get_vendor(dev); 153*f495ec29SRui Paulo did = pci_get_device(dev); 154*f495ec29SRui Paulo if ((vid == AMDPM_VENDORID_AMD) && 155*f495ec29SRui Paulo ((did == AMDPM_DEVICEID_AMD756PM) || 156*f495ec29SRui Paulo (did == AMDPM_DEVICEID_AMD766PM) || 157*f495ec29SRui Paulo (did == AMDPM_DEVICEID_AMD768PM) || 158*f495ec29SRui Paulo (did == AMDPM_DEVICEID_AMD8111PM))) { 159*f495ec29SRui Paulo device_set_desc(dev, "AMD 756/766/768/8111 Power Management Controller"); 160*f495ec29SRui Paulo 161*f495ec29SRui Paulo /* 162*f495ec29SRui Paulo * We have to do this, since the BIOS won't give us the 163*f495ec29SRui Paulo * resource info (not mine, anyway). 164*f495ec29SRui Paulo */ 165*f495ec29SRui Paulo base = pci_read_config(dev, AMDPCI_PMBASE, 4); 166*f495ec29SRui Paulo base &= 0xff00; 167*f495ec29SRui Paulo bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE, 168*f495ec29SRui Paulo base+0xe0, 32); 169*f495ec29SRui Paulo return (BUS_PROBE_DEFAULT); 170*f495ec29SRui Paulo } 171*f495ec29SRui Paulo 172*f495ec29SRui Paulo if ((vid == AMDPM_VENDORID_NVIDIA) && 173*f495ec29SRui Paulo (did == AMDPM_DEVICEID_NF_SMB)) { 174*f495ec29SRui Paulo device_set_desc(dev, "nForce SMBus Controller"); 175*f495ec29SRui Paulo 176*f495ec29SRui Paulo /* 177*f495ec29SRui Paulo * We have to do this, since the BIOS won't give us the 178*f495ec29SRui Paulo * resource info (not mine, anyway). 179*f495ec29SRui Paulo */ 180*f495ec29SRui Paulo base = pci_read_config(dev, NFPCI_PMBASE, 4); 181*f495ec29SRui Paulo base &= 0xff00; 182*f495ec29SRui Paulo bus_set_resource(dev, SYS_RES_IOPORT, NFPCI_PMBASE, 183*f495ec29SRui Paulo base, 32); 184*f495ec29SRui Paulo 185*f495ec29SRui Paulo return (BUS_PROBE_DEFAULT); 186*f495ec29SRui Paulo } 187*f495ec29SRui Paulo 188*f495ec29SRui Paulo return ENXIO; 189*f495ec29SRui Paulo } 190*f495ec29SRui Paulo 191*f495ec29SRui Paulo static int 192*f495ec29SRui Paulo amdpm_attach(device_t dev) 193*f495ec29SRui Paulo { 194*f495ec29SRui Paulo struct amdpm_softc *amdpm_sc = device_get_softc(dev); 195*f495ec29SRui Paulo u_char val_b; 196*f495ec29SRui Paulo 197*f495ec29SRui Paulo /* Enable I/O block access */ 198*f495ec29SRui Paulo val_b = pci_read_config(dev, AMDPCI_GEN_CONFIG_PM, 1); 199*f495ec29SRui Paulo pci_write_config(dev, AMDPCI_GEN_CONFIG_PM, val_b | AMDPCI_PMIOEN, 1); 200*f495ec29SRui Paulo 201*f495ec29SRui Paulo /* Allocate I/O space */ 202*f495ec29SRui Paulo if (pci_get_vendor(dev) == AMDPM_VENDORID_AMD) 203*f495ec29SRui Paulo amdpm_sc->rid = AMDPCI_PMBASE; 204*f495ec29SRui Paulo else 205*f495ec29SRui Paulo amdpm_sc->rid = NFPCI_PMBASE; 206*f495ec29SRui Paulo amdpm_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 207*f495ec29SRui Paulo &amdpm_sc->rid, RF_ACTIVE); 208*f495ec29SRui Paulo 209*f495ec29SRui Paulo if (amdpm_sc->res == NULL) { 210*f495ec29SRui Paulo device_printf(dev, "could not map i/o space\n"); 211*f495ec29SRui Paulo return (ENXIO); 212*f495ec29SRui Paulo } 213*f495ec29SRui Paulo 214*f495ec29SRui Paulo mtx_init(&amdpm_sc->lock, device_get_nameunit(dev), "amdpm", MTX_DEF); 215*f495ec29SRui Paulo 216*f495ec29SRui Paulo /* Allocate a new smbus device */ 217*f495ec29SRui Paulo amdpm_sc->smbus = device_add_child(dev, "smbus", -1); 218*f495ec29SRui Paulo if (!amdpm_sc->smbus) { 219*f495ec29SRui Paulo amdpm_detach(dev); 220*f495ec29SRui Paulo return (EINVAL); 221*f495ec29SRui Paulo } 222*f495ec29SRui Paulo 223*f495ec29SRui Paulo bus_generic_attach(dev); 224*f495ec29SRui Paulo 225*f495ec29SRui Paulo return (0); 226*f495ec29SRui Paulo } 227*f495ec29SRui Paulo 228*f495ec29SRui Paulo static int 229*f495ec29SRui Paulo amdpm_detach(device_t dev) 230*f495ec29SRui Paulo { 231*f495ec29SRui Paulo struct amdpm_softc *amdpm_sc = device_get_softc(dev); 232*f495ec29SRui Paulo 233*f495ec29SRui Paulo if (amdpm_sc->smbus) { 234*f495ec29SRui Paulo device_delete_child(dev, amdpm_sc->smbus); 235*f495ec29SRui Paulo amdpm_sc->smbus = NULL; 236*f495ec29SRui Paulo } 237*f495ec29SRui Paulo 238*f495ec29SRui Paulo mtx_destroy(&amdpm_sc->lock); 239*f495ec29SRui Paulo if (amdpm_sc->res) 240*f495ec29SRui Paulo bus_release_resource(dev, SYS_RES_IOPORT, amdpm_sc->rid, 241*f495ec29SRui Paulo amdpm_sc->res); 242*f495ec29SRui Paulo 243*f495ec29SRui Paulo return (0); 244*f495ec29SRui Paulo } 245*f495ec29SRui Paulo 246*f495ec29SRui Paulo static int 247*f495ec29SRui Paulo amdpm_callback(device_t dev, int index, void *data) 248*f495ec29SRui Paulo { 249*f495ec29SRui Paulo int error = 0; 250*f495ec29SRui Paulo 251*f495ec29SRui Paulo switch (index) { 252*f495ec29SRui Paulo case SMB_REQUEST_BUS: 253*f495ec29SRui Paulo case SMB_RELEASE_BUS: 254*f495ec29SRui Paulo break; 255*f495ec29SRui Paulo default: 256*f495ec29SRui Paulo error = EINVAL; 257*f495ec29SRui Paulo } 258*f495ec29SRui Paulo 259*f495ec29SRui Paulo return (error); 260*f495ec29SRui Paulo } 261*f495ec29SRui Paulo 262*f495ec29SRui Paulo static int 263*f495ec29SRui Paulo amdpm_clear(struct amdpm_softc *sc) 264*f495ec29SRui Paulo { 265*f495ec29SRui Paulo 266*f495ec29SRui Paulo AMDPM_LOCK_ASSERT(sc); 267*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_STATUS, AMDSMB_GS_CLEAR_STS); 268*f495ec29SRui Paulo DELAY(10); 269*f495ec29SRui Paulo 270*f495ec29SRui Paulo return (0); 271*f495ec29SRui Paulo } 272*f495ec29SRui Paulo 273*f495ec29SRui Paulo #if 0 274*f495ec29SRui Paulo static int 275*f495ec29SRui Paulo amdpm_abort(struct amdpm_softc *sc) 276*f495ec29SRui Paulo { 277*f495ec29SRui Paulo u_short l; 278*f495ec29SRui Paulo 279*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 280*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, l | AMDSMB_GE_ABORT); 281*f495ec29SRui Paulo 282*f495ec29SRui Paulo return (0); 283*f495ec29SRui Paulo } 284*f495ec29SRui Paulo #endif 285*f495ec29SRui Paulo 286*f495ec29SRui Paulo static int 287*f495ec29SRui Paulo amdpm_idle(struct amdpm_softc *sc) 288*f495ec29SRui Paulo { 289*f495ec29SRui Paulo u_short sts; 290*f495ec29SRui Paulo 291*f495ec29SRui Paulo AMDPM_LOCK_ASSERT(sc); 292*f495ec29SRui Paulo sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS); 293*f495ec29SRui Paulo 294*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: busy? STS=0x%x\n", sts)); 295*f495ec29SRui Paulo 296*f495ec29SRui Paulo return (~(sts & AMDSMB_GS_HST_STS)); 297*f495ec29SRui Paulo } 298*f495ec29SRui Paulo 299*f495ec29SRui Paulo /* 300*f495ec29SRui Paulo * Poll the SMBus controller 301*f495ec29SRui Paulo */ 302*f495ec29SRui Paulo static int 303*f495ec29SRui Paulo amdpm_wait(struct amdpm_softc *sc) 304*f495ec29SRui Paulo { 305*f495ec29SRui Paulo int count = 10000; 306*f495ec29SRui Paulo u_short sts = 0; 307*f495ec29SRui Paulo int error; 308*f495ec29SRui Paulo 309*f495ec29SRui Paulo AMDPM_LOCK_ASSERT(sc); 310*f495ec29SRui Paulo /* Wait for command to complete (SMBus controller is idle) */ 311*f495ec29SRui Paulo while(count--) { 312*f495ec29SRui Paulo DELAY(10); 313*f495ec29SRui Paulo sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS); 314*f495ec29SRui Paulo if (!(sts & AMDSMB_GS_HST_STS)) 315*f495ec29SRui Paulo break; 316*f495ec29SRui Paulo } 317*f495ec29SRui Paulo 318*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: STS=0x%x (count=%d)\n", sts, count)); 319*f495ec29SRui Paulo 320*f495ec29SRui Paulo error = SMB_ENOERR; 321*f495ec29SRui Paulo 322*f495ec29SRui Paulo if (!count) 323*f495ec29SRui Paulo error |= SMB_ETIMEOUT; 324*f495ec29SRui Paulo 325*f495ec29SRui Paulo if (sts & AMDSMB_GS_ABRT_STS) 326*f495ec29SRui Paulo error |= SMB_EABORT; 327*f495ec29SRui Paulo 328*f495ec29SRui Paulo if (sts & AMDSMB_GS_COL_STS) 329*f495ec29SRui Paulo error |= SMB_ENOACK; 330*f495ec29SRui Paulo 331*f495ec29SRui Paulo if (sts & AMDSMB_GS_PRERR_STS) 332*f495ec29SRui Paulo error |= SMB_EBUSERR; 333*f495ec29SRui Paulo 334*f495ec29SRui Paulo if (error != SMB_ENOERR) 335*f495ec29SRui Paulo amdpm_clear(sc); 336*f495ec29SRui Paulo 337*f495ec29SRui Paulo return (error); 338*f495ec29SRui Paulo } 339*f495ec29SRui Paulo 340*f495ec29SRui Paulo static int 341*f495ec29SRui Paulo amdpm_quick(device_t dev, u_char slave, int how) 342*f495ec29SRui Paulo { 343*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 344*f495ec29SRui Paulo int error; 345*f495ec29SRui Paulo u_short l; 346*f495ec29SRui Paulo 347*f495ec29SRui Paulo AMDPM_LOCK(sc); 348*f495ec29SRui Paulo amdpm_clear(sc); 349*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 350*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 351*f495ec29SRui Paulo return (EBUSY); 352*f495ec29SRui Paulo } 353*f495ec29SRui Paulo 354*f495ec29SRui Paulo switch (how) { 355*f495ec29SRui Paulo case SMB_QWRITE: 356*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: QWRITE to 0x%x", slave)); 357*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 358*f495ec29SRui Paulo break; 359*f495ec29SRui Paulo case SMB_QREAD: 360*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: QREAD to 0x%x", slave)); 361*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 362*f495ec29SRui Paulo break; 363*f495ec29SRui Paulo default: 364*f495ec29SRui Paulo panic("%s: unknown QUICK command (%x)!", __func__, how); 365*f495ec29SRui Paulo } 366*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 367*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_QUICK | AMDSMB_GE_HOST_STC); 368*f495ec29SRui Paulo 369*f495ec29SRui Paulo error = amdpm_wait(sc); 370*f495ec29SRui Paulo 371*f495ec29SRui Paulo AMDPM_DEBUG(printf(", error=0x%x\n", error)); 372*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 373*f495ec29SRui Paulo 374*f495ec29SRui Paulo return (error); 375*f495ec29SRui Paulo } 376*f495ec29SRui Paulo 377*f495ec29SRui Paulo static int 378*f495ec29SRui Paulo amdpm_sendb(device_t dev, u_char slave, char byte) 379*f495ec29SRui Paulo { 380*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 381*f495ec29SRui Paulo int error; 382*f495ec29SRui Paulo u_short l; 383*f495ec29SRui Paulo 384*f495ec29SRui Paulo AMDPM_LOCK(sc); 385*f495ec29SRui Paulo amdpm_clear(sc); 386*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 387*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 388*f495ec29SRui Paulo return (SMB_EBUSY); 389*f495ec29SRui Paulo } 390*f495ec29SRui Paulo 391*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 392*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte); 393*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 394*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC); 395*f495ec29SRui Paulo 396*f495ec29SRui Paulo error = amdpm_wait(sc); 397*f495ec29SRui Paulo 398*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); 399*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 400*f495ec29SRui Paulo 401*f495ec29SRui Paulo return (error); 402*f495ec29SRui Paulo } 403*f495ec29SRui Paulo 404*f495ec29SRui Paulo static int 405*f495ec29SRui Paulo amdpm_recvb(device_t dev, u_char slave, char *byte) 406*f495ec29SRui Paulo { 407*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 408*f495ec29SRui Paulo int error; 409*f495ec29SRui Paulo u_short l; 410*f495ec29SRui Paulo 411*f495ec29SRui Paulo AMDPM_LOCK(sc); 412*f495ec29SRui Paulo amdpm_clear(sc); 413*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 414*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 415*f495ec29SRui Paulo return (SMB_EBUSY); 416*f495ec29SRui Paulo } 417*f495ec29SRui Paulo 418*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 419*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 420*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC); 421*f495ec29SRui Paulo 422*f495ec29SRui Paulo if ((error = amdpm_wait(sc)) == SMB_ENOERR) 423*f495ec29SRui Paulo *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 424*f495ec29SRui Paulo 425*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); 426*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 427*f495ec29SRui Paulo 428*f495ec29SRui Paulo return (error); 429*f495ec29SRui Paulo } 430*f495ec29SRui Paulo 431*f495ec29SRui Paulo static int 432*f495ec29SRui Paulo amdpm_writeb(device_t dev, u_char slave, char cmd, char byte) 433*f495ec29SRui Paulo { 434*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 435*f495ec29SRui Paulo int error; 436*f495ec29SRui Paulo u_short l; 437*f495ec29SRui Paulo 438*f495ec29SRui Paulo AMDPM_LOCK(sc); 439*f495ec29SRui Paulo amdpm_clear(sc); 440*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 441*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 442*f495ec29SRui Paulo return (SMB_EBUSY); 443*f495ec29SRui Paulo } 444*f495ec29SRui Paulo 445*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 446*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte); 447*f495ec29SRui Paulo AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 448*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 449*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC); 450*f495ec29SRui Paulo 451*f495ec29SRui Paulo error = amdpm_wait(sc); 452*f495ec29SRui Paulo 453*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); 454*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 455*f495ec29SRui Paulo 456*f495ec29SRui Paulo return (error); 457*f495ec29SRui Paulo } 458*f495ec29SRui Paulo 459*f495ec29SRui Paulo static int 460*f495ec29SRui Paulo amdpm_readb(device_t dev, u_char slave, char cmd, char *byte) 461*f495ec29SRui Paulo { 462*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 463*f495ec29SRui Paulo int error; 464*f495ec29SRui Paulo u_short l; 465*f495ec29SRui Paulo 466*f495ec29SRui Paulo AMDPM_LOCK(sc); 467*f495ec29SRui Paulo amdpm_clear(sc); 468*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 469*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 470*f495ec29SRui Paulo return (SMB_EBUSY); 471*f495ec29SRui Paulo } 472*f495ec29SRui Paulo 473*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 474*f495ec29SRui Paulo AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 475*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 476*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC); 477*f495ec29SRui Paulo 478*f495ec29SRui Paulo if ((error = amdpm_wait(sc)) == SMB_ENOERR) 479*f495ec29SRui Paulo *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 480*f495ec29SRui Paulo 481*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); 482*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 483*f495ec29SRui Paulo 484*f495ec29SRui Paulo return (error); 485*f495ec29SRui Paulo } 486*f495ec29SRui Paulo 487*f495ec29SRui Paulo static int 488*f495ec29SRui Paulo amdpm_writew(device_t dev, u_char slave, char cmd, short word) 489*f495ec29SRui Paulo { 490*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 491*f495ec29SRui Paulo int error; 492*f495ec29SRui Paulo u_short l; 493*f495ec29SRui Paulo 494*f495ec29SRui Paulo AMDPM_LOCK(sc); 495*f495ec29SRui Paulo amdpm_clear(sc); 496*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 497*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 498*f495ec29SRui Paulo return (SMB_EBUSY); 499*f495ec29SRui Paulo } 500*f495ec29SRui Paulo 501*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 502*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, word); 503*f495ec29SRui Paulo AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 504*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 505*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC); 506*f495ec29SRui Paulo 507*f495ec29SRui Paulo error = amdpm_wait(sc); 508*f495ec29SRui Paulo 509*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); 510*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 511*f495ec29SRui Paulo 512*f495ec29SRui Paulo return (error); 513*f495ec29SRui Paulo } 514*f495ec29SRui Paulo 515*f495ec29SRui Paulo static int 516*f495ec29SRui Paulo amdpm_readw(device_t dev, u_char slave, char cmd, short *word) 517*f495ec29SRui Paulo { 518*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 519*f495ec29SRui Paulo int error; 520*f495ec29SRui Paulo u_short l; 521*f495ec29SRui Paulo 522*f495ec29SRui Paulo AMDPM_LOCK(sc); 523*f495ec29SRui Paulo amdpm_clear(sc); 524*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 525*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 526*f495ec29SRui Paulo return (SMB_EBUSY); 527*f495ec29SRui Paulo } 528*f495ec29SRui Paulo 529*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 530*f495ec29SRui Paulo AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 531*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 532*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC); 533*f495ec29SRui Paulo 534*f495ec29SRui Paulo if ((error = amdpm_wait(sc)) == SMB_ENOERR) 535*f495ec29SRui Paulo *word = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 536*f495ec29SRui Paulo 537*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); 538*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 539*f495ec29SRui Paulo 540*f495ec29SRui Paulo return (error); 541*f495ec29SRui Paulo } 542*f495ec29SRui Paulo 543*f495ec29SRui Paulo static int 544*f495ec29SRui Paulo amdpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 545*f495ec29SRui Paulo { 546*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 547*f495ec29SRui Paulo u_char i; 548*f495ec29SRui Paulo int error; 549*f495ec29SRui Paulo u_short l; 550*f495ec29SRui Paulo 551*f495ec29SRui Paulo if (count < 1 || count > 32) 552*f495ec29SRui Paulo return (SMB_EINVAL); 553*f495ec29SRui Paulo 554*f495ec29SRui Paulo AMDPM_LOCK(sc); 555*f495ec29SRui Paulo amdpm_clear(sc); 556*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 557*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 558*f495ec29SRui Paulo return (SMB_EBUSY); 559*f495ec29SRui Paulo } 560*f495ec29SRui Paulo 561*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB); 562*f495ec29SRui Paulo 563*f495ec29SRui Paulo /* 564*f495ec29SRui Paulo * Do we have to reset the internal 32-byte buffer? 565*f495ec29SRui Paulo * Can't see how to do this from the data sheet. 566*f495ec29SRui Paulo */ 567*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, count); 568*f495ec29SRui Paulo 569*f495ec29SRui Paulo /* Fill the 32-byte internal buffer */ 570*f495ec29SRui Paulo for (i = 0; i < count; i++) { 571*f495ec29SRui Paulo AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[i]); 572*f495ec29SRui Paulo DELAY(2); 573*f495ec29SRui Paulo } 574*f495ec29SRui Paulo AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 575*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 576*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, 577*f495ec29SRui Paulo (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC); 578*f495ec29SRui Paulo 579*f495ec29SRui Paulo error = amdpm_wait(sc); 580*f495ec29SRui Paulo 581*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 582*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 583*f495ec29SRui Paulo 584*f495ec29SRui Paulo return (error); 585*f495ec29SRui Paulo } 586*f495ec29SRui Paulo 587*f495ec29SRui Paulo static int 588*f495ec29SRui Paulo amdpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 589*f495ec29SRui Paulo { 590*f495ec29SRui Paulo struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev); 591*f495ec29SRui Paulo u_char data, len, i; 592*f495ec29SRui Paulo int error; 593*f495ec29SRui Paulo u_short l; 594*f495ec29SRui Paulo 595*f495ec29SRui Paulo if (*count < 1 || *count > 32) 596*f495ec29SRui Paulo return (SMB_EINVAL); 597*f495ec29SRui Paulo 598*f495ec29SRui Paulo AMDPM_LOCK(sc); 599*f495ec29SRui Paulo amdpm_clear(sc); 600*f495ec29SRui Paulo if (!amdpm_idle(sc)) { 601*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 602*f495ec29SRui Paulo return (SMB_EBUSY); 603*f495ec29SRui Paulo } 604*f495ec29SRui Paulo 605*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB); 606*f495ec29SRui Paulo 607*f495ec29SRui Paulo AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd); 608*f495ec29SRui Paulo 609*f495ec29SRui Paulo l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE); 610*f495ec29SRui Paulo AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, 611*f495ec29SRui Paulo (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC); 612*f495ec29SRui Paulo 613*f495ec29SRui Paulo if ((error = amdpm_wait(sc)) != SMB_ENOERR) 614*f495ec29SRui Paulo goto error; 615*f495ec29SRui Paulo 616*f495ec29SRui Paulo len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA); 617*f495ec29SRui Paulo 618*f495ec29SRui Paulo /* Read the 32-byte internal buffer */ 619*f495ec29SRui Paulo for (i = 0; i < len; i++) { 620*f495ec29SRui Paulo data = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO); 621*f495ec29SRui Paulo if (i < *count) 622*f495ec29SRui Paulo buf[i] = data; 623*f495ec29SRui Paulo DELAY(2); 624*f495ec29SRui Paulo } 625*f495ec29SRui Paulo *count = len; 626*f495ec29SRui Paulo 627*f495ec29SRui Paulo error: 628*f495ec29SRui Paulo AMDPM_DEBUG(printf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error)); 629*f495ec29SRui Paulo AMDPM_UNLOCK(sc); 630*f495ec29SRui Paulo 631*f495ec29SRui Paulo return (error); 632*f495ec29SRui Paulo } 633*f495ec29SRui Paulo 634*f495ec29SRui Paulo static devclass_t amdpm_devclass; 635*f495ec29SRui Paulo 636*f495ec29SRui Paulo static device_method_t amdpm_methods[] = { 637*f495ec29SRui Paulo /* Device interface */ 638*f495ec29SRui Paulo DEVMETHOD(device_probe, amdpm_probe), 639*f495ec29SRui Paulo DEVMETHOD(device_attach, amdpm_attach), 640*f495ec29SRui Paulo DEVMETHOD(device_detach, amdpm_detach), 641*f495ec29SRui Paulo 642*f495ec29SRui Paulo /* SMBus interface */ 643*f495ec29SRui Paulo DEVMETHOD(smbus_callback, amdpm_callback), 644*f495ec29SRui Paulo DEVMETHOD(smbus_quick, amdpm_quick), 645*f495ec29SRui Paulo DEVMETHOD(smbus_sendb, amdpm_sendb), 646*f495ec29SRui Paulo DEVMETHOD(smbus_recvb, amdpm_recvb), 647*f495ec29SRui Paulo DEVMETHOD(smbus_writeb, amdpm_writeb), 648*f495ec29SRui Paulo DEVMETHOD(smbus_readb, amdpm_readb), 649*f495ec29SRui Paulo DEVMETHOD(smbus_writew, amdpm_writew), 650*f495ec29SRui Paulo DEVMETHOD(smbus_readw, amdpm_readw), 651*f495ec29SRui Paulo DEVMETHOD(smbus_bwrite, amdpm_bwrite), 652*f495ec29SRui Paulo DEVMETHOD(smbus_bread, amdpm_bread), 653*f495ec29SRui Paulo 654*f495ec29SRui Paulo { 0, 0 } 655*f495ec29SRui Paulo }; 656*f495ec29SRui Paulo 657*f495ec29SRui Paulo static driver_t amdpm_driver = { 658*f495ec29SRui Paulo "amdpm", 659*f495ec29SRui Paulo amdpm_methods, 660*f495ec29SRui Paulo sizeof(struct amdpm_softc), 661*f495ec29SRui Paulo }; 662*f495ec29SRui Paulo 663*f495ec29SRui Paulo DRIVER_MODULE(amdpm, pci, amdpm_driver, amdpm_devclass, 0, 0); 664*f495ec29SRui Paulo DRIVER_MODULE(smbus, amdpm, smbus_driver, smbus_devclass, 0, 0); 665*f495ec29SRui Paulo 666*f495ec29SRui Paulo MODULE_DEPEND(amdpm, pci, 1, 1, 1); 667*f495ec29SRui Paulo MODULE_DEPEND(amdpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 668*f495ec29SRui Paulo MODULE_VERSION(amdpm, 1); 669