On Sun, Feb 26, 2012 at 17:00 +0100, Mike Belopuhov wrote: > On Sat, Feb 25, 2012 at 15:46 +0100, Mike Belopuhov wrote: > > 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. > > > > this has been successfully tested on loongson, albeit per miod's > suggestion will remain disabled. this new revision also silences > a timeout warning seen on soekrises. > > ok? >
any objections to this diff going in? it was tested on alix and soekris by a few and on loongson by miod. > 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 26 Feb 2012 15:56:20 -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: 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 26 Feb 2012 15:56:20 -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 26 Feb 2012 15:56:20 -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,186 @@ 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) { > + glxpcib_smb_reset(sc); > + return (ETIMEDOUT); > + } > + return (0); > +} > +#endif /* SMALL_KERNEL */