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