svn commit: r276278 - in stable/10: share/man/man4 sys/arm/freescale/imx sys/arm/ti sys/dev/iicbus sys/dev/ofw

Ian Lepore ian at FreeBSD.org
Sat Dec 27 02:37:55 UTC 2014


Author: ian
Date: Sat Dec 27 02:37:52 2014
New Revision: 276278
URL: https://svnweb.freebsd.org/changeset/base/276278

Log:
  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

Modified:
  stable/10/share/man/man4/iicbus.4
  stable/10/sys/arm/freescale/imx/imx_i2c.c
  stable/10/sys/arm/ti/ti_i2c.c
  stable/10/sys/dev/iicbus/iicbus.c
  stable/10/sys/dev/iicbus/iicbus.h
  stable/10/sys/dev/iicbus/iicbus_if.m
  stable/10/sys/dev/ofw/ofw_iicbus.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/share/man/man4/iicbus.4
==============================================================================
--- stable/10/share/man/man4/iicbus.4	Sat Dec 27 02:17:35 2014	(r276277)
+++ stable/10/share/man/man4/iicbus.4	Sat Dec 27 02:37:52 2014	(r276278)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 24, 2014
+.Dd November 17, 2014
 .Dt IICBUS 4
 .Os
 .Sh NAME
@@ -103,11 +103,58 @@ Some I2C interfaces are available:
 .It Sy lpbb Ta "parallel port specific bit-banging interface"
 .It Sy bktr Ta "Brooktree848 video chipset, hardware and software master-only interface"
 .El
+.Sh BUS FREQUENCY CONFIGURATION
+The operating frequency of an I2C bus may be fixed or configurable.
+The bus may be used as part of some larger standard interface, and that
+interface specification may require a fixed frequency.  
+The driver for that hardware would not honor an attempt to configure a 
+different speed.
+A general purpose I2C bus, such as those found in many embedded systems,
+will often support multiple bus frequencies.  
+.Pp
+When a system supports multiple I2C busses, a different frequency can
+be configured for each bus by number, represented by the
+.Va %d
+in the variable names below.
+Busses can be configured using any combination of device hints,
+Flattened Device Tree (FDT) data, tunables set via
+.Xr loader 8 ,
+or at runtime using
+.Xr sysctl 8 .
+When configuration is supplied using more than one method, FDT and
+hint data will be overridden by a tunable, which can be overriden by
+.Xr sysctl 8 .
+.Ss Device Hints
+Set
+.Va hint.iicbus.%d.frequency
+to the frequency in Hz, on systems that use device hints to configure
+I2C devices.
+The hint is also honored by systems that use FDT data if
+no frequency is configured using FDT.
+.Ss Flattened Device Tree Data
+Configure the I2C bus speed using the FDT standard
+.Va clock-frequency
+property of the node describing the I2C controller hardware.
+.Ss Sysctl and Tunable
+Set
+.Va dev.iicbus.%d.frequency
+in
+.Xr loader.conf 5 .
+The same variable can be changed at any time with
+.Xr sysctl 8 .
+Reset the bus using
+.Xr i2c 8
+or the
+.Xr iic 4 
+.Va I2CRSTCARD
+ioctl to make the change take effect.
 .Sh SEE ALSO
+.Xr fdt 4 ,
 .Xr iic 4 ,
 .Xr iicbb 4 ,
 .Xr lpbb 4 ,
-.Xr pcf 4
+.Xr pcf 4 ,
+.Xr i2c 8
 .Sh HISTORY
 The
 .Nm

Modified: stable/10/sys/arm/freescale/imx/imx_i2c.c
==============================================================================
--- stable/10/sys/arm/freescale/imx/imx_i2c.c	Sat Dec 27 02:17:35 2014	(r276277)
+++ stable/10/sys/arm/freescale/imx/imx_i2c.c	Sat Dec 27 02:37:52 2014	(r276278)
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
 #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 @@ __FBSDID("$FreeBSD$");
 #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 @@ __FBSDID("$FreeBSD$");
 #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 @@ static int
 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);

Modified: stable/10/sys/arm/ti/ti_i2c.c
==============================================================================
--- stable/10/sys/arm/ti/ti_i2c.c	Sat Dec 27 02:17:35 2014	(r276277)
+++ stable/10/sys/arm/ti/ti_i2c.c	Sat Dec 27 02:37:52 2014	(r276278)
@@ -102,8 +102,7 @@ struct ti_i2c_softc
 
 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 */
@@ -116,27 +115,30 @@ struct ti_i2c_clock_config
 #endif
 
 #if defined(SOC_OMAP4)
+/*
+ * OMAP4 i2c bus clock is 96MHz / ((psc + 1) * (scll + 7 + sclh + 5)).
+ * The prescaler values for 100KHz and 400KHz modes come from the table in the
+ * OMAP4 TRM.  The table doesn't list 1MHz; these values should give that speed.
+ */
 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,  3,   5,   7,  0,  0},
+/*	{ 3200000,  1, 113, 115,  7, 10}, - HS mode */
+	{       0 /* Table terminator */ }
 };
 #endif
 
 #if defined(SOC_TI_AM335X)
 /*
- * AM335X doesn't support HS mode.  For 100kHz I2C clock set the internal
- * clock to 12Mhz, for 400kHz I2C clock set the internal clock to 24Mhz.
+ * AM335x i2c bus clock is 48MHZ / ((psc + 1) * (scll + 7 + sclh + 5))
+ * In all cases we prescale the clock to 24MHz as recommended in the manual.
  */
 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, 1, 111, 117, 0, 0},
+	{  400000, 1,  23,  25, 0, 0},
+	{ 1000000, 1,   5,   7, 0, 0},
+	{       0 /* Table terminator */ }
 };
 #endif
 
@@ -512,6 +514,7 @@ ti_i2c_reset(struct ti_i2c_softc *sc, u_
 {
 	int timeout;
 	struct ti_i2c_clock_config *clkcfg;
+	u_int busfreq;
 	uint16_t fifo_trsh, reg, scll, sclh;
 
 	switch (ti_chip()) {
@@ -528,13 +531,24 @@ ti_i2c_reset(struct ti_i2c_softc *sc, u_
 	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

Modified: stable/10/sys/dev/iicbus/iicbus.c
==============================================================================
--- stable/10/sys/dev/iicbus/iicbus.c	Sat Dec 27 02:17:35 2014	(r276277)
+++ stable/10/sys/dev/iicbus/iicbus.c	Sat Dec 27 02:37:52 2014	(r276278)
@@ -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,
 	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
 };

Modified: stable/10/sys/dev/iicbus/iicbus.h
==============================================================================
--- stable/10/sys/dev/iicbus/iicbus.h	Sat Dec 27 02:17:35 2014	(r276277)
+++ stable/10/sys/dev/iicbus/iicbus.h	Sat Dec 27 02:37:52 2014	(r276278)
@@ -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;

Modified: stable/10/sys/dev/iicbus/iicbus_if.m
==============================================================================
--- stable/10/sys/dev/iicbus/iicbus_if.m	Sat Dec 27 02:17:35 2014	(r276277)
+++ stable/10/sys/dev/iicbus/iicbus_if.m	Sat Dec 27 02:37:52 2014	(r276278)
@@ -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;

Modified: stable/10/sys/dev/ofw/ofw_iicbus.c
==============================================================================
--- stable/10/sys/dev/ofw/ofw_iicbus.c	Sat Dec 27 02:17:35 2014	(r276277)
+++ stable/10/sys/dev/ofw/ofw_iicbus.c	Sat Dec 27 02:37:52 2014	(r276278)
@@ -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


More information about the svn-src-stable mailing list