Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146324103
D1174.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D1174.diff
View Options
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 <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
+#include <sys/limits.h>
#include <sys/module.h>
#include <sys/resource.h>
@@ -45,6 +46,8 @@
#include <sys/lock.h>
#include <sys/mutex.h>
+#include <arm/freescale/imx/imx_ccmvar.h>
+
#include <dev/iicbus/iiconf.h>
#include <dev/iicbus/iicbus.h>
#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 <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
+#include <sys/sysctl.h>
#include <sys/bus.h>
#include <dev/iicbus/iiconf.h>
@@ -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
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 2, 6:20 PM (3 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29166510
Default Alt Text
D1174.diff (10 KB)
Attached To
Mode
D1174: Allow i2c bus speed to be configured via hints, FDT data, and sysctl.
Attached
Detach File
Event Timeline
Log In to Comment