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 */