Index: sys/arm/freescale/imx/imx_i2c.c =================================================================== --- sys/arm/freescale/imx/imx_i2c.c +++ sys/arm/freescale/imx/imx_i2c.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,8 @@ #include #include +#include + #include #include #include "iicbus_if.h" @@ -79,6 +82,32 @@ #define I2C_BAUD_RATE_DEF 0x3F #define I2C_DFSSR_DIV 0x10 +/* + * A table of available divisors and the associated coded values to put in the + * FDR register to achieve that divisor.. There is no algorithmic relationship I + * can see between divisors and the codes that go into the register. The table + * begins and ends with entries that handle insane configuration values. + */ +struct clkdiv { + u_int divisor; + u_int regcode; +}; +static struct clkdiv clkdiv_table[] = { + { 0, 0x20 }, { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, + { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, + { 40, 0x26 }, { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, + { 52, 0x05 }, { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2a }, + { 72, 0x2b }, { 80, 0x2c }, { 88, 0x09 }, { 96, 0x2d }, + { 104, 0x0a }, { 112, 0x2e }, { 128, 0x2f }, { 144, 0x0c }, + { 160, 0x30 }, { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0f }, + { 256, 0x33 }, { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, + { 448, 0x36 }, { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, + { 640, 0x38 }, { 768, 0x39 }, { 896, 0x3a }, { 960, 0x17 }, + { 1024, 0x3b }, { 1152, 0x18 }, { 1280, 0x3c }, { 1536, 0x3d }, + { 1792, 0x3e }, { 1920, 0x1b }, { 2048, 0x3f }, { 2304, 0x1c }, + { 2560, 0x1d }, { 3072, 0x1e }, { 3840, 0x1f }, {UINT_MAX, 0x1f} +}; + #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) @@ -390,28 +419,29 @@ i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr) { struct i2c_softc *sc; - uint8_t baud_rate; + u_int busfreq, div, i, ipgfreq; sc = device_get_softc(dev); - switch (speed) { - case IIC_FAST: - baud_rate = I2C_BAUD_RATE_FAST; - break; - case IIC_SLOW: - case IIC_UNKNOWN: - case IIC_FASTEST: - default: - baud_rate = I2C_BAUD_RATE_DEF; - break; + /* + * Look up the divisor that gives the nearest speed that doesn't exceed + * the configured value for the bus. + */ + ipgfreq = imx_ccm_ipg_hz(); + busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); + div = (ipgfreq + busfreq - 1) / busfreq; + for (i = 0; i < nitems(clkdiv_table); i++) { + if (clkdiv_table[i].divisor >= div) + break; } + div = clkdiv_table[i].regcode; mtx_lock(&sc->mutex); i2c_write_reg(sc, I2C_CONTROL_REG, 0x0); i2c_write_reg(sc, I2C_STATUS_REG, 0x0); DELAY(1000); - i2c_write_reg(sc, I2C_FDR_REG, 20); + i2c_write_reg(sc, I2C_FDR_REG, (uint8_t)div); i2c_write_reg(sc, I2C_CONTROL_REG, I2CCR_MEN); DELAY(1000); i2c_write_reg(sc, I2C_STATUS_REG, 0x0); Index: sys/arm/ti/ti_i2c.c =================================================================== --- sys/arm/ti/ti_i2c.c +++ sys/arm/ti/ti_i2c.c @@ -102,8 +102,7 @@ struct ti_i2c_clock_config { - int speed; - int bitrate; + u_int frequency; /* Bus frequency in Hz */ uint8_t psc; /* Fast/Standard mode prescale divider */ uint8_t scll; /* Fast/Standard mode SCL low time */ uint8_t sclh; /* Fast/Standard mode SCL high time */ @@ -113,12 +112,11 @@ #if defined(SOC_OMAP4) static struct ti_i2c_clock_config ti_omap4_i2c_clock_configs[] = { - { IIC_UNKNOWN, 100000, 23, 13, 15, 0, 0}, - { IIC_SLOW, 100000, 23, 13, 15, 0, 0}, - { IIC_FAST, 400000, 9, 5, 7, 0, 0}, - { IIC_FASTEST, 1000000, 5, 3, 4, 0, 0}, - /* { IIC_FASTEST, 3200000, 1, 113, 115, 7, 10}, - HS mode */ - { -1, 0 } + { 100000, 23, 13, 15, 0, 0}, + { 400000, 9, 5, 7, 0, 0}, + { 1000000, 5, 3, 4, 0, 0}, +/* { 3200000, 1, 113, 115, 7, 10}, - HS mode */ + { 0 /* Table terminator */ } }; #endif @@ -128,11 +126,9 @@ * clock to 12Mhz, for 400kHz I2C clock set the internal clock to 24Mhz. */ static struct ti_i2c_clock_config ti_am335x_i2c_clock_configs[] = { - { IIC_UNKNOWN, 100000, 7, 59, 61, 0, 0}, - { IIC_SLOW, 100000, 7, 59, 61, 0, 0}, - { IIC_FAST, 400000, 3, 23, 25, 0, 0}, - { IIC_FASTEST, 400000, 3, 23, 25, 0, 0}, - { -1, 0 } + { 100000, 7, 59, 61, 0, 0}, + { 400000, 3, 23, 25, 0, 0}, + { 0 /* Table terminator */ } }; #endif @@ -508,6 +504,7 @@ { int timeout; struct ti_i2c_clock_config *clkcfg; + u_int busfreq; uint16_t fifo_trsh, reg, scll, sclh; switch (ti_chip()) { @@ -524,13 +521,24 @@ default: panic("Unknown Ti SoC, unable to reset the i2c"); } - while (clkcfg->speed != -1) { - if (clkcfg->speed == speed) + + /* + * If we haven't attached the bus yet, just init at the default slow + * speed. This lets us get the hardware initialized enough to attach + * the bus which is where the real speed configuration is handled. After + * the bus is attached, get the configured speed from it. Search the + * configuration table for the best speed we can do that doesn't exceed + * the requested speed. + */ + if (sc->sc_iicbus == NULL) + busfreq = 100000; + else + busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); + for (;;) { + if (clkcfg[1].frequency == 0 || clkcfg[1].frequency > busfreq) break; clkcfg++; } - if (clkcfg->speed == -1) - return (EINVAL); /* * 23.1.4.3 - HS I2C Software Reset Index: sys/dev/iicbus/iicbus.h =================================================================== --- sys/dev/iicbus/iicbus.h +++ sys/dev/iicbus/iicbus.h @@ -44,6 +44,7 @@ u_char strict; /* deny operations that violate the * I2C protocol */ struct mtx lock; + u_int bus_freq; /* Configured bus Hz. */ }; struct iicbus_ivar @@ -67,7 +68,8 @@ #define IICBUS_UNLOCK(sc) mtx_unlock(&(sc)->lock) #define IICBUS_ASSERT_LOCKED(sc) mtx_assert(&(sc)->lock, MA_OWNED) -extern int iicbus_generic_intr(device_t dev, int event, char *buf); +int iicbus_generic_intr(device_t dev, int event, char *buf); +void iicbus_init_frequency(device_t dev, u_int bus_freq); extern driver_t iicbus_driver; extern devclass_t iicbus_devclass; Index: sys/dev/iicbus/iicbus.c =================================================================== --- sys/dev/iicbus/iicbus.c +++ sys/dev/iicbus/iicbus.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -96,6 +97,7 @@ sc->dev = dev; mtx_init(&sc->lock, "iicbus", NULL, MTX_DEF); + iicbus_init_frequency(dev, 0); iicbus_reset(dev, IIC_FASTEST, 0, NULL); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "strict", &strict) == 0) @@ -243,6 +245,51 @@ return (IIC_ENOTSUPP); } +void +iicbus_init_frequency(device_t dev, u_int bus_freq) +{ + struct iicbus_softc *sc = IICBUS_SOFTC(dev); + + /* + * If a bus frequency value was passed in, use it. Otherwise initialize + * it first to the standard i2c 100KHz frequency, then override that + * from a hint if one exists. + */ + if (bus_freq > 0) + sc->bus_freq = bus_freq; + else { + sc->bus_freq = 100000; + resource_int_value(device_get_name(dev), device_get_unit(dev), + "frequency", (int *)&sc->bus_freq); + } + /* + * Set up the sysctl that allows the bus frequency to be changed. + * It is flagged as a tunable so that the user can set the value in + * loader(8), and that will override any other setting from any source. + * The sysctl tunable/value is the one most directly controlled by the + * user and thus the one that always takes precedence. + */ + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "frequency", CTLFLAG_RW | CTLFLAG_TUN, &sc->bus_freq, + sc->bus_freq, "Bus frequency in Hz"); +} + +static u_int +iicbus_get_frequency(device_t dev, u_char speed) +{ + struct iicbus_softc *sc = IICBUS_SOFTC(dev); + + /* + * If the frequency has not been configured for the bus, or the request + * is specifically for SLOW speed, use the standard 100KHz rate, else + * use the configured bus speed. + */ + if (sc->bus_freq == 0 || speed == IIC_SLOW) + return (100000); + return (sc->bus_freq); +} + static device_method_t iicbus_methods[] = { /* device interface */ DEVMETHOD(device_probe, iicbus_probe), @@ -260,6 +307,7 @@ /* iicbus interface */ DEVMETHOD(iicbus_transfer, iicbus_transfer), + DEVMETHOD(iicbus_get_frequency, iicbus_get_frequency), DEVMETHOD_END }; Index: sys/dev/iicbus/iicbus_if.m =================================================================== --- sys/dev/iicbus/iicbus_if.m +++ sys/dev/iicbus/iicbus_if.m @@ -31,6 +31,15 @@ INTERFACE iicbus; +CODE { + static u_int + iicbus_default_frequency(device_t bus, u_char speed) + { + + return (100000); + } +}; + # # Interpret interrupt # @@ -115,3 +124,14 @@ struct iic_msg *msgs; uint32_t nmsgs; }; + +# +# Return the frequency in Hz for the bus running at the given +# symbolic speed. Only the IIC_SLOW speed has meaning, it is always +# 100KHz. The UNKNOWN, FAST, and FASTEST rates all map to the +# configured bus frequency, or 100KHz when not otherwise configured. +# +METHOD u_int get_frequency { + device_t dev; + u_char speed; +} DEFAULT iicbus_default_frequency; Index: sys/dev/ofw/ofw_iicbus.c =================================================================== --- sys/dev/ofw/ofw_iicbus.c +++ sys/dev/ofw/ofw_iicbus.c @@ -101,12 +101,24 @@ { struct iicbus_softc *sc = IICBUS_SOFTC(dev); struct ofw_iicbus_devinfo *dinfo; - phandle_t child; - pcell_t paddr; + phandle_t child, node; + pcell_t freq, paddr; device_t childdev; sc->dev = dev; mtx_init(&sc->lock, "iicbus", NULL, MTX_DEF); + + /* + * If there is a clock-frequency property for the device node, use it as + * the starting value for the bus frequency. Then call the common + * routine that handles the tunable/sysctl which allows the FDT value to + * be overridden by the user. + */ + node = ofw_bus_get_node(dev); + freq = 0; + OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)); + iicbus_init_frequency(dev, freq); + iicbus_reset(dev, IIC_FASTEST, 0, NULL); bus_generic_probe(dev); @@ -115,8 +127,7 @@ /* * Attach those children represented in the device tree. */ - for (child = OF_child(ofw_bus_get_node(dev)); child != 0; - child = OF_peer(child)) { + for (child = OF_child(node); child != 0; child = OF_peer(child)) { /* * Try to get the I2C address first from the i2c-address * property, then try the reg property. It moves around