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