summaryrefslogtreecommitdiffstats
path: root/sys/arm
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/arm
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/arm')
-rw-r--r--sys/arm/freescale/imx/imx_i2c.c54
-rw-r--r--sys/arm/ti/ti_i2c.c52
2 files changed, 75 insertions, 31 deletions
diff --git a/sys/arm/freescale/imx/imx_i2c.c b/sys/arm/freescale/imx/imx_i2c.c
index 3c192ea..abcb014 100644
--- a/sys/arm/freescale/imx/imx_i2c.c
+++ b/sys/arm/freescale/imx/imx_i2c.c
@@ -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);
diff --git a/sys/arm/ti/ti_i2c.c b/sys/arm/ti/ti_i2c.c
index e865f91..22a1877 100644
--- a/sys/arm/ti/ti_i2c.c
+++ b/sys/arm/ti/ti_i2c.c
@@ -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_char speed)
{
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_char speed)
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
OpenPOWER on IntegriCloud