summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-03-07 14:23:37 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2019-03-07 14:23:37 +0100
commita9ba3bc6bfebac17f76f9833d3745fce79c5ea6b (patch)
tree76bead4b9cc474dd1b52434b0c321e5ea8b8b800 /drivers
parentcf99cb69005fff65bcd77d5ada375318d49e4bed (diff)
parentf9b18e51e222937d68acbbc6af9c42d3f5204927 (diff)
downloadbarebox-a9ba3bc6bfebac17f76f9833d3745fce79c5ea6b.tar.gz
barebox-a9ba3bc6bfebac17f76f9833d3745fce79c5ea6b.tar.xz
Merge branch 'for-next/imx-i2c'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/Kconfig3
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-imx-early.c310
-rw-r--r--drivers/i2c/busses/i2c-imx.c272
-rw-r--r--drivers/i2c/busses/i2c-imx.h52
5 files changed, 452 insertions, 186 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 56259d82d4..fc314ec9c6 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -16,3 +16,6 @@ config I2C_MUX
source drivers/i2c/muxes/Kconfig
endif
+
+config I2C_IMX_EARLY
+ bool
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 8dccc38379..61d7c86e76 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
+lwl-$(CONFIG_I2C_IMX_EARLY) += i2c-imx-early.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
diff --git a/drivers/i2c/busses/i2c-imx-early.c b/drivers/i2c/busses/i2c-imx-early.c
new file mode 100644
index 0000000000..d67226441e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-imx-early.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ * Copyright 2006,2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Early I2C support functions to read SPD data or board
+ * information.
+ * Based on U-Boot drivers/i2c/fsl_i2c.c
+ */
+#include <common.h>
+#include <i2c/i2c.h>
+#include <i2c/i2c-early.h>
+
+#include "i2c-imx.h"
+
+struct fsl_i2c {
+ void __iomem *regs;
+ unsigned int i2cr_ien_opcode;
+ unsigned int i2sr_clr_opcode;
+ unsigned int ifdr;
+ unsigned int regshift;
+};
+
+static inline void fsl_i2c_write_reg(unsigned int val,
+ struct fsl_i2c *fsl_i2c,
+ unsigned int reg)
+{
+ reg <<= fsl_i2c->regshift;
+
+ writeb(val, fsl_i2c->regs + reg);
+}
+
+static inline unsigned char fsl_i2c_read_reg(struct fsl_i2c *fsl_i2c,
+ unsigned int reg)
+{
+ reg <<= fsl_i2c->regshift;
+
+ return readb(fsl_i2c->regs + reg);
+}
+
+static int i2c_fsl_poll_status(struct fsl_i2c *fsl_i2c, uint8_t set, uint8_t clear)
+{
+ int timeout = 1000000;
+ uint8_t temp;
+
+ while (1) {
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2SR);
+ if (temp & set)
+ return 0;
+ if (~temp & clear)
+ return 0;
+
+ if (!--timeout) {
+ pr_debug("timeout waiting for status %s 0x%02x, cur status: 0x%02x\n",
+ set ? "set" : "clear",
+ set ? set : clear,
+ temp);
+ return -EIO;
+ }
+ }
+}
+
+static int i2c_fsl_bus_busy(struct fsl_i2c *fsl_i2c)
+{
+ return i2c_fsl_poll_status(fsl_i2c, I2SR_IBB, 0);
+}
+
+static int i2c_fsl_bus_idle(struct fsl_i2c *fsl_i2c)
+{
+ return i2c_fsl_poll_status(fsl_i2c, 0, I2SR_IBB);
+}
+
+static int i2c_fsl_trx_complete(struct fsl_i2c *fsl_i2c)
+{
+ int ret;
+
+ ret = i2c_fsl_poll_status(fsl_i2c, I2SR_IIF, 0);
+ if (ret)
+ return ret;
+
+ fsl_i2c_write_reg(fsl_i2c->i2sr_clr_opcode,
+ fsl_i2c, FSL_I2C_I2SR);
+
+ return 0;
+}
+
+static int i2c_fsl_acked(struct fsl_i2c *fsl_i2c)
+{
+ return i2c_fsl_poll_status(fsl_i2c, 0, I2SR_RXAK);
+}
+
+static int i2c_fsl_start(struct fsl_i2c *fsl_i2c)
+{
+ unsigned int temp = 0;
+ int ret;
+
+ fsl_i2c_write_reg(fsl_i2c->ifdr, fsl_i2c, FSL_I2C_IFDR);
+
+ /* Enable I2C controller */
+ fsl_i2c_write_reg(fsl_i2c->i2sr_clr_opcode,
+ fsl_i2c, FSL_I2C_I2SR);
+ fsl_i2c_write_reg(fsl_i2c->i2cr_ien_opcode,
+ fsl_i2c, FSL_I2C_I2CR);
+
+ /* Wait controller to be stable */
+ udelay(100);
+
+ /* Start I2C transaction */
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp |= I2CR_MSTA;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ ret = i2c_fsl_bus_busy(fsl_i2c);
+ if (ret)
+ return -EAGAIN;
+
+ temp |= I2CR_MTX | I2CR_TXAK;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ return ret;
+}
+
+static void i2c_fsl_stop(struct fsl_i2c *fsl_i2c)
+{
+ unsigned int temp = 0;
+
+ /* Stop I2C transaction */
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+ /* wait for the stop condition to be send, otherwise the i2c
+ * controller is disabled before the STOP is sent completely */
+
+ i2c_fsl_bus_idle(fsl_i2c);
+}
+
+static int i2c_fsl_send(struct fsl_i2c *fsl_i2c, uint8_t data)
+{
+ int ret;
+
+ pr_debug("%s send 0x%02x\n", __func__, data);
+
+ fsl_i2c_write_reg(data, fsl_i2c, FSL_I2C_I2DR);
+
+ ret = i2c_fsl_trx_complete(fsl_i2c);
+ if (ret) {
+ pr_debug("%s timeout 1\n", __func__);
+ return ret;
+ }
+
+ ret = i2c_fsl_acked(fsl_i2c);
+ if (ret) {
+ pr_debug("%s timeout 2\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int i2c_fsl_write(struct fsl_i2c *fsl_i2c, struct i2c_msg *msg)
+{
+ int i, ret;
+
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ ret = i2c_fsl_send(fsl_i2c, msg->addr << 1);
+ if (ret)
+ return ret;
+ }
+
+ /* write data */
+ for (i = 0; i < msg->len; i++) {
+ ret = i2c_fsl_send(fsl_i2c, msg->buf[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int i2c_fsl_read(struct fsl_i2c *fsl_i2c, struct i2c_msg *msg)
+{
+ int i, ret;
+ unsigned int temp;
+
+ /* clear IIF */
+ fsl_i2c_write_reg(fsl_i2c->i2sr_clr_opcode,
+ fsl_i2c, FSL_I2C_I2SR);
+
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ ret = i2c_fsl_send(fsl_i2c, (msg->addr << 1) | 1);
+ if (ret)
+ return ret;
+ }
+
+ /* setup bus to read data */
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp &= ~I2CR_MTX;
+ if (msg->len - 1)
+ temp &= ~I2CR_TXAK;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2DR); /* dummy read */
+
+ /* read data */
+ for (i = 0; i < msg->len; i++) {
+ ret = i2c_fsl_trx_complete(fsl_i2c);
+ if (ret)
+ return ret;
+
+ if (i == (msg->len - 1)) {
+ i2c_fsl_stop(fsl_i2c);
+ } else if (i == (msg->len - 2)) {
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp |= I2CR_TXAK;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+ }
+ msg->buf[i] = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2DR);
+ }
+ return 0;
+}
+
+/**
+ * i2c_fsl_xfer - transfer I2C messages on i.MX compatible I2C controllers
+ * @ctx: driver context pointer
+ * @msgs: pointer to I2C messages
+ * @num: number of messages to transfer
+ *
+ * This function transfers I2C messages on i.MX and compatible I2C controllers.
+ * If successful returns the number of messages transferred, otherwise a negative
+ * error code is returned.
+ */
+int i2c_fsl_xfer(void *ctx, struct i2c_msg *msgs, int num)
+{
+ struct fsl_i2c *fsl_i2c = ctx;
+ unsigned int i, temp;
+ int ret;
+
+ pr_debug("%s enter\n", __func__);
+
+ /* Start I2C transfer */
+ for (i = 0; i < 3; i++) {
+ ret = i2c_fsl_start(fsl_i2c);
+ if (!ret)
+ break;
+ if (ret == -EAGAIN)
+ continue;
+ return ret;
+ }
+
+ /* read/write data */
+ for (i = 0; i < num; i++) {
+ if (i && !(msgs[i].flags & I2C_M_DATA_ONLY)) {
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp |= I2CR_RSTA;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ ret = i2c_fsl_bus_busy(fsl_i2c);
+ if (ret)
+ goto fail0;
+ }
+
+ /* write/read data */
+ if (msgs[i].flags & I2C_M_RD)
+ ret = i2c_fsl_read(fsl_i2c, &msgs[i]);
+ else
+ ret = i2c_fsl_write(fsl_i2c, &msgs[i]);
+ if (ret)
+ goto fail0;
+ }
+
+fail0:
+ /* Stop I2C transfer */
+ i2c_fsl_stop(fsl_i2c);
+
+ /* Disable I2C controller, and force our state to stopped */
+ temp = fsl_i2c->i2cr_ien_opcode ^ I2CR_IEN,
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ return (ret < 0) ? ret : num;
+}
+
+static struct fsl_i2c fsl_i2c;
+
+/**
+ * ls1046_i2c_init - Return a context pointer for accessing I2C on LS1046a
+ * @regs: The base address of the I2C controller to access
+ *
+ * This function returns a context pointer suitable to transfer I2C messages
+ * using i2c_fsl_xfer.
+ */
+void *ls1046_i2c_init(void __iomem *regs)
+{
+ fsl_i2c.regs = regs;
+ fsl_i2c.regshift = 0;
+ fsl_i2c.i2cr_ien_opcode = I2CR_IEN_OPCODE_0;
+ fsl_i2c.i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C;
+ /* Divider for ~100kHz when coming from the ROM */
+ fsl_i2c.ifdr = 0x3e;
+
+ return &fsl_i2c;
+}
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 67937da73a..4c7346063c 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -49,61 +49,7 @@
#include <i2c/i2c.h>
#include <mach/clock.h>
-/* This will be the driver name */
-#define DRIVER_NAME "i2c-fsl"
-
-/* Default value */
-#define FSL_I2C_BIT_RATE 100000 /* 100kHz */
-
-/* IMX I2C registers:
- * the I2C register offset is different between SoCs,
- * to provid support for all these chips, split the
- * register offset into a fixed base address and a
- * variable shift value, then the full register offset
- * will be calculated by
- * reg_off = ( reg_base_addr << reg_shift)
- */
-#define FSL_I2C_IADR 0x00 /* i2c slave address */
-#define FSL_I2C_IFDR 0x01 /* i2c frequency divider */
-#define FSL_I2C_I2CR 0x02 /* i2c control */
-#define FSL_I2C_I2SR 0x03 /* i2c status */
-#define FSL_I2C_I2DR 0x04 /* i2c transfer data */
-#define FSL_I2C_DFSRR 0x14 /* i2c digital filter sampling rate */
-
-#define IMX_I2C_REGSHIFT 2
-#define VF610_I2C_REGSHIFT 0
-
-
-/* Bits of FSL I2C registers */
-#define I2SR_RXAK 0x01
-#define I2SR_IIF 0x02
-#define I2SR_SRW 0x04
-#define I2SR_IAL 0x10
-#define I2SR_IBB 0x20
-#define I2SR_IAAS 0x40
-#define I2SR_ICF 0x80
-#define I2CR_RSTA 0x04
-#define I2CR_TXAK 0x08
-#define I2CR_MTX 0x10
-#define I2CR_MSTA 0x20
-#define I2CR_IIEN 0x40
-#define I2CR_IEN 0x80
-
-/* register bits different operating codes definition:
- * 1) I2SR: Interrupt flags clear operation differ between SoCs:
- * - write zero to clear(w0c) INT flag on i.MX,
- * - but write one to clear(w1c) INT flag on Vybrid.
- * 2) I2CR: I2C module enable operation also differ between SoCs:
- * - set I2CR_IEN bit enable the module on i.MX,
- * - but clear I2CR_IEN bit enable the module on Vybrid.
- */
-#define I2SR_CLR_OPCODE_W0C 0x0
-#define I2SR_CLR_OPCODE_W1C (I2SR_IAL | I2SR_IIF)
-#define I2CR_IEN_OPCODE_0 0x0
-#define I2CR_IEN_OPCODE_1 I2CR_IEN
-
-#define I2C_PM_TIMEOUT 10 /* ms */
-
+#include "i2c-imx.h"
/*
* sorted list of clock divider, register value pairs
@@ -168,7 +114,6 @@ struct fsl_i2c_struct {
struct clk *clk;
struct i2c_adapter adapter;
unsigned int disable_delay;
- int stopped;
unsigned int ifdr; /* FSL_I2C_IFDR */
unsigned int dfsrr; /* FSL_I2C_DFSRR */
struct i2c_bus_recovery_info rinfo;
@@ -191,7 +136,6 @@ static inline unsigned char fsl_i2c_read_reg(struct fsl_i2c_struct *i2c_fsl,
return readb(i2c_fsl->base + reg);
}
-#ifdef CONFIG_I2C_DEBUG
static void i2c_fsl_dump_reg(struct i2c_adapter *adapter)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
@@ -213,54 +157,51 @@ static void i2c_fsl_dump_reg(struct i2c_adapter *adapter)
(reg_sr & I2SR_SRW ? 1 : 0), (reg_sr & I2SR_IIF ? 1 : 0),
(reg_sr & I2SR_RXAK ? 1 : 0));
}
-#else
-static inline void i2c_fsl_dump_reg(struct i2c_adapter *adapter)
-{
- return;
-}
-#endif
-
-static int i2c_fsl_bus_busy(struct i2c_adapter *adapter, int for_busy)
+static int i2c_fsl_poll_status(struct i2c_adapter *adapter, int timeout_ms,
+ uint8_t set, uint8_t clear)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
uint64_t start;
- unsigned int temp;
+ uint8_t temp;
start = get_time_ns();
while (1) {
temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR);
- if (for_busy && (temp & I2SR_IBB))
- break;
- if (!for_busy && !(temp & I2SR_IBB))
- break;
- if (is_timeout(start, 500 * MSECOND)) {
- dev_err(&adapter->dev,
- "<%s> timeout waiting for I2C bus %s\n",
- __func__,for_busy ? "busy" : "not busy");
+ if (temp & set)
+ return 0;
+ if (~temp & clear)
+ return 0;
+
+ if (is_timeout(start, timeout_ms * MSECOND)) {
+ dev_dbg(&adapter->dev,
+ "timeout waiting for status %s 0x%02x, cur status: 0x%02x\n",
+ set ? "set" : "clear",
+ set ? set : clear,
+ temp);
return -EIO;
}
}
+}
- return 0;
+static int i2c_fsl_bus_busy(struct i2c_adapter *adapter)
+{
+ return i2c_fsl_poll_status(adapter, 500, I2SR_IBB, 0);
+}
+
+static int i2c_fsl_bus_idle(struct i2c_adapter *adapter)
+{
+ return i2c_fsl_poll_status(adapter, 500, 0, I2SR_IBB);
}
static int i2c_fsl_trx_complete(struct i2c_adapter *adapter)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
- uint64_t start;
-
- start = get_time_ns();
- while (1) {
- unsigned int reg = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR);
- if (reg & I2SR_IIF)
- break;
+ int ret;
- if (is_timeout(start, 100 * MSECOND)) {
- dev_err(&adapter->dev, "<%s> TXR timeout\n", __func__);
- return -EIO;
- }
- }
+ ret = i2c_fsl_poll_status(adapter, 100, I2SR_IIF, 0);
+ if (ret)
+ return ret;
fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode,
i2c_fsl, FSL_I2C_I2SR);
@@ -270,22 +211,7 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter)
static int i2c_fsl_acked(struct i2c_adapter *adapter)
{
- struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
- uint64_t start;
-
- start = get_time_ns();
- while (1) {
- unsigned int reg = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR);
- if (!(reg & I2SR_RXAK))
- break;
-
- if (is_timeout(start, MSECOND)) {
- dev_dbg(&adapter->dev, "<%s> No ACK\n", __func__);
- return -EIO;
- }
- }
-
- return 0;
+ return i2c_fsl_poll_status(adapter, 1, 0, I2SR_RXAK);
}
static int i2c_fsl_start(struct i2c_adapter *adapter)
@@ -314,7 +240,7 @@ static int i2c_fsl_start(struct i2c_adapter *adapter)
temp |= I2CR_MSTA;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
- result = i2c_fsl_bus_busy(adapter, 1);
+ result = i2c_fsl_bus_busy(adapter);
if (result) {
result = i2c_recover_bus(&i2c_fsl->adapter);
if (result)
@@ -322,8 +248,6 @@ static int i2c_fsl_start(struct i2c_adapter *adapter)
return -EAGAIN;
}
- i2c_fsl->stopped = 0;
-
temp |= I2CR_MTX | I2CR_TXAK;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
@@ -335,24 +259,20 @@ static void i2c_fsl_stop(struct i2c_adapter *adapter)
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
unsigned int temp = 0;
- if (!i2c_fsl->stopped) {
- /* Stop I2C transaction */
- temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
- temp &= ~(I2CR_MSTA | I2CR_MTX);
- fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
- /* wait for the stop condition to be send, otherwise the i2c
- * controller is disabled before the STOP is sent completely */
- i2c_fsl->stopped = i2c_fsl_bus_busy(adapter, 0) ? 0 : 1;
- }
-
- if (!i2c_fsl->stopped) {
- i2c_fsl_bus_busy(adapter, 0);
- i2c_fsl->stopped = 1;
- }
+ /* Stop I2C transaction */
+ temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
+ if (!(temp & I2CR_MSTA))
+ return;
- /* Disable I2C controller, and force our state to stopped */
- temp = i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
+ /* wait for the stop condition to be send, otherwise the i2c
+ * controller is disabled before the STOP is sent completely */
+
+ /* adding this delay helps on low bitrates */
+ udelay(i2c_fsl->disable_delay);
+
+ i2c_fsl_bus_idle(adapter);
}
#ifdef CONFIG_PPC
@@ -462,45 +382,44 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
}
#endif
-static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+static int i2c_fsl_send(struct i2c_adapter *adapter, uint8_t data)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
- int i, result;
+ int result;
- if ( !(msgs->flags & I2C_M_DATA_ONLY) ) {
- dev_dbg(&adapter->dev,
- "<%s> write slave address: addr=0x%02x\n",
- __func__, msgs->addr << 1);
+ dev_dbg(&adapter->dev, "<%s> send 0x%02x\n", __func__, data);
- /* write slave address */
- fsl_i2c_write_reg(msgs->addr << 1, i2c_fsl, FSL_I2C_I2DR);
+ fsl_i2c_write_reg(data, i2c_fsl, FSL_I2C_I2DR);
- result = i2c_fsl_trx_complete(adapter);
- if (result)
- return result;
- result = i2c_fsl_acked(adapter);
+ result = i2c_fsl_trx_complete(adapter);
+ if (result)
+ return result;
+
+ return i2c_fsl_acked(adapter);
+}
+
+static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msg)
+{
+ int i, result;
+
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ result = i2c_fsl_send(adapter, msg->addr << 1);
if (result)
return result;
}
/* write data */
- for (i = 0; i < msgs->len; i++) {
- dev_dbg(&adapter->dev,
- "<%s> write byte: B%d=0x%02X\n",
- __func__, i, msgs->buf[i]);
- fsl_i2c_write_reg(msgs->buf[i], i2c_fsl, FSL_I2C_I2DR);
-
- result = i2c_fsl_trx_complete(adapter);
- if (result)
- return result;
- result = i2c_fsl_acked(adapter);
+ for (i = 0; i < msg->len; i++) {
+ result = i2c_fsl_send(adapter, msg->buf[i]);
if (result)
return result;
}
+
return 0;
}
-static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msg,
+ bool is_last)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
int i, result;
@@ -510,18 +429,8 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode,
i2c_fsl, FSL_I2C_I2SR);
- if ( !(msgs->flags & I2C_M_DATA_ONLY) ) {
- dev_dbg(&adapter->dev,
- "<%s> write slave address: addr=0x%02x\n",
- __func__, (msgs->addr << 1) | 0x01);
-
- /* write slave address */
- fsl_i2c_write_reg((msgs->addr << 1) | 0x01, i2c_fsl, FSL_I2C_I2DR);
-
- result = i2c_fsl_trx_complete(adapter);
- if (result)
- return result;
- result = i2c_fsl_acked(adapter);
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ result = i2c_fsl_send(adapter, (msg->addr << 1) | 1);
if (result)
return result;
}
@@ -529,43 +438,29 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
/* setup bus to read data */
temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
temp &= ~I2CR_MTX;
- if (msgs->len - 1)
+ if (msg->len - 1)
temp &= ~I2CR_TXAK;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR); /* dummy read */
/* read data */
- for (i = 0; i < msgs->len; i++) {
+ for (i = 0; i < msg->len; i++) {
result = i2c_fsl_trx_complete(adapter);
if (result)
return result;
- if (i == (msgs->len - 1)) {
- /*
- * It must generate STOP before read I2DR to prevent
- * controller from generating another clock cycle
- */
- temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
- temp &= ~(I2CR_MSTA | I2CR_MTX);
- fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
-
- /*
- * adding this delay helps on low bitrates
- */
- udelay(i2c_fsl->disable_delay);
-
- i2c_fsl_bus_busy(adapter, 0);
- i2c_fsl->stopped = 1;
- } else if (i == (msgs->len - 2)) {
+ if (is_last && i == msg->len - 1) {
+ i2c_fsl_stop(adapter);
+ } else if (i == (msg->len - 2)) {
temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
temp |= I2CR_TXAK;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
}
- msgs->buf[i] = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR);
+ msg->buf[i] = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR);
dev_dbg(&adapter->dev, "<%s> read byte: B%d=0x%02X\n",
- __func__, i, msgs->buf[i]);
+ __func__, i, msg->buf[i]);
}
return 0;
}
@@ -594,7 +489,7 @@ static int i2c_fsl_xfer(struct i2c_adapter *adapter,
temp |= I2CR_RSTA;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
- result = i2c_fsl_bus_busy(adapter, 1);
+ result = i2c_fsl_bus_busy(adapter);
if (result)
goto fail0;
}
@@ -602,7 +497,7 @@ static int i2c_fsl_xfer(struct i2c_adapter *adapter,
/* write/read data */
if (msgs[i].flags & I2C_M_RD)
- result = i2c_fsl_read(adapter, &msgs[i]);
+ result = i2c_fsl_read(adapter, &msgs[i], i == num - 1);
else
result = i2c_fsl_write(adapter, &msgs[i]);
if (result)
@@ -613,6 +508,10 @@ fail0:
/* Stop I2C transfer */
i2c_fsl_stop(adapter);
+ /* Disable I2C controller, and force our state to stopped */
+ temp = i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
+
return (result < 0) ? result : num;
}
@@ -664,6 +563,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
struct fsl_i2c_struct *i2c_fsl;
struct i2c_platform_data *pdata;
int ret;
+ int bitrate;
pdata = pdev->platform_data;
@@ -705,10 +605,12 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
i2c_fsl->dfsrr = -1;
/* Set up clock divider */
+ bitrate = 100000;
+ of_property_read_u32(pdev->device_node, "clock-frequency", &bitrate);
if (pdata && pdata->bitrate)
- i2c_fsl_set_clk(i2c_fsl, pdata->bitrate);
- else
- i2c_fsl_set_clk(i2c_fsl, FSL_I2C_BIT_RATE);
+ bitrate = pdata->bitrate;
+
+ i2c_fsl_set_clk(i2c_fsl, bitrate);
/* Set up chip registers to defaults */
fsl_i2c_write_reg(i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
@@ -753,9 +655,7 @@ static __maybe_unused struct of_device_id imx_i2c_dt_ids[] = {
static struct driver_d i2c_fsl_driver = {
.probe = i2c_fsl_probe,
- .name = DRIVER_NAME,
-#ifndef CONFIG_PPC
+ .name = "i2c-fsl",
.of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids),
-#endif
};
coredevice_platform_driver(i2c_fsl_driver);
diff --git a/drivers/i2c/busses/i2c-imx.h b/drivers/i2c/busses/i2c-imx.h
new file mode 100644
index 0000000000..3e3e1317f2
--- /dev/null
+++ b/drivers/i2c/busses/i2c-imx.h
@@ -0,0 +1,52 @@
+#ifndef I2C_IMX_H
+#define I2C_IMX_H
+
+/*
+ * IMX I2C registers:
+ * the I2C register offset is different between SoCs, to provide support for
+ * all these chips, split the register offset into a fixed base address and a
+ * variable shift value, then the full register offset will be calculated by:
+ * reg_off = reg_base_addr << reg_shift
+ */
+#define FSL_I2C_IADR 0x00 /* i2c slave address */
+#define FSL_I2C_IFDR 0x01 /* i2c frequency divider */
+#define FSL_I2C_I2CR 0x02 /* i2c control */
+#define FSL_I2C_I2SR 0x03 /* i2c status */
+#define FSL_I2C_I2DR 0x04 /* i2c transfer data */
+#define FSL_I2C_DFSRR 0x05 /* i2c digital filter sampling rate */
+
+#define IMX_I2C_REGSHIFT 2
+#define VF610_I2C_REGSHIFT 0
+
+/* Bits of FSL I2C registers */
+#define I2SR_RXAK 0x01
+#define I2SR_IIF 0x02
+#define I2SR_SRW 0x04
+#define I2SR_IAL 0x10
+#define I2SR_IBB 0x20
+#define I2SR_IAAS 0x40
+#define I2SR_ICF 0x80
+#define I2CR_RSTA 0x04
+#define I2CR_TXAK 0x08
+#define I2CR_MTX 0x10
+#define I2CR_MSTA 0x20
+#define I2CR_IIEN 0x40
+#define I2CR_IEN 0x80
+
+/*
+ * register bits different operating codes definition:
+ *
+ * 1) I2SR: Interrupt flags clear operation differ between SoCs:
+ * - write zero to clear(w0c) INT flag on i.MX,
+ * - but write one to clear(w1c) INT flag on Vybrid.
+ *
+ * 2) I2CR: I2C module enable operation also differ between SoCs:
+ * - set I2CR_IEN bit enable the module on i.MX,
+ * - but clear I2CR_IEN bit enable the module on Vybrid.
+ */
+#define I2SR_CLR_OPCODE_W0C 0x0
+#define I2SR_CLR_OPCODE_W1C (I2SR_IAL | I2SR_IIF)
+#define I2CR_IEN_OPCODE_0 0x0
+#define I2CR_IEN_OPCODE_1 I2CR_IEN
+
+#endif /* I2C_IMX_H */