Sync amdpm(4) with FreeBSD (and bring in amdsmb(4)).
authorSascha Wildner <saw@online.de>
Tue, 17 Aug 2010 05:02:07 +0000 (07:02 +0200)
committerSascha Wildner <saw@online.de>
Tue, 17 Aug 2010 05:03:09 +0000 (07:03 +0200)
Makefile_upgrade.inc
share/man/man4/Makefile
share/man/man4/amdpm.4 [copied from share/man/man4/man4.i386/amdpm.4 with 81% similarity]
share/man/man4/amdsmb.4 [moved from share/man/man4/man4.i386/amdpm.4 with 59% similarity]
share/man/man4/man4.i386/Makefile
sys/dev/powermng/Makefile
sys/dev/powermng/amdpm/amdpm.c
sys/dev/powermng/amdsmb/Makefile [new file with mode: 0644]
sys/dev/powermng/amdsmb/amdsmb.c [new file with mode: 0644]

index edfa573..af50e49 100644 (file)
@@ -1496,6 +1496,8 @@ TO_REMOVE+=/usr/share/man/cat3/evdns_search_ndots_set.3.gz
 TO_REMOVE+=/usr/share/man/cat3/evdns_set_log_fn.3.gz
 TO_REMOVE+=/usr/share/man/cat4/i386/alpm.4.gz
 TO_REMOVE+=/usr/share/man/man4/i386/alpm.4.gz
+TO_REMOVE+=/usr/share/man/cat4/i386/amdpm.4.gz
+TO_REMOVE+=/usr/share/man/man4/i386/amdpm.4.gz
 
 # XXX Remove when adjusted to the new 802.11 framework
 TO_REMOVE+=/boot/modules/if_acx.ko
index 46a751a..90af184 100644 (file)
@@ -28,6 +28,8 @@ MAN=  aac.4 \
        alpm.4 \
        altq.4 \
        amd.4 \
+       amdpm.4 \
+       amdsmb.4 \
        amr.4 \
        an.4 \
        aps.4 \
similarity index 81%
copy from share/man/man4/man4.i386/amdpm.4
copy to share/man/man4/amdpm.4
index dc5871f..f3a1141 100644 (file)
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/share/man/man4/man4.i386/amdpm.4,v 1.1.2.3 2002/03/12 12:28:48 murray Exp $
-.\" $DragonFly: src/share/man/man4/man4.i386/amdpm.4,v 1.3 2006/11/11 18:50:04 swildner Exp $
+.\" $FreeBSD: src/share/man/man4/amdpm.4,v 1.1 2010/02/08 23:30:28 gavin Exp $
 .\"
-.Dd September 16, 2001
-.Dt AMDPM 4 i386
+.Dd August 17, 2010
+.Dt AMDPM 4
 .Os
 .Sh NAME
 .Nm amdpm
-.Nd AMD 756 Power Management controller driver
+.Nd AMD 756/766/768/8111 Power Management controller driver
 .Sh SYNOPSIS
 .Cd device smbus
 .Cd device smb
 .Cd device amdpm
 .Sh DESCRIPTION
-This driver provides access to the
-.Tn AMD 756 Power management controller .
-Currently, only smbus controller function is implemented.
+This driver provides access to
+.Tn AMD 756/766/768/8111 Power management controllers .
+Currently, only the SMBus 1.0 controller function is implemented.
+The SMBus 2.0 functionality of the AMD 8111 controller is supported via the
+.Xr amdsmb 4
+driver.
 .Pp
 The embedded SMBus controller of the AMD 756 chipset may give you access
 to the monitoring facilities of your mainboard.
@@ -48,6 +50,7 @@ See
 for writing user code to fetch voltages, temperature and so on from the
 monitoring chip of your mainboard.
 .Sh SEE ALSO
+.Xr amdsmb 4 ,
 .Xr smb 4 ,
 .Xr smbus 4
 .Sh HISTORY
@@ -58,7 +61,7 @@ driver first appeared in
 .Sh AUTHORS
 .An -nosplit
 This driver was written by
-.An Matthew C. Forman .
+.An "Matthew C. Forman" .
 Based heavily on the
 .Nm alpm
 driver by
similarity index 59%
rename from share/man/man4/man4.i386/amdpm.4
rename to share/man/man4/amdsmb.4
index dc5871f..7c280c3 100644 (file)
@@ -1,5 +1,4 @@
-.\" Copyright (c) 2001 Murray Stokely
-.\" Copyright (c) 1999 Takanori Watanabe
+.\" Copyright (c) 2005 Christian Brueffer
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/share/man/man4/man4.i386/amdpm.4,v 1.1.2.3 2002/03/12 12:28:48 murray Exp $
-.\" $DragonFly: src/share/man/man4/man4.i386/amdpm.4,v 1.3 2006/11/11 18:50:04 swildner Exp $
+.\" $FreeBSD: src/share/man/man4/amdsmb.4,v 1.3 2006/09/30 15:14:48 ru Exp $
 .\"
-.Dd September 16, 2001
-.Dt AMDPM 4 i386
+.Dd December 31, 2005
+.Dt AMDSMB 4
 .Os
 .Sh NAME
-.Nm amdpm
-.Nd AMD 756 Power Management controller driver
+.Nm amdsmb
+.Nd "AMD-8111 SMBus 2.0 controller driver"
 .Sh SYNOPSIS
-.Cd device smbus
-.Cd device smb
-.Cd device amdpm
+.Cd "device smbus"
+.Cd "device smb"
+.Cd "device amdsmb
 .Sh DESCRIPTION
-This driver provides access to the
-.Tn AMD 756 Power management controller .
-Currently, only smbus controller function is implemented.
-.Pp
-The embedded SMBus controller of the AMD 756 chipset may give you access
-to the monitoring facilities of your mainboard.
-See
-.Xr smb 4
-for writing user code to fetch voltages, temperature and so on from the
-monitoring chip of your mainboard.
+The
+.Nm
+driver provides access to the AMD-8111 SMBus 2.0 controller.
 .Sh SEE ALSO
 .Xr smb 4 ,
 .Xr smbus 4
@@ -54,16 +45,10 @@ monitoring chip of your mainboard.
 The
 .Nm
 driver first appeared in
-.Fx 4.5 .
+.Fx 6.2 .
 .Sh AUTHORS
 .An -nosplit
-This driver was written by
-.An Matthew C. Forman .
-Based heavily on the
-.Nm alpm
-driver by
-.An Nicolas Souchu .
-This manual page was written by
-.An Murray Stokely Aq murray@FreeBSD.org .
-.Sh BUGS
-Only polling mode is supported.
+The
+.Nm
+driver was written by
+.An Ruslan Ermilov Aq ru@FreeBSD.org .
index 59e1846..a1e1d71 100644 (file)
@@ -1,7 +1,7 @@
 # $FreeBSD: src/share/man/man4/man4.i386/Makefile,v 1.122.2.12 2003/04/15 17:23:32 fjoe Exp $
 # $DragonFly: src/share/man/man4/man4.i386/Makefile,v 1.15 2008/08/28 09:47:04 hasso Exp $
 
-MAN=   aic.4 amdpm.4 apm.4 ar.4 asc.4 asr.4 \
+MAN=   aic.4 apm.4 ar.4 asc.4 asr.4 \
        cs.4 cy.4 \
        dgb.4 digi.4 el.4 en.4 ep.4 est.4 ex.4 fe.4 glxsb.4 gsc.4 \
        ie.4 io.4 labpc.4 le.4 linux.4 lnc.4 longrun.4 \
index 0f5fcca..508abe6 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIR=        alpm amdpm aps coretemp ichsmb intpm kate km lm viapm wbsio
+SUBDIR=        alpm amdpm amdsmb aps coretemp ichsmb intpm kate km lm viapm wbsio
 
 .if ${MACHINE_ARCH} == "i386"
 SUBDIR+= powernow
index 6062835..5ac9558 100644 (file)
@@ -27,7 +27,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/pci/amdpm.c,v 1.10 2003/09/06 13:56:56 dfr Exp $
+ * $FreeBSD: src/sys/pci/amdpm.c,v 1.22 2008/06/06 18:29:56 jhb Exp $
  *
  */
 
  */
 
 #include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/globaldata.h>
 #include <sys/kernel.h>
-#include <sys/systm.h>
+#include <sys/lock.h>
 #include <sys/module.h>
-#include <sys/bus.h>
-#include <sys/uio.h>
 #include <sys/rman.h>
-
-#include <machine/clock.h>
+#include <sys/systm.h>
 
 #include <bus/pci/pcivar.h>
 #include <bus/pci/pcireg.h>
 
-#include <bus/iicbus/iiconf.h>
 #include <bus/smbus/smbconf.h>
 #include "smbus_if.h"
 
@@ -64,12 +62,12 @@ static int amdpm_debug = 0;
 #define AMDPM_DEVICEID_AMD756PM 0x740b
 #define AMDPM_DEVICEID_AMD766PM 0x7413
 #define AMDPM_DEVICEID_AMD768PM 0x7443
+#define AMDPM_DEVICEID_AMD8111PM 0x746B
 
 /* nVidia nForce chipset */
 #define AMDPM_VENDORID_NVIDIA 0x10de
 #define AMDPM_DEVICEID_NF_SMB 0x01b4
 
-
 /* PCI Configuration space registers */
 #define AMDPCI_PMBASE 0x58
 #define NFPCI_PMBASE  0x14
@@ -107,6 +105,8 @@ static int amdpm_debug = 0;
 #define AMDSMB_GE_CYC_PROCCALL 4
 #define AMDSMB_GE_CYC_BLOCK 5
 
+#define        LSB             0x1     /* XXX: Better name: Read/Write? */
+
 #define AMDSMB_HSTADDR  (0x04)
 #define AMDSMB_HSTDATA  (0x06)
 #define AMDSMB_HSTCMD   (0x08)
@@ -120,20 +120,24 @@ struct amdpm_softc {
        int base;
        int rid;
        struct resource *res;
-       bus_space_tag_t smbst;
-       bus_space_handle_t smbsh;
-
        device_t smbus;
+       struct lock lock;
 };
 
+#define        AMDPM_LOCK(amdpm)               lockmgr(&(amdpm)->lock, LK_EXCLUSIVE)
+#define        AMDPM_UNLOCK(amdpm)             lockmgr(&(amdpm)->lock, LK_RELEASE)
+#define        AMDPM_LOCK_ASSERT(amdpm)        KKASSERT(lockstatus(&(amdpm)->lock, curthread) != 0)
+
 #define AMDPM_SMBINB(amdpm,register) \
-       (bus_space_read_1(amdpm->smbst, amdpm->smbsh, register))
+       (bus_read_1(amdpm->res, register))
 #define AMDPM_SMBOUTB(amdpm,register,value) \
-       (bus_space_write_1(amdpm->smbst, amdpm->smbsh, register, value))
+       (bus_write_1(amdpm->res, register, value))
 #define AMDPM_SMBINW(amdpm,register) \
-       (bus_space_read_2(amdpm->smbst, amdpm->smbsh, register))
+       (bus_read_2(amdpm->res, register))
 #define AMDPM_SMBOUTW(amdpm,register,value) \
-       (bus_space_write_2(amdpm->smbst, amdpm->smbsh, register, value))
+       (bus_write_2(amdpm->res, register, value))
+
+static int     amdpm_detach(device_t dev);
 
 static int
 amdpm_probe(device_t dev)
@@ -147,18 +151,19 @@ amdpm_probe(device_t dev)
        if ((vid == AMDPM_VENDORID_AMD) &&
            ((did == AMDPM_DEVICEID_AMD756PM) ||
             (did == AMDPM_DEVICEID_AMD766PM) ||
-            (did == AMDPM_DEVICEID_AMD768PM))) {
-             device_set_desc(dev, "AMD 756/766/768 Power Management Controller");
-
-             /*
-              * We have to do this, since the BIOS won't give us the
-              * resource info (not mine, anyway).
-              */
-             base = pci_read_config(dev, AMDPCI_PMBASE, 4);
-             base &= 0xff00;
-             bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE,
-                              base+0xe0, 32);
-             return (0);
+            (did == AMDPM_DEVICEID_AMD768PM) ||
+            (did == AMDPM_DEVICEID_AMD8111PM))) {
+               device_set_desc(dev, "AMD 756/766/768/8111 Power Management Controller");
+
+               /*
+                * We have to do this, since the BIOS won't give us the
+                * resource info (not mine, anyway).
+                */
+               base = pci_read_config(dev, AMDPCI_PMBASE, 4);
+               base &= 0xff00;
+               bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE,
+                                base+0xe0, 32);
+               return (BUS_PROBE_DEFAULT);
        }
 
        if ((vid == AMDPM_VENDORID_NVIDIA) &&
@@ -174,7 +179,7 @@ amdpm_probe(device_t dev)
                bus_set_resource(dev, SYS_RES_IOPORT, NFPCI_PMBASE,
                                 base, 32);
 
-               return (0);
+               return (BUS_PROBE_DEFAULT);
        }
 
        return ENXIO;
@@ -195,20 +200,22 @@ amdpm_attach(device_t dev)
                amdpm_sc->rid = AMDPCI_PMBASE;
        else
                amdpm_sc->rid = NFPCI_PMBASE;
-       amdpm_sc->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &amdpm_sc->rid, 0, ~0, 1, RF_ACTIVE);
+       amdpm_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
+               &amdpm_sc->rid, RF_ACTIVE);
 
        if (amdpm_sc->res == NULL) {
                device_printf(dev, "could not map i/o space\n");
                return (ENXIO);
        }
 
-       amdpm_sc->smbst = rman_get_bustag(amdpm_sc->res);
-       amdpm_sc->smbsh = rman_get_bushandle(amdpm_sc->res);
+       lockinit(&amdpm_sc->lock, "amdpm", 0, LK_CANRECURSE);
 
        /* Allocate a new smbus device */
        amdpm_sc->smbus = device_add_child(dev, "smbus", -1);
-       if (!amdpm_sc->smbus)
-         return (EINVAL);
+       if (!amdpm_sc->smbus) {
+               amdpm_detach(dev);
+               return (EINVAL);
+       }
 
        bus_generic_attach(dev);
 
@@ -221,19 +228,20 @@ amdpm_detach(device_t dev)
        struct amdpm_softc *amdpm_sc = device_get_softc(dev);
 
        if (amdpm_sc->smbus) {
-         device_delete_child(dev, amdpm_sc->smbus);
-         amdpm_sc->smbus = NULL;
+               device_delete_child(dev, amdpm_sc->smbus);
+               amdpm_sc->smbus = NULL;
        }
 
+       lockuninit(&amdpm_sc->lock);
        if (amdpm_sc->res)
-         bus_release_resource(dev, SYS_RES_IOPORT, amdpm_sc->rid,
-                                       amdpm_sc->res);
+               bus_release_resource(dev, SYS_RES_IOPORT, amdpm_sc->rid,
+                                    amdpm_sc->res);
 
        return (0);
 }
 
 static int
-amdpm_callback(device_t dev, int index, caddr_t *data)
+amdpm_callback(device_t dev, int index, void *data)
 {
        int error = 0;
 
@@ -251,6 +259,8 @@ amdpm_callback(device_t dev, int index, caddr_t *data)
 static int
 amdpm_clear(struct amdpm_softc *sc)
 {
+
+       AMDPM_LOCK_ASSERT(sc);
        AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_STATUS, AMDSMB_GS_CLEAR_STS);
        DELAY(10);
 
@@ -275,6 +285,7 @@ amdpm_idle(struct amdpm_softc *sc)
 {
        u_short sts;
 
+       AMDPM_LOCK_ASSERT(sc);
        sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS);
 
        AMDPM_DEBUG(kprintf("amdpm: busy? STS=0x%x\n", sts));
@@ -292,6 +303,7 @@ amdpm_wait(struct amdpm_softc *sc)
        u_short sts = 0;
        int error;
 
+       AMDPM_LOCK_ASSERT(sc);
        /* Wait for command to complete (SMBus controller is idle) */
        while(count--) {
                DELAY(10);
@@ -329,9 +341,12 @@ amdpm_quick(device_t dev, u_char slave, int how)
        int error;
        u_short l;
 
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (EBUSY);
+       }
 
        switch (how) {
        case SMB_QWRITE:
@@ -351,6 +366,7 @@ amdpm_quick(device_t dev, u_char slave, int how)
        error = amdpm_wait(sc);
 
        AMDPM_DEBUG(kprintf(", error=0x%x\n", error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -362,9 +378,12 @@ amdpm_sendb(device_t dev, u_char slave, char byte)
        int error;
        u_short l;
 
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
        AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
        AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
@@ -374,6 +393,7 @@ amdpm_sendb(device_t dev, u_char slave, char byte)
        error = amdpm_wait(sc);
 
        AMDPM_DEBUG(kprintf("amdpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -385,9 +405,12 @@ amdpm_recvb(device_t dev, u_char slave, char *byte)
        int error;
        u_short l;
 
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
        AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
        l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
@@ -397,6 +420,7 @@ amdpm_recvb(device_t dev, u_char slave, char *byte)
                *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
 
        AMDPM_DEBUG(kprintf("amdpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -408,9 +432,12 @@ amdpm_writeb(device_t dev, u_char slave, char cmd, char byte)
        int error;
        u_short l;
 
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
        AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
        AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
@@ -421,6 +448,7 @@ amdpm_writeb(device_t dev, u_char slave, char cmd, char byte)
        error = amdpm_wait(sc);
 
        AMDPM_DEBUG(kprintf("amdpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -432,9 +460,12 @@ amdpm_readb(device_t dev, u_char slave, char cmd, char *byte)
        int error;
        u_short l;
 
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
        AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
        AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
@@ -445,6 +476,7 @@ amdpm_readb(device_t dev, u_char slave, char cmd, char *byte)
                *byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
 
        AMDPM_DEBUG(kprintf("amdpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -456,9 +488,12 @@ amdpm_writew(device_t dev, u_char slave, char cmd, short word)
        int error;
        u_short l;
 
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
        AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
        AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, word);
@@ -469,6 +504,7 @@ amdpm_writew(device_t dev, u_char slave, char cmd, short word)
        error = amdpm_wait(sc);
 
        AMDPM_DEBUG(kprintf("amdpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -480,9 +516,12 @@ amdpm_readw(device_t dev, u_char slave, char cmd, short *word)
        int error;
        u_short l;
 
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
        AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
        AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
@@ -493,6 +532,7 @@ amdpm_readw(device_t dev, u_char slave, char cmd, short *word)
                *word = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
 
        AMDPM_DEBUG(kprintf("amdpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -501,84 +541,89 @@ static int
 amdpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
 {
        struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
-       u_char remain, len, i;
-       int error = SMB_ENOERR;
+       u_char i;
+       int error;
        u_short l;
 
+       if (count < 1 || count > 32)
+               return (SMB_EINVAL);
+
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if(!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
-       remain = count;
-       while (remain) {
-               len = min(remain, 32);
-
-               AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
-
-               /*
-                * Do we have to reset the internal 32-byte buffer?
-                * Can't see how to do this from the data sheet.
-                */
-
-               AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, len);
-
-               /* Fill the 32-byte internal buffer */
-               for (i=0; i<len; i++) {
-                       AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[count-remain+i]);
-                       DELAY(2);
-               }
-               AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
-               l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
-               AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
+       AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
 
-               if ((error = amdpm_wait(sc)) != SMB_ENOERR)
-                       goto error;
+       /*
+        * Do we have to reset the internal 32-byte buffer?
+        * Can't see how to do this from the data sheet.
+        */
+       AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, count);
 
-               remain -= len;
+       /* Fill the 32-byte internal buffer */
+       for (i = 0; i < count; i++) {
+               AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[i]);
+               DELAY(2);
        }
+       AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
+       l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
+       AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
+           (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
+
+       error = amdpm_wait(sc);
 
-error:
        AMDPM_DEBUG(kprintf("amdpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
 
 static int
-amdpm_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
+amdpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
 {
        struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
-       u_char remain, len, i;
-       int error = SMB_ENOERR;
+       u_char data, len, i;
+       int error;
        u_short l;
 
+       if (*count < 1 || *count > 32)
+               return (SMB_EINVAL);
+
+       AMDPM_LOCK(sc);
        amdpm_clear(sc);
-       if (!amdpm_idle(sc))
+       if (!amdpm_idle(sc)) {
+               AMDPM_UNLOCK(sc);
                return (SMB_EBUSY);
+       }
 
-       remain = count;
-       while (remain) {
-               AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
-
-               AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
+       AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
 
-               l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
-               AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
+       AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
 
-               if ((error = amdpm_wait(sc)) != SMB_ENOERR)
-                       goto error;
+       l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
+       AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
+           (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
 
-               len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
+       if ((error = amdpm_wait(sc)) != SMB_ENOERR)
+               goto error;
 
-               /* Read the 32-byte internal buffer */
-               for (i=0; i<len; i++) {
-                       buf[count-remain+i] = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO);
-                       DELAY(2);
-               }
+       len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
 
-               remain -= len;
+       /* Read the 32-byte internal buffer */
+       for (i = 0; i < len; i++) {
+               data = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO);
+               if (i < *count)
+                       buf[i] = data;
+               DELAY(2);
        }
+       *count = len;
+
 error:
-       AMDPM_DEBUG(kprintf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
+       AMDPM_DEBUG(kprintf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
+       AMDPM_UNLOCK(sc);
 
        return (error);
 }
@@ -613,6 +658,7 @@ static driver_t amdpm_driver = {
 };
 
 DRIVER_MODULE(amdpm, pci, amdpm_driver, amdpm_devclass, 0, 0);
+DRIVER_MODULE(smbus, amdpm, smbus_driver, smbus_devclass, 0, 0);
 
 MODULE_DEPEND(amdpm, pci, 1, 1, 1);
 MODULE_DEPEND(amdpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
diff --git a/sys/dev/powermng/amdsmb/Makefile b/sys/dev/powermng/amdsmb/Makefile
new file mode 100644 (file)
index 0000000..7c2dd11
--- /dev/null
@@ -0,0 +1,7 @@
+# $FreeBSD: src/sys/modules/i2c/controllers/amdsmb/Makefile,v 1.1 2005/12/21 15:49:51 ru Exp $
+
+KMOD=  amdsmb
+SRCS=  amdsmb.c
+SRCS+= device_if.h smbus_if.h pci_if.h bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/powermng/amdsmb/amdsmb.c b/sys/dev/powermng/amdsmb/amdsmb.c
new file mode 100644 (file)
index 0000000..22f47f9
--- /dev/null
@@ -0,0 +1,582 @@
+/*-
+ * Copyright (c) 2005 Ruslan Ermilov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/pci/amdsmb.c,v 1.6 2008/06/06 18:29:56 jhb Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/globaldata.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+
+#include <sys/rman.h>
+
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+
+#include <bus/smbus/smbconf.h>
+#include "smbus_if.h"
+
+#define        AMDSMB_DEBUG(x) if (amdsmb_debug) (x)
+
+#ifdef DEBUG
+static int amdsmb_debug = 1;
+#else
+static int amdsmb_debug = 0;
+#endif
+
+#define        AMDSMB_VENDORID_AMD             0x1022
+#define        AMDSMB_DEVICEID_AMD8111_SMB2    0x746a
+
+/*
+ * ACPI 3.0, Chapter 12, Embedded Controller Interface.
+ */
+#define        EC_DATA         0x00    /* data register */
+#define        EC_SC           0x04    /* status of controller */
+#define        EC_CMD          0x04    /* command register */
+
+#define        EC_SC_IBF       0x02    /* data ready for embedded controller */
+#define        EC_SC_OBF       0x01    /* data ready for host */
+#define        EC_CMD_WR       0x81    /* write EC */
+#define        EC_CMD_RD       0x80    /* read EC */
+
+/*
+ * ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
+ */
+#define        SMB_PRTCL       0x00    /* protocol */
+#define        SMB_STS         0x01    /* status */
+#define        SMB_ADDR        0x02    /* address */
+#define        SMB_CMD         0x03    /* command */
+#define        SMB_DATA        0x04    /* 32 data registers */
+#define        SMB_BCNT        0x24    /* number of data bytes */
+#define        SMB_ALRM_A      0x25    /* alarm address */
+#define        SMB_ALRM_D      0x26    /* 2 bytes alarm data */
+
+#define        SMB_STS_DONE    0x80
+#define        SMB_STS_ALRM    0x40
+#define        SMB_STS_RES     0x20
+#define        SMB_STS_STATUS  0x1f
+#define        SMB_STS_OK      0x00    /* OK */
+#define        SMB_STS_UF      0x07    /* Unknown Failure */
+#define        SMB_STS_DANA    0x10    /* Device Address Not Acknowledged */
+#define        SMB_STS_DED     0x11    /* Device Error Detected */
+#define        SMB_STS_DCAD    0x12    /* Device Command Access Denied */
+#define        SMB_STS_UE      0x13    /* Unknown Error */
+#define        SMB_STS_DAD     0x17    /* Device Access Denied */
+#define        SMB_STS_T       0x18    /* Timeout */
+#define        SMB_STS_HUP     0x19    /* Host Unsupported Protocol */
+#define        SMB_STS_B       0x1a    /* Busy */
+#define        SMB_STS_PEC     0x1f    /* PEC (CRC-8) Error */
+
+#define        SMB_PRTCL_WRITE                 0x00
+#define        SMB_PRTCL_READ                  0x01
+#define        SMB_PRTCL_QUICK                 0x02
+#define        SMB_PRTCL_BYTE                  0x04
+#define        SMB_PRTCL_BYTE_DATA             0x06
+#define        SMB_PRTCL_WORD_DATA             0x08
+#define        SMB_PRTCL_BLOCK_DATA            0x0a
+#define        SMB_PRTCL_PROC_CALL             0x0c
+#define        SMB_PRTCL_BLOCK_PROC_CALL       0x0d
+#define        SMB_PRTCL_PEC                   0x80
+
+struct amdsmb_softc {
+       int rid;
+       struct resource *res;
+       device_t smbus;
+       struct lock lock;
+};
+
+#define        AMDSMB_LOCK(amdsmb)             lockmgr(&(amdsmb)->lock, LK_EXCLUSIVE)
+#define        AMDSMB_UNLOCK(amdsmb)           lockmgr(&(amdsmb)->lock, LK_RELEASE)
+#define        AMDSMB_LOCK_ASSERT(amdsmb)      KKASSERT(lockstatus(&(amdsmb)->lock, curthread) != 0)
+
+#define        AMDSMB_ECINB(amdsmb, register)                                  \
+       (bus_read_1(amdsmb->res, register))
+#define        AMDSMB_ECOUTB(amdsmb, register, value) \
+       (bus_write_1(amdsmb->res, register, value))
+
+static int     amdsmb_detach(device_t dev);
+
+static int
+amdsmb_probe(device_t dev)
+{
+       u_int16_t vid;
+       u_int16_t did;
+
+       vid = pci_get_vendor(dev);
+       did = pci_get_device(dev);
+
+       if (vid == AMDSMB_VENDORID_AMD) {
+               switch(did) {
+               case AMDSMB_DEVICEID_AMD8111_SMB2:
+                       device_set_desc(dev, "AMD-8111 SMBus 2.0 Controller");
+                       return (BUS_PROBE_DEFAULT);
+               }
+       }
+
+       return (ENXIO);
+}
+
+static int
+amdsmb_attach(device_t dev)
+{
+       struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
+
+       /* Allocate I/O space */
+       amdsmb_sc->rid = PCIR_BAR(0);
+
+       amdsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
+               &amdsmb_sc->rid, RF_ACTIVE);
+
+       if (amdsmb_sc->res == NULL) {
+               device_printf(dev, "could not map i/o space\n");
+               return (ENXIO);
+       }
+
+       lockinit(&amdsmb_sc->lock, "amdsmb", 0, LK_CANRECURSE);
+
+       /* Allocate a new smbus device */
+       amdsmb_sc->smbus = device_add_child(dev, "smbus", -1);
+       if (!amdsmb_sc->smbus) {
+               amdsmb_detach(dev);
+               return (EINVAL);
+       }
+
+       bus_generic_attach(dev);
+
+       return (0);
+}
+
+static int
+amdsmb_detach(device_t dev)
+{
+       struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
+
+       if (amdsmb_sc->smbus) {
+               device_delete_child(dev, amdsmb_sc->smbus);
+               amdsmb_sc->smbus = NULL;
+       }
+
+       lockuninit(&amdsmb_sc->lock);
+       if (amdsmb_sc->res)
+               bus_release_resource(dev, SYS_RES_IOPORT, amdsmb_sc->rid,
+                   amdsmb_sc->res);
+
+       return (0);
+}
+
+static int
+amdsmb_callback(device_t dev, int index, void *data)
+{
+       int error = 0;
+
+       switch (index) {
+       case SMB_REQUEST_BUS:
+       case SMB_RELEASE_BUS:
+               break;
+       default:
+               error = EINVAL;
+       }
+
+       return (error);
+}
+
+static int
+amdsmb_ec_wait_write(struct amdsmb_softc *sc)
+{
+       int timeout = 500;
+
+       while (timeout-- && AMDSMB_ECINB(sc, EC_SC) & EC_SC_IBF)
+               DELAY(1);
+       if (timeout == 0) {
+               device_printf(sc->smbus, "timeout waiting for IBF to clear\n");
+               return (1);
+       }
+       return (0);
+}
+
+static int
+amdsmb_ec_wait_read(struct amdsmb_softc *sc)
+{
+       int timeout = 500;
+
+       while (timeout-- && ~AMDSMB_ECINB(sc, EC_SC) & EC_SC_OBF)
+               DELAY(1);
+       if (timeout == 0) {
+               device_printf(sc->smbus, "timeout waiting for OBF to set\n");
+               return (1);
+       }
+       return (0);
+}
+
+static int
+amdsmb_ec_read(struct amdsmb_softc *sc, u_char addr, u_char *data)
+{
+
+       AMDSMB_LOCK_ASSERT(sc);
+       if (amdsmb_ec_wait_write(sc))
+               return (1);
+       AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_RD);
+
+       if (amdsmb_ec_wait_write(sc))
+               return (1);
+       AMDSMB_ECOUTB(sc, EC_DATA, addr);
+
+       if (amdsmb_ec_wait_read(sc))
+               return (1);
+       *data = AMDSMB_ECINB(sc, EC_DATA);
+
+       return (0);
+}
+
+static int
+amdsmb_ec_write(struct amdsmb_softc *sc, u_char addr, u_char data)
+{
+
+       AMDSMB_LOCK_ASSERT(sc);
+       if (amdsmb_ec_wait_write(sc))
+               return (1);
+       AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_WR);
+
+       if (amdsmb_ec_wait_write(sc))
+               return (1);
+       AMDSMB_ECOUTB(sc, EC_DATA, addr);
+
+       if (amdsmb_ec_wait_write(sc))
+               return (1);
+       AMDSMB_ECOUTB(sc, EC_DATA, data);
+
+       return (0);
+}
+
+static int
+amdsmb_wait(struct amdsmb_softc *sc)
+{
+       u_char sts, temp;
+       int error, count;
+
+       AMDSMB_LOCK_ASSERT(sc);
+       amdsmb_ec_read(sc, SMB_PRTCL, &temp);
+       if (temp != 0)
+       {
+               count = 10000;
+               do {
+                       DELAY(500);
+                       amdsmb_ec_read(sc, SMB_PRTCL, &temp);
+               } while (temp != 0 && count--);
+               if (count == 0)
+                       return (SMB_ETIMEOUT);
+       }
+
+       amdsmb_ec_read(sc, SMB_STS, &sts);
+       sts &= SMB_STS_STATUS;
+       AMDSMB_DEBUG(kprintf("amdsmb: STS=0x%x\n", sts));
+
+       switch (sts) {
+       case SMB_STS_OK:
+               error = SMB_ENOERR;
+               break;
+       case SMB_STS_DANA:
+               error = SMB_ENOACK;
+               break;
+       case SMB_STS_B:
+               error = SMB_EBUSY;
+               break;
+       case SMB_STS_T:
+               error = SMB_ETIMEOUT;
+               break;
+       case SMB_STS_DCAD:
+       case SMB_STS_DAD:
+       case SMB_STS_HUP:
+               error = SMB_ENOTSUPP;
+               break;
+       default:
+               error = SMB_EBUSERR;
+               break;
+       }
+
+       return (error);
+}
+
+static int
+amdsmb_quick(device_t dev, u_char slave, int how)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       u_char protocol;
+       int error;
+
+       protocol = SMB_PRTCL_QUICK;
+
+       switch (how) {
+       case SMB_QWRITE:
+               protocol |= SMB_PRTCL_WRITE;
+               AMDSMB_DEBUG(kprintf("amdsmb: QWRITE to 0x%x", slave));
+               break;
+       case SMB_QREAD:
+               protocol |= SMB_PRTCL_READ;
+               AMDSMB_DEBUG(kprintf("amdsmb: QREAD to 0x%x", slave));
+               break;
+       default:
+               panic("%s: unknown QUICK command (%x)!", __func__, how);
+       }
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, protocol);
+
+       error = amdsmb_wait(sc);
+
+       AMDSMB_DEBUG(kprintf(", error=0x%x\n", error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_sendb(device_t dev, u_char slave, char byte)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       int error;
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_CMD, byte);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
+
+       error = amdsmb_wait(sc);
+
+       AMDSMB_DEBUG(kprintf("amdsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n",
+          slave, byte, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_recvb(device_t dev, u_char slave, char *byte)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       int error;
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
+
+       if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
+               amdsmb_ec_read(sc, SMB_DATA, byte);
+
+       AMDSMB_DEBUG(kprintf("amdsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n",
+           slave, *byte, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       int error;
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_CMD, cmd);
+       amdsmb_ec_write(sc, SMB_DATA, byte);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
+
+       error = amdsmb_wait(sc);
+
+       AMDSMB_DEBUG(kprintf("amdsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, "
+           "error=0x%x\n", slave, cmd, byte, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       int error;
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_CMD, cmd);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
+
+       if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
+               amdsmb_ec_read(sc, SMB_DATA, byte);
+
+       AMDSMB_DEBUG(kprintf("amdsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, "
+           "error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_writew(device_t dev, u_char slave, char cmd, short word)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       int error;
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_CMD, cmd);
+       amdsmb_ec_write(sc, SMB_DATA, word);
+       amdsmb_ec_write(sc, SMB_DATA + 1, word >> 8);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
+
+       error = amdsmb_wait(sc);
+
+       AMDSMB_DEBUG(kprintf("amdsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, "
+           "error=0x%x\n", slave, cmd, word, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_readw(device_t dev, u_char slave, char cmd, short *word)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       u_char temp[2];
+       int error;
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_CMD, cmd);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
+
+       if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
+               amdsmb_ec_read(sc, SMB_DATA + 0, &temp[0]);
+               amdsmb_ec_read(sc, SMB_DATA + 1, &temp[1]);
+               *word = temp[0] | (temp[1] << 8);
+       }
+
+       AMDSMB_DEBUG(kprintf("amdsmb: READW from 0x%x, cmd=0x%x, word=0x%x, "
+           "error=0x%x\n", slave, cmd, (unsigned short)*word, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       u_char i;
+       int error;
+
+       if (count < 1 || count > 32)
+               return (SMB_EINVAL);
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_CMD, cmd);
+       amdsmb_ec_write(sc, SMB_BCNT, count);
+       for (i = 0; i < count; i++)
+               amdsmb_ec_write(sc, SMB_DATA + i, buf[i]);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
+
+       error = amdsmb_wait(sc);
+
+       AMDSMB_DEBUG(kprintf("amdsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, "
+           "error=0x%x", slave, count, cmd, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static int
+amdsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
+{
+       struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
+       u_char data, len, i;
+       int error;
+
+       if (*count < 1 || *count > 32)
+               return (SMB_EINVAL);
+
+       AMDSMB_LOCK(sc);
+       amdsmb_ec_write(sc, SMB_CMD, cmd);
+       amdsmb_ec_write(sc, SMB_ADDR, slave);
+       amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
+
+       if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
+               amdsmb_ec_read(sc, SMB_BCNT, &len);
+               for (i = 0; i < len; i++) {
+                       amdsmb_ec_read(sc, SMB_DATA + i, &data);
+                       if (i < *count)
+                               buf[i] = data;
+               }
+               *count = len;
+       }
+
+       AMDSMB_DEBUG(kprintf("amdsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, "
+           "error=0x%x", slave, *count, cmd, error));
+       AMDSMB_UNLOCK(sc);
+
+       return (error);
+}
+
+static device_method_t amdsmb_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         amdsmb_probe),
+       DEVMETHOD(device_attach,        amdsmb_attach),
+       DEVMETHOD(device_detach,        amdsmb_detach),
+
+       /* SMBus interface */
+       DEVMETHOD(smbus_callback,       amdsmb_callback),
+       DEVMETHOD(smbus_quick,          amdsmb_quick),
+       DEVMETHOD(smbus_sendb,          amdsmb_sendb),
+       DEVMETHOD(smbus_recvb,          amdsmb_recvb),
+       DEVMETHOD(smbus_writeb,         amdsmb_writeb),
+       DEVMETHOD(smbus_readb,          amdsmb_readb),
+       DEVMETHOD(smbus_writew,         amdsmb_writew),
+       DEVMETHOD(smbus_readw,          amdsmb_readw),
+       DEVMETHOD(smbus_bwrite,         amdsmb_bwrite),
+       DEVMETHOD(smbus_bread,          amdsmb_bread),
+
+       { 0, 0 }
+};
+
+static devclass_t amdsmb_devclass;
+
+static driver_t amdsmb_driver = {
+       "amdsmb",
+       amdsmb_methods,
+       sizeof(struct amdsmb_softc),
+};
+
+DRIVER_MODULE(amdsmb, pci, amdsmb_driver, amdsmb_devclass, 0, 0);
+DRIVER_MODULE(smbus, amdsmb, smbus_driver, smbus_devclass, 0, 0);
+
+MODULE_DEPEND(amdsmb, pci, 1, 1, 1);
+MODULE_DEPEND(amdsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
+MODULE_VERSION(amdsmb, 1);