diff options
author | ian <ian@FreeBSD.org> | 2014-12-27 02:37:52 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2014-12-27 02:37:52 +0000 |
commit | d082e488cb5d0ab8f76c2694943f4b1688869f8b (patch) | |
tree | 8da22701848310928d5201db5385f6480785c535 /sys/dev | |
parent | 799361e237fdb276321038f0ef7f547ad2a0a863 (diff) | |
download | FreeBSD-src-d082e488cb5d0ab8f76c2694943f4b1688869f8b.zip FreeBSD-src-d082e488cb5d0ab8f76c2694943f4b1688869f8b.tar.gz |
MFC r274641, r274644, r274822, r276049:
Allow i2c bus speed to be configured via hints, FDT data, and sysctl.
Implement bus speed setting for OMAP4, AM335x, and imx5/6.
Fix the i2c bus speed divisors for TI OMAP4 and AM335x to give the
advertised 100, 400, and 1000 KHz speeds.
PR: 195009
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/iicbus/iicbus.c | 48 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbus.h | 4 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbus_if.m | 20 | ||||
-rw-r--r-- | sys/dev/ofw/ofw_iicbus.c | 19 |
4 files changed, 86 insertions, 5 deletions
diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c index d919277..dc56760 100644 --- a/sys/dev/iicbus/iicbus.c +++ b/sys/dev/iicbus/iicbus.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #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 @@ iicbus_attach(device_t dev) 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 @@ iicbus_null_repeated_start(device_t dev, u_char addr) 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 @@ static device_method_t iicbus_methods[] = { /* iicbus interface */ DEVMETHOD(iicbus_transfer, iicbus_transfer), + DEVMETHOD(iicbus_get_frequency, iicbus_get_frequency), DEVMETHOD_END }; diff --git a/sys/dev/iicbus/iicbus.h b/sys/dev/iicbus/iicbus.h index 54fe980..b5905ad 100644 --- a/sys/dev/iicbus/iicbus.h +++ b/sys/dev/iicbus/iicbus.h @@ -44,6 +44,7 @@ struct iicbus_softc 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 @@ IICBUS_ACCESSOR(nostop, NOSTOP, bool) #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; diff --git a/sys/dev/iicbus/iicbus_if.m b/sys/dev/iicbus/iicbus_if.m index c57fac5..120b5e7 100644 --- a/sys/dev/iicbus/iicbus_if.m +++ b/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 @@ METHOD int transfer { 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; diff --git a/sys/dev/ofw/ofw_iicbus.c b/sys/dev/ofw/ofw_iicbus.c index 1574f6e..f6d1be5 100644 --- a/sys/dev/ofw/ofw_iicbus.c +++ b/sys/dev/ofw/ofw_iicbus.c @@ -101,12 +101,24 @@ ofw_iicbus_attach(device_t dev) { 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 @@ ofw_iicbus_attach(device_t dev) /* * 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 |