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