hi,

here's a diff that adds support for the SMBus controller found
on AMD CS5536:

glxpcib0 at pci0 dev 15 function 0 "AMD CS5536 ISA" rev 0x03: rev 3, 32-bit 
3579545Hz timer, watchdog, gpio, i2c
gpio0 at glxpcib0: 32 pins
iic0 at glxpcib0
maxtmp0 at iic0 addr 0x4c: lm86

# sysctl hw.sensors
hw.sensors.maxtmp0.temp0=41.00 degC (Internal)
hw.sensors.maxtmp0.temp1=47.50 degC (External)

huge thanks to paul de weerd for getting me an alix board!

tests on loongson and ok's are much appreciated.

Index: arch/i386/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/i386/conf/GENERIC,v
retrieving revision 1.730
diff -u -p -r1.730 GENERIC
--- arch/i386/conf/GENERIC      28 Jan 2012 00:39:15 -0000      1.730
+++ arch/i386/conf/GENERIC      25 Feb 2012 14:28:14 -0000
@@ -91,6 +91,7 @@ gscpcib* at pci?              # NS Geode SC1100 PCI-
 gpio*  at gscpcib?
 glxpcib* at pci?               # AMD CS5536 PCI-ISA bridge
 gpio*  at glxpcib?
+iic*   at glxpcib?
 kate*  at pci?                 # AMD K8 temperature sensor
 km*    at pci?                 # AMD K10 temperature sensor
 amas*  at pci? disable         # AMD memory configuration
Index: arch/loongson/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/loongson/conf/GENERIC,v
retrieving revision 1.36
diff -u -p -r1.36 GENERIC
--- arch/loongson/conf/GENERIC  7 Jul 2011 23:41:09 -0000       1.36
+++ arch/loongson/conf/GENERIC  25 Feb 2012 14:28:14 -0000
@@ -39,6 +39,7 @@ pci*          at bonito?
 # Lemote Lynloong, Lemote Fuloong 2F and Lemote Yeeloong devices
 glxpcib*       at pci?
 gpio*          at glxpcib?
+iic*           at glxpcib?
 isa0           at glxpcib?
 mcclock0       at isa? port 0x70
 pckbc0         at isa?                         # Yeeloong only
Index: dev/pci/files.pci
===================================================================
RCS file: /cvs/src/sys/dev/pci/files.pci,v
retrieving revision 1.281
diff -u -p -r1.281 files.pci
--- dev/pci/files.pci   15 Nov 2011 22:27:53 -0000      1.281
+++ dev/pci/files.pci   25 Feb 2012 14:28:15 -0000
@@ -807,6 +807,6 @@ attach      itherm at pci
 file   dev/pci/itherm.c                itherm
 
 # AMD Geode CS5536 PCI-ISA bridge
-device glxpcib: isabus, gpiobus
+device glxpcib: isabus, gpiobus, i2cbus
 attach glxpcib at pci
 file   dev/pci/glxpcib.c               glxpcib
Index: dev/pci/glxpcib.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/glxpcib.c,v
retrieving revision 1.3
diff -u -p -r1.3 glxpcib.c
--- dev/pci/glxpcib.c   23 Oct 2010 17:42:57 -0000      1.3
+++ dev/pci/glxpcib.c   25 Feb 2012 14:28:15 -0000
@@ -28,6 +28,7 @@
 #include <sys/device.h>
 #include <sys/gpio.h>
 #include <sys/timetc.h>
+#include <sys/rwlock.h>
 
 #include <machine/bus.h>
 #ifdef __i386__
@@ -35,6 +36,8 @@
 #endif
 
 #include <dev/gpio/gpiovar.h>
+#include <dev/i2c/i2cvar.h>
+
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
 #include <dev/pci/pcidevs.h>
@@ -133,6 +136,36 @@
 #define AMD5536_GPIO_IN_INVRT_EN 0x24  /* invert input */
 #define        AMD5536_GPIO_READ_BACK  0x30    /* read back value */
 
+/* SMB */
+#define MSR_LBAR_SMB           DIVIL_LBAR_SMB
+#define MSR_SMB_SIZE           0x08
+#define MSR_SMB_ADDR_MASK      0xfff8
+#define AMD5536_SMB_SDA                0x00 /* serial data */
+#define AMD5536_SMB_STS                0x01 /* status */
+#define AMD5536_SMB_STS_SLVSTOP        0x80 /* slave stop */
+#define AMD5536_SMB_STS_SDAST  0x40 /* smb data status */
+#define AMD5536_SMB_STS_BER    0x20 /* bus error */
+#define AMD5536_SMB_STS_NEGACK 0x10 /* negative acknowledge */
+#define AMD5536_SMB_STS_STASTR 0x08 /* stall after start */
+#define AMD5536_SMB_STS_MASTER 0x02 /* master */
+#define AMD5536_SMB_STS_XMIT   0x01 /* transmit or receive */
+#define AMD5536_SMB_CST                0x02 /* control status */
+#define AMD5536_SMB_CST_MATCH  0x04 /* address match */
+#define AMD5536_SMB_CST_BB     0x02 /* bus busy */
+#define AMD5536_SMB_CST_BUSY   0x01 /* busy */
+#define AMD5536_SMB_CTL1       0x03 /* control 1 */
+#define AMD5536_SMB_CTL1_STASTRE 0x80 /* stall after start enable */
+#define AMD5536_SMB_CTL1_ACK   0x10 /* receive acknowledge */
+#define AMD5536_SMB_CTL1_INTEN 0x04 /* interrupt enable  */
+#define AMD5536_SMB_CTL1_STOP  0x02 /* stop */
+#define AMD5536_SMB_CTL1_START 0x01 /* start */
+#define AMD5536_SMB_ADDR       0x04 /* serial address */
+#define AMD5536_SMB_ADDR_SAEN  0x80 /* slave enable */
+#define AMD5536_SMB_CTL2       0x05 /* control 2 */
+#define AMD5536_SMB_CTL2_EN    0x01 /* enable clock */
+#define AMD5536_SMB_CTL2_FREQ  0x78 /* 100 kHz */
+#define AMD5536_SMB_CTL3       0x06 /* control 3 */
+
 /*
  * MSR registers we want to preserve accross suspend/resume
  */
@@ -151,12 +184,21 @@ struct glxpcib_softc {
 
        uint64_t                sc_msrsave[nitems(glxpcib_msrlist)];
 
-#if !defined(SMALL_KERNEL) && NGPIO > 0
+#ifndef SMALL_KERNEL
+#if NGPIO > 0
        /* GPIO interface */
        bus_space_tag_t         sc_gpio_iot;
        bus_space_handle_t      sc_gpio_ioh;
        struct gpio_chipset_tag sc_gpio_gc;
        gpio_pin_t              sc_gpio_pins[AMD5536_GPIO_NPINS];
+#endif
+       /* I2C interface */
+       bus_space_tag_t         sc_smb_iot;
+       bus_space_handle_t      sc_smb_ioh;
+       struct i2c_controller   sc_smb_ic;
+       struct rwlock           sc_smb_lck;
+
+       /* Watchdog */
        int                     sc_wdog;
        int                     sc_wdog_period;
 #endif
@@ -180,11 +222,23 @@ void      pcibattach(struct device *parent, s
 
 u_int  glxpcib_get_timecount(struct timecounter *tc);
 
-#if !defined(SMALL_KERNEL) && NGPIO > 0
+#ifndef SMALL_KERNEL
+int     glxpcib_wdogctl_cb(void *, int);
+#if NGPIO > 0
 void   glxpcib_gpio_pin_ctl(void *, int, int);
 int    glxpcib_gpio_pin_read(void *, int);
 void   glxpcib_gpio_pin_write(void *, int, int);
-int     glxpcib_wdogctl_cb(void *, int);
+#endif
+int    glxpcib_smb_acquire_bus(void *, int);
+void   glxpcib_smb_release_bus(void *, int);
+int    glxpcib_smb_send_start(void *, int);
+int    glxpcib_smb_send_stop(void *, int);
+void   glxpcib_smb_send_ack(void *, int);
+int    glxpcib_smb_initiate_xfer(void *, i2c_addr_t, int);
+int    glxpcib_smb_read_byte(void *, uint8_t *, int);
+int    glxpcib_smb_write_byte(void *, uint8_t, int);
+void   glxpcib_smb_reset(struct glxpcib_softc *);
+int    glxpcib_smb_wait(struct glxpcib_softc *, int, int);
 #endif
 
 const struct pci_matchid glxpcib_devices[] = {
@@ -208,12 +262,18 @@ glxpcib_attach(struct device *parent, st
 {
        struct glxpcib_softc *sc = (struct glxpcib_softc *)self;
        struct timecounter *tc = &sc->sc_timecounter;
-#if !defined(SMALL_KERNEL) && NGPIO > 0
+#ifndef SMALL_KERNEL
        struct pci_attach_args *pa = (struct pci_attach_args *)aux;
-       u_int64_t wa, ga;
+       u_int64_t wa;
+#if NGPIO > 0
+       u_int64_t ga;
        struct gpiobus_attach_args gba;
        int i, gpio = 0;
 #endif
+       u_int64_t sa;
+       struct i2cbus_attach_args iba;
+       int i2c = 0;
+#endif
        tc->tc_get_timecount = glxpcib_get_timecount;
        tc->tc_counter_mask = 0xffffffff;
        tc->tc_frequency = 3579545;
@@ -230,7 +290,7 @@ glxpcib_attach(struct device *parent, st
            (int)rdmsr(AMD5536_REV) & AMD5536_REV_MASK,
            tc->tc_frequency);
 
-#if !defined(SMALL_KERNEL) && NGPIO > 0
+#ifndef SMALL_KERNEL
        /* Attach the watchdog timer */
        sc->sc_iot = pa->pa_iot;
        wa = rdmsr(MSR_LBAR_MFGPT);
@@ -246,6 +306,7 @@ glxpcib_attach(struct device *parent, st
                printf(", watchdog");
        }
 
+#if NGPIO > 0
        /* map GPIO I/O space */
        sc->sc_gpio_iot = pa->pa_iot;
        ga = rdmsr(MSR_LBAR_GPIO);
@@ -280,13 +341,61 @@ glxpcib_attach(struct device *parent, st
                gpio = 1;
 
        }
-#endif
+#endif /* NGPIO */
+
+       /* Map SMB I/O space */
+       sc->sc_smb_iot = pa->pa_iot;
+       sa = rdmsr(MSR_LBAR_SMB);
+       if (sa & MSR_LBAR_ENABLE &&
+           !bus_space_map(sc->sc_smb_iot, sa & MSR_SMB_ADDR_MASK,
+           MSR_SMB_SIZE, 0, &sc->sc_smb_ioh)) {
+               printf(", i2c");
+
+               /* Enable controller */
+               bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+                   AMD5536_SMB_CTL2, AMD5536_SMB_CTL2_EN |
+                   AMD5536_SMB_CTL2_FREQ);
+
+               /* Disable interrupts */
+               bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+                   AMD5536_SMB_CTL1, 0);
+
+               /* Disable slave address */
+               bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+                   AMD5536_SMB_ADDR, 0);
+
+               /* Stall the bus after start */
+               bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+                   AMD5536_SMB_CTL1, AMD5536_SMB_CTL1_STASTRE);
+
+               /* Attach I2C framework */
+               sc->sc_smb_ic.ic_cookie = sc;
+               sc->sc_smb_ic.ic_acquire_bus = glxpcib_smb_acquire_bus;
+               sc->sc_smb_ic.ic_release_bus = glxpcib_smb_release_bus;
+               sc->sc_smb_ic.ic_send_start = glxpcib_smb_send_start;
+               sc->sc_smb_ic.ic_send_stop = glxpcib_smb_send_stop;
+               sc->sc_smb_ic.ic_initiate_xfer = glxpcib_smb_initiate_xfer;
+               sc->sc_smb_ic.ic_read_byte = glxpcib_smb_read_byte;
+               sc->sc_smb_ic.ic_write_byte = glxpcib_smb_write_byte;
+
+               rw_init(&sc->sc_smb_lck, "iiclk");
+
+               bzero(&iba, sizeof(iba));
+               iba.iba_name = "iic";
+               iba.iba_tag = &sc->sc_smb_ic;
+               i2c = 1;
+       }
+#endif /* SMALL_KERNEL */
        pcibattach(parent, self, aux);
 
-#if !defined(SMALL_KERNEL) && NGPIO > 0
+#ifndef SMALL_KERNEL
+#if NGPIO > 0
        if (gpio)
                config_found(&sc->sc_dev, &gba, gpiobus_print);
 #endif
+       if (i2c)
+               config_found(&sc->sc_dev, &iba, iicbus_print);
+#endif
 }
 
 int
@@ -336,7 +445,7 @@ glxpcib_get_timecount(struct timecounter
         return rdmsr(AMD5536_TMC);
 }
 
-#if !defined(SMALL_KERNEL) && NGPIO > 0
+#ifndef SMALL_KERNEL
 int
 glxpcib_wdogctl_cb(void *v, int period)
 {
@@ -360,6 +469,7 @@ glxpcib_wdogctl_cb(void *v, int period)
        return period;
 }
 
+#if NGPIO > 0
 int
 glxpcib_gpio_pin_read(void *arg, int pin)
 {
@@ -462,6 +572,188 @@ glxpcib_gpio_pin_ctl(void *arg, int pin,
        for (n = 0; n < nreg; n++)
                bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg[n],
                    val[n]);
-} 
+}
+#endif /* GPIO */
 
-#endif
+int
+glxpcib_smb_acquire_bus(void *arg, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+
+       if (cold || flags & I2C_F_POLL)
+               return (0);
+
+       return (rw_enter(&sc->sc_smb_lck, RW_WRITE | RW_INTR));
+}
+
+void
+glxpcib_smb_release_bus(void *arg, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+
+       if (cold || flags & I2C_F_POLL)
+               return;
+
+       rw_exit(&sc->sc_smb_lck);
+}
+
+int
+glxpcib_smb_send_start(void *arg, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+       u_int8_t ctl;
+
+       ctl = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+           AMD5536_SMB_CTL1);
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL1,
+           ctl | AMD5536_SMB_CTL1_START);
+
+       return (0);
+}
+
+int
+glxpcib_smb_send_stop(void *arg, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+       u_int8_t ctl;
+
+       ctl = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+           AMD5536_SMB_CTL1);
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL1,
+           ctl | AMD5536_SMB_CTL1_STOP);
+
+       return (0);
+}
+
+void
+glxpcib_smb_send_ack(void *arg, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+       u_int8_t ctl;
+
+       ctl = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+           AMD5536_SMB_CTL1);
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL1,
+           ctl | AMD5536_SMB_CTL1_ACK);
+}
+
+int
+glxpcib_smb_initiate_xfer(void *arg, i2c_addr_t addr, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+       int error, dir;
+
+       /* Issue start condition */
+       glxpcib_smb_send_start(sc, flags);
+
+       /* Wait for bus mastership */
+       if ((error = glxpcib_smb_wait(sc, AMD5536_SMB_STS_MASTER |
+           AMD5536_SMB_STS_SDAST, flags)) != 0)
+               return (error);
+
+       /* Send address byte */
+       dir = (flags & I2C_F_READ ? 1 : 0);
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_SDA,
+           (addr << 1) | dir);
+
+       return (0);
+}
+
+int
+glxpcib_smb_read_byte(void *arg, uint8_t *bytep, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+       int error;
+
+       /* Wait for the bus to be ready */
+       if ((error = glxpcib_smb_wait(sc, AMD5536_SMB_STS_SDAST, flags)))
+               return (error);
+
+       /* Acknowledge the last byte */
+       if (flags & I2C_F_LAST)
+               glxpcib_smb_send_ack(sc, 0);
+
+       /* Read data byte */
+       *bytep = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+           AMD5536_SMB_SDA);
+
+       return (0);
+}
+
+int
+glxpcib_smb_write_byte(void *arg, uint8_t byte, int flags)
+{
+       struct glxpcib_softc *sc = arg;
+       int error;
+
+       /* Wait for the bus to be ready */
+       if ((error = glxpcib_smb_wait(sc, AMD5536_SMB_STS_SDAST, flags)))
+               return (error);
+
+       /* Send stop after the last byte */
+       if (flags & I2C_F_STOP)
+               glxpcib_smb_send_stop(sc, 0);
+
+       /* Write data byte */
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_SDA,
+           byte);
+
+       return (0);
+}
+
+void
+glxpcib_smb_reset(struct glxpcib_softc *sc)
+{
+       u_int8_t st;
+
+       /* Clear MASTER, NEGACK and BER */
+       st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_STS);
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_STS, st |
+           AMD5536_SMB_STS_MASTER | AMD5536_SMB_STS_NEGACK |
+           AMD5536_SMB_STS_BER);
+
+       /* Disable and re-enable controller */
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL2, 0);
+       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh, AMD5536_SMB_CTL2,
+           AMD5536_SMB_CTL2_EN | AMD5536_SMB_CTL2_FREQ);
+
+       /* Send stop */
+       glxpcib_smb_send_stop(sc, 0);
+}
+
+int
+glxpcib_smb_wait(struct glxpcib_softc *sc, int bits, int flags)
+{
+       u_int8_t st;
+       int i;
+
+       for (i = 0; i < 100; i++) {
+               st = bus_space_read_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+                   AMD5536_SMB_STS);
+               if (st & AMD5536_SMB_STS_BER) {
+                       printf("%s: bus error, bits=%#x st=%#x\n",
+                           sc->sc_dev.dv_xname, bits, st);
+                       glxpcib_smb_reset(sc);
+                       return (EIO);
+               }
+               if ((bits & AMD5536_SMB_STS_MASTER) == 0 &&
+                   (st & AMD5536_SMB_STS_NEGACK)) {
+                       glxpcib_smb_reset(sc);
+                       return (EIO);
+               }
+               if (st & AMD5536_SMB_STS_STASTR)
+                       bus_space_write_1(sc->sc_smb_iot, sc->sc_smb_ioh,
+                           AMD5536_SMB_STS, AMD5536_SMB_STS_STASTR);
+               if ((st & bits) == bits)
+                       break;
+               delay(2);
+       }
+       if ((st & bits) != bits) {
+               printf("%s: timeout, bits=%#x st=%#x\n",
+                   sc->sc_dev.dv_xname, bits, st);
+               glxpcib_smb_reset(sc);
+               return (ETIMEDOUT);
+       }
+       return (0);
+}
+#endif /* SMALL_KERNEL */

Reply via email to