summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2014-12-27 02:37:52 +0000
committerian <ian@FreeBSD.org>2014-12-27 02:37:52 +0000
commitd082e488cb5d0ab8f76c2694943f4b1688869f8b (patch)
tree8da22701848310928d5201db5385f6480785c535 /sys/dev
parent799361e237fdb276321038f0ef7f547ad2a0a863 (diff)
downloadFreeBSD-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.c48
-rw-r--r--sys/dev/iicbus/iicbus.h4
-rw-r--r--sys/dev/iicbus/iicbus_if.m20
-rw-r--r--sys/dev/ofw/ofw_iicbus.c19
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
OpenPOWER on IntegriCloud