[linux-yocto] [PATCH 07/38] drivers/i2c: Changes to support axxia BSP
Cristian Bercaru
cristian.bercaru at windriver.com
Wed May 20 12:23:50 PDT 2015
From: Charlie Paul <cpaul.windriver at gmail.com>
These files are used to add the i2c functionality
to the LSI axxia 5500 board.
Signed-off-by: Charlie Paul <cpaul.windriver at gmail.com>
---
drivers/i2c/busses/Kconfig | 15 +
drivers/i2c/busses/acp3400-i2c.c | 515 +++++++++++++++++++++++++++++++++
drivers/i2c/busses/i2c-axxia.c | 592 ++++++++++++++++++++++----------------
include/linux/i2c-axxia.h | 39 +++
4 files changed, 916 insertions(+), 245 deletions(-)
create mode 100644 drivers/i2c/busses/acp3400-i2c.c
create mode 100644 include/linux/i2c-axxia.h
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 566814e..a0367d1 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1085,6 +1085,21 @@ config SCx200_ACB
This support is also available as a module. If so, the module
will be called scx200_acb.
+config I2C_AXXIA
+ tristate "Axxia I2C bus support"
+ select I2C_ALGOBIT
+ help
+ Say yes if you want to support the I2C bus on Axxia platforms.
+
+ If you don't know, say Y.
+
+config ACP3400_I2C
+ tristate "ACP3400 I2C support"
+ depends on ACP
+ default y
+ help
+ I2C adapter for acp476 based boards
+
config I2C_OPAL
tristate "IBM OPAL I2C driver"
depends on PPC_POWERNV
diff --git a/drivers/i2c/busses/acp3400-i2c.c b/drivers/i2c/busses/acp3400-i2c.c
new file mode 100644
index 0000000..7cdf452
--- /dev/null
+++ b/drivers/i2c/busses/acp3400-i2c.c
@@ -0,0 +1,515 @@
+/*
+ * ACP3400 I2C adapter
+ *
+ * Based on DU-TS I2C Adapter Driver
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#define DRV_NAME "acp3400-i2c"
+
+struct acp3400_i2c_regs {
+ unsigned int txconfig;
+ unsigned int rxconfig;
+ unsigned int txstatus;
+ unsigned int rxstatus;
+ unsigned int irqenable;
+ unsigned int irqclear;
+ unsigned int irqstatus;
+ unsigned int clkconfig;
+ unsigned int startsetup;
+ unsigned int stopsetup;
+ unsigned int datasetup;
+ unsigned int bypassmode;
+ unsigned int slaveaddr;
+ unsigned int txdata0;
+ unsigned int txdata1;
+ unsigned int rxdata0;
+ unsigned int rxdata1;
+};
+
+struct acp3400_i2c_timer_regs {
+ unsigned int loadval; /* 0x20 */
+ unsigned int val; /* 0x24 */
+ unsigned int control; /* 0x28 */
+ unsigned int irq_clr; /* 0x2c */
+ unsigned int irq_stat_raw; /* 0x30 */
+ unsigned int irq_stat; /* 0x34 */
+ unsigned int bg_loadval; /* 0x38 */
+};
+
+
+/* Master Clock Configuration */
+/* Configured clock frequency i2c_freq = 100kHz. */
+
+/* I2C register values */
+#define ACPI2C_CLK_100KHZ (1000 | (1000 << 16))
+#define ACPI2C_MSTSHC (940 | (800 << 16))
+#define ACPI2C_MSPSHC (800 | (0 << 16))
+#define ACPI2C_MDSHC (255 | (127 << 16))
+
+#define ACPI2C_XFER_START 0x00000001
+#define ACPI2C_XFER_DONE 0x00000001
+#define ACPI2C_READ_MODE 0x00000200
+#define ACPI2C_STOP_MODE 0x20000000
+#define ACPI2C_MASTER_MODE 0x00000100
+#define ACPI2C_MASTER_OP_CLEAR 0x00000400
+#define ACPI2C_10BIT_ADDR 0x00000080
+#define ACPI2C_CLEAR_IRQ 0x0000000F
+#define ACPI2C_DELAY 500 /* us */
+#define ACPI2C_RETRIES 100
+#define ACPI2C_REG_BSIZE 4 /* bytes */
+#define ACPI2C_DATA_REGS 2
+
+struct acp3400_i2c {
+ struct device *dev;
+ struct i2c_adapter adap;
+ struct acp3400_i2c_regs __iomem *i2c_regs;
+ struct acp3400_i2c_timer_regs __iomem *timer_regs;
+ struct mutex i2c_lock;
+};
+
+#ifdef ACP3400_I2C_DEBUG
+static void dump_regs(struct acp3400_i2c *i2c)
+{
+ pr_info("i2c-reg: txconfig %8.8x\n",
+ in_le32(&i2c->i2c_regs->txconfig));
+ pr_info("i2c-reg: rxconfig %8.8x\n",
+ in_le32(&i2c->i2c_regs->rxconfig));
+ pr_info("i2c-reg: txstatus %8.8x\n",
+ in_le32(&i2c->i2c_regs->txstatus));
+ pr_info("i2c-reg: rxstatus %8.8x\n",
+ in_le32(&i2c->i2c_regs->rxstatus));
+ pr_info("i2c-reg: irqenable %8.8x\n",
+ in_le32(&i2c->i2c_regs->irqenable));
+ pr_info("i2c-reg: irqclear %8.8x\n",
+ in_le32(&i2c->i2c_regs->irqclear));
+ pr_info("i2c-reg: irqstatus %8.8x\n",
+ in_le32(&i2c->i2c_regs->irqstatus));
+ pr_info("i2c-reg: clkconfig %8.8x\n",
+ in_le32(&i2c->i2c_regs->clkconfig));
+ pr_info("i2c-reg: startsetup %8.8x\n",
+ in_le32(&i2c->i2c_regs->startsetup));
+ pr_info("i2c-reg: stopsetup %8.8x\n",
+ in_le32(&i2c->i2c_regs->stopsetup));
+ pr_info("i2c-reg: datasetup %8.8x\n",
+ in_le32(&i2c->i2c_regs->datasetup));
+ pr_info("i2c-reg: bypassmode %8.8x\n",
+ in_le32(&i2c->i2c_regs->bypassmode));
+ pr_info("i2c-reg: slaveaddr %8.8x\n",
+ in_le32(&i2c->i2c_regs->slaveaddr));
+ pr_info("i2c-reg: txdata0 %8.8x\n",
+ in_le32(&i2c->i2c_regs->txdata0));
+ pr_info("i2c-reg: txdata1 %8.8x\n",
+ in_le32(&i2c->i2c_regs->txdata1));
+ pr_info("i2c-reg: rxdata0 %8.8x\n",
+ in_le32(&i2c->i2c_regs->rxdata0));
+ pr_info("i2c-reg: rxdata1 %8.8x\n",
+ in_le32(&i2c->i2c_regs->rxdata1));
+ pr_info("i2c-timer-reg: loadval %8.8x\n",
+ in_le32(&i2c->timer_regs->loadval));
+ pr_info("i2c-timer-reg: val %8.8x\n",
+ in_le32(&i2c->timer_regs->val));
+ pr_info("i2c-timer-reg: control %8.8x\n",
+ in_le32(&i2c->timer_regs->control));
+}
+#endif
+/*
+ * Low level write routine
+ */
+static int acp3400_i2c_write_bytes(struct acp3400_i2c *i2c,
+ struct i2c_msg *msgs)
+{
+ unsigned char *bufp = msgs->buf;
+ unsigned int reg_value, data[2] = {0, 0};
+ int cnt, ret = 0;
+
+ if (msgs->len > (ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS))
+ msgs->len = ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS;
+
+ /* Set message */
+ for (cnt = 0; cnt < msgs->len; cnt++) {
+ data[1] <<= 8;
+ data[1] |= ((data[0] >> 24) & 0xFF);
+ data[0] <<= 8;
+ data[0] |= bufp[cnt];
+ }
+ out_le32(&i2c->i2c_regs->txdata0, data[0]);
+ out_le32(&i2c->i2c_regs->txdata1, data[1]);
+
+ /* setup and start a transmission */
+ reg_value = ACPI2C_MASTER_MODE | ACPI2C_STOP_MODE;
+ reg_value |= (msgs->len << 1) & 0x1e;
+ if (msgs->flags & I2C_M_TEN) {
+ reg_value |= ACPI2C_10BIT_ADDR;
+ /* TODO update slave address accordingly */
+ }
+ out_le32(&i2c->i2c_regs->txconfig, reg_value);
+
+ reg_value &= ~ACPI2C_STOP_MODE;
+ out_le32(&i2c->i2c_regs->txconfig, reg_value);
+
+ reg_value |= ACPI2C_XFER_START;
+ out_le32(&i2c->i2c_regs->txconfig, reg_value);
+
+ /* Check if the message has been sent
+ * Wait a totally of 1 s for the transmission */
+ reg_value = cnt = 0;
+ while (0 == reg_value && cnt++ < ACPI2C_RETRIES) {
+ udelay(ACPI2C_DELAY);
+ /* Read transmission status */
+ reg_value = in_le32(&i2c->i2c_regs->txstatus);
+ }
+#ifdef ACP3400_I2C_DEBUG
+ if (ACPI2C_XFER_DONE != reg_value)
+ dump_regs(i2c);
+#endif
+ /* Clear registers */
+ out_le32(&i2c->i2c_regs->irqclear, ACPI2C_CLEAR_IRQ);
+ out_le32(&i2c->i2c_regs->txconfig, ACPI2C_MASTER_OP_CLEAR);
+
+ out_le32(&i2c->i2c_regs->txconfig, ACPI2C_STOP_MODE);
+
+ if (ACPI2C_XFER_DONE == reg_value)
+ ret = msgs->len;
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+/*
+ * Low level read routine
+ */
+
+static int acp3400_i2c_read_bytes(struct acp3400_i2c *i2c,
+ struct i2c_msg *msgs)
+{
+ unsigned char *bufp = msgs->buf;
+ unsigned int reg_value, data[2];
+ int cnt, ret = 0;
+
+ if (msgs->len > (ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS))
+ msgs->len = ACPI2C_REG_BSIZE * ACPI2C_DATA_REGS;
+
+ /* Setup a reception */
+ reg_value = (msgs->len << 1) & 0x1e;
+ if (msgs->flags & I2C_M_TEN) {
+ reg_value |= ACPI2C_10BIT_ADDR;
+ /* TODO update slave address accordingly */
+ }
+ out_le32(&i2c->i2c_regs->rxconfig, reg_value);
+
+ /* set read mode and start clock */
+ reg_value |= ACPI2C_XFER_START;
+ out_le32(&i2c->i2c_regs->rxconfig, reg_value);
+
+ reg_value = ACPI2C_STOP_MODE | ACPI2C_MASTER_MODE | ACPI2C_READ_MODE;
+ out_le32(&i2c->i2c_regs->txconfig, reg_value);
+
+ reg_value &= ~ACPI2C_STOP_MODE;
+ out_le32(&i2c->i2c_regs->txconfig, reg_value);
+
+ reg_value |= ACPI2C_XFER_START;
+ out_le32(&i2c->i2c_regs->txconfig, reg_value);
+
+ /* Check if the message has been received
+ * Wait a totally of 1 s for the reception */
+ reg_value = cnt = 0;
+ while (0 == (ACPI2C_XFER_DONE & reg_value) &&
+ cnt++ < ACPI2C_RETRIES) {
+ udelay(ACPI2C_DELAY);
+ /* Read transmission status */
+ reg_value = in_le32(&i2c->i2c_regs->rxstatus);
+ }
+
+ /* get message */
+ data[0] = in_le32(&i2c->i2c_regs->rxdata0);
+ data[1] = in_le32(&i2c->i2c_regs->rxdata1);
+ for (cnt = 0; cnt < msgs->len; cnt++) {
+ if (cnt < ACPI2C_REG_BSIZE)
+ bufp[cnt] = data[0] >> ((8 * cnt) & 0xFF);
+ else
+ bufp[cnt] = data[1] >>
+ ((8 * (cnt - ACPI2C_REG_BSIZE)) & 0xFF);
+ }
+#ifdef ACP3400_I2C_DEBUG
+ if (ACPI2C_XFER_DONE != (reg_value & 0x03))
+ dump_regs(i2c);
+#endif
+ /* clear registers */
+ out_le32(&i2c->i2c_regs->irqclear, ACPI2C_CLEAR_IRQ);
+ out_le32(&i2c->i2c_regs->txconfig, ACPI2C_MASTER_OP_CLEAR);
+
+ out_le32(&i2c->i2c_regs->txconfig, ACPI2C_STOP_MODE);
+
+ if (ACPI2C_XFER_DONE == (reg_value & 0x03))
+ ret = msgs->len;
+ else
+ ret = -EIO;
+
+ return ret;
+}
+
+/*
+ * I2C timer setup
+ */
+static void acp3400_i2c_timer_setup(struct acp3400_i2c *i2c)
+{
+ /* disable timer 1 */
+ out_le32(&i2c->timer_regs->control, 0);
+ /* Program the Timer1 Load Value register with a value that sets
+ * the timer period to 250 ns (that is, 4 MHz frequency). When you
+ * configure the ACP peripheral clock (clk_per) for 200 MHz, the
+ * Timer1 Load Value is 0x31. */
+ out_le32(&i2c->timer_regs->loadval, 0x31);
+ out_le32(&i2c->timer_regs->bg_loadval, 0x31);
+
+ /* Configure and enable Timer1 for periodic wrapping mode
+ * with a prescaler of 1 by writing 0xc0 to the Timer1 Control
+ * Register. */
+ out_le32(&i2c->timer_regs->control, 0xc0);
+}
+/*
+ * Low level master transfer routine
+ */
+static int acp3400_i2c_xfer_bytes(struct acp3400_i2c *i2c,
+ struct i2c_msg *msgs)
+{
+ int ret = 0;
+
+ mutex_lock(&i2c->i2c_lock);
+ /* Prepare ACP3400 I2C for a transaction */
+ out_le32(&i2c->i2c_regs->txconfig,
+ ACPI2C_MASTER_OP_CLEAR | ACPI2C_MASTER_MODE);
+
+ out_le32(&i2c->i2c_regs->txconfig,
+ ACPI2C_MASTER_MODE | ACPI2C_STOP_MODE);
+
+ /* I2C clock frequency and duty cycle */
+ out_le32(&i2c->i2c_regs->clkconfig, ACPI2C_CLK_100KHZ);
+ /* The setup and hold durations for the START condition. */
+ out_le32(&i2c->i2c_regs->startsetup, ACPI2C_MSTSHC);
+ /* The setup and hold durations for the STOP condition. */
+ out_le32(&i2c->i2c_regs->stopsetup, ACPI2C_MSPSHC);
+ /* The setup and hold durations for the data bits. */
+ out_le32(&i2c->i2c_regs->datasetup, ACPI2C_MDSHC);
+ /* Set Slave Address */
+ out_le32(&i2c->i2c_regs->slaveaddr, msgs->addr);
+ /* Disable the actions for which the host requires to be interrupted */
+ out_le32(&i2c->i2c_regs->irqenable, 0);
+
+ /* Send/Receive Data */
+ if (msgs->flags & I2C_M_RD)
+ ret = acp3400_i2c_read_bytes(i2c, msgs);
+ else
+ ret = acp3400_i2c_write_bytes(i2c, msgs);
+
+ mutex_unlock(&i2c->i2c_lock);
+ return ret;
+}
+static void acp3400_i2c_dummy_xfer(struct acp3400_i2c *i2c)
+{
+ /* Prepare ACP3400 I2C for a transaction */
+ out_le32(&i2c->i2c_regs->txconfig,
+ ACPI2C_MASTER_OP_CLEAR | ACPI2C_MASTER_MODE);
+
+ out_le32(&i2c->i2c_regs->txconfig,
+ ACPI2C_MASTER_MODE | ACPI2C_STOP_MODE);
+
+ /* I2C clock frequency and duty cycle */
+ out_le32(&i2c->i2c_regs->clkconfig, ACPI2C_CLK_100KHZ);
+ /* The setup and hold durations for the START condition. */
+ out_le32(&i2c->i2c_regs->startsetup, ACPI2C_MSTSHC);
+ /* The setup and hold durations for the STOP condition. */
+ out_le32(&i2c->i2c_regs->stopsetup, ACPI2C_MSPSHC);
+ /* The setup and hold durations for the data bits. */
+ out_le32(&i2c->i2c_regs->datasetup, ACPI2C_MDSHC);
+ /* Set Dummy Slave Address */
+ out_le32(&i2c->i2c_regs->slaveaddr, 0x7f);
+ /* Disable the actions for which the host requires to be interrupted */
+ out_le32(&i2c->i2c_regs->irqenable, 0);
+ /* Number of bytes 0, clear stop mode */
+ out_le32(&i2c->i2c_regs->txconfig, ACPI2C_MASTER_MODE);
+
+ /* Set Transmit Ready - triggers the transmit transaction */
+ out_le32(&i2c->i2c_regs->txconfig,
+ (ACPI2C_XFER_START | ACPI2C_MASTER_MODE));
+
+}
+
+/*
+ * Generic master transfer entrypoint.
+ * Returns the number of processed messages or error (<0)
+ */
+static int acp3400_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct acp3400_i2c *i2c = i2c_get_adapdata(adap);
+ int msg_cnt, ret = 0;
+
+ if (!num)
+ return 0;
+
+#ifdef ACP3400_I2C_DEBUG
+ if (num == 1 && msgs[0].addr == 0x7f && msgs[0].len == 0) {
+ mutex_lock(&i2c->i2c_lock);
+ acp3400_i2c_dummy_xfer(i2c);
+ mutex_unlock(&i2c->i2c_lock);
+ return 0;
+ }
+#endif
+ /*
+ * Check the sanity of the passed messages.
+ * Uhh, generic i2c layer is more suitable place for such code...
+ */
+ if ((msgs[0].addr > 0x3ff) ||
+ (!(msgs[0].flags & I2C_M_TEN) && (msgs[0].addr > 0x7f)))
+ return -EINVAL;
+
+ for (msg_cnt = 0; msg_cnt < num; ++msg_cnt) {
+ if (msgs[msg_cnt].len <= 0)
+ return -EINVAL;
+ if ((msgs[0].addr != msgs[msg_cnt].addr) ||
+ ((msgs[0].flags & I2C_M_TEN) !=
+ (msgs[msg_cnt].flags & I2C_M_TEN)))
+ return -EINVAL;
+ }
+
+ /* Do real transfer */
+ for (msg_cnt = 0; msg_cnt < num; msg_cnt++)
+ ret = acp3400_i2c_xfer_bytes(i2c, &msgs[msg_cnt]);
+ return ret < 0 ? ret : num;
+}
+
+static u32 acp3400_i2c_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm acp3400_i2c_algo = {
+ .master_xfer = acp3400_i2c_xfer,
+ .functionality = acp3400_i2c_functionality,
+};
+
+static struct i2c_adapter acp3400_i2c_ops = {
+ .owner = THIS_MODULE,
+ .name = "ACP3400 adapter",
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .algo = &acp3400_i2c_algo,
+ .timeout = HZ,
+};
+
+static int acp34xx_i2c_probe(struct platform_device *dev)
+{
+ struct device_node *np = dev->dev.of_node;
+
+ struct acp3400_i2c *i2c;
+ int result = -ENODEV;
+ const u32 *field;
+
+ if (!np)
+ return -ENODEV;
+
+ field = of_get_property(np, "enabled", NULL);
+ if (!field || (field && (0 == *field)))
+ return -EINVAL;
+
+ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ goto err;
+
+ i2c->i2c_regs = of_iomap(np, 0);
+ i2c->timer_regs = of_iomap(np, 1);
+ if (!i2c->i2c_regs || !i2c->timer_regs) {
+ pr_err("%s: failed to map I/O\n", np->full_name);
+ goto err;
+ }
+
+ i2c->adap = acp3400_i2c_ops;
+ i2c_set_adapdata(&i2c->adap, i2c);
+ i2c->adap.dev.of_node = of_node_get(np);
+ mutex_init(&i2c->i2c_lock);
+
+ /* I2C timer setup */
+ acp3400_i2c_timer_setup(i2c);
+ acp3400_i2c_dummy_xfer(i2c);
+ result = i2c_add_adapter(&i2c->adap);
+ if (result < 0) {
+ pr_err("%s: failed to add adapter\n",
+ np->full_name);
+ goto err;
+ }
+
+ pr_info("%s: adapter has been added\n", np->full_name);
+
+ dev_set_drvdata(&dev->dev, i2c);
+ return 0;
+err:
+ if (i2c) {
+ if (i2c->i2c_regs)
+ iounmap(i2c->i2c_regs);
+ if (i2c->timer_regs)
+ iounmap(i2c->timer_regs);
+ kfree(i2c);
+ }
+
+ return result;
+}
+
+
+static int acp34xx_i2c_remove(struct platform_device *dev)
+{
+ struct acp3400_i2c *i2c = dev_get_drvdata(&dev->dev);
+
+ i2c_del_adapter(&i2c->adap);
+ kfree(i2c);
+
+ return 0;
+}
+
+static struct of_device_id acp_i2c_match[] = {
+ {
+ .compatible = "acp-i2c",
+ },
+ {
+ .compatible = "acp, acp3400-i2c",
+ },
+ { /* end of list */ },
+};
+
+static struct platform_driver acp_i2c_driver = {
+ .driver = {
+ .name = "acp-i2c",
+ .owner = THIS_MODULE,
+ .of_match_table = acp_i2c_match,
+ },
+ .probe = acp34xx_i2c_probe,
+ .remove = acp34xx_i2c_remove,
+};
+
+module_platform_driver(acp_i2c_driver);
+
+MODULE_AUTHOR("Andrey Panteleev <andrey.xx.panteleev at ericsson.com>");
+MODULE_DESCRIPTION("I2C adapter for ACP3400");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c
index 768a598..6c84826 100644
--- a/drivers/i2c/busses/i2c-axxia.c
+++ b/drivers/i2c/busses/i2c-axxia.c
@@ -1,134 +1,163 @@
/*
- * This driver implements I2C master functionality using the LSI API2C
- * controller.
- *
- * NOTE: The controller has a limitation in that it can only do transfers of
- * maximum 255 bytes at a time. If a larger transfer is attempted, error code
- * (-EINVAL) is returned.
+ * drivers/i2c/busses/i2c-axxia.c
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
+ *
+ * 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.
*/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/init.h>
+#include <linux/io.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
#define SCL_WAIT_TIMEOUT_NS 25000000
-#define I2C_XFER_TIMEOUT (msecs_to_jiffies(250))
+#define I2C_XFER_TIMEOUT (msecs_to_jiffies(500))
#define I2C_STOP_TIMEOUT (msecs_to_jiffies(100))
-#define FIFO_SIZE 8
-
-#define GLOBAL_CONTROL 0x00
-#define GLOBAL_MST_EN BIT(0)
-#define GLOBAL_SLV_EN BIT(1)
-#define GLOBAL_IBML_EN BIT(2)
-#define INTERRUPT_STATUS 0x04
-#define INTERRUPT_ENABLE 0x08
-#define INT_SLV BIT(1)
-#define INT_MST BIT(0)
-#define WAIT_TIMER_CONTROL 0x0c
-#define WT_EN BIT(15)
-#define WT_VALUE(_x) ((_x) & 0x7fff)
-#define IBML_TIMEOUT 0x10
-#define IBML_LOW_MEXT 0x14
-#define IBML_LOW_SEXT 0x18
-#define TIMER_CLOCK_DIV 0x1c
-#define I2C_BUS_MONITOR 0x20
-#define SOFT_RESET 0x24
-#define MST_COMMAND 0x28
-#define CMD_BUSY (1<<3)
-#define CMD_MANUAL (0x00 | CMD_BUSY)
-#define CMD_AUTO (0x01 | CMD_BUSY)
-#define MST_RX_XFER 0x2c
-#define MST_TX_XFER 0x30
-#define MST_ADDR_1 0x34
-#define MST_ADDR_2 0x38
-#define MST_DATA 0x3c
-#define MST_TX_FIFO 0x40
-#define MST_RX_FIFO 0x44
-#define MST_INT_ENABLE 0x48
-#define MST_INT_STATUS 0x4c
-#define MST_STATUS_RFL (1 << 13) /* RX FIFO serivce */
-#define MST_STATUS_TFL (1 << 12) /* TX FIFO service */
-#define MST_STATUS_SNS (1 << 11) /* Manual mode done */
-#define MST_STATUS_SS (1 << 10) /* Automatic mode done */
-#define MST_STATUS_SCC (1 << 9) /* Stop complete */
-#define MST_STATUS_IP (1 << 8) /* Invalid parameter */
-#define MST_STATUS_TSS (1 << 7) /* Timeout */
-#define MST_STATUS_AL (1 << 6) /* Arbitration lost */
-#define MST_STATUS_ND (1 << 5) /* NAK on data phase */
-#define MST_STATUS_NA (1 << 4) /* NAK on address phase */
-#define MST_STATUS_NAK (MST_STATUS_NA | \
- MST_STATUS_ND)
-#define MST_STATUS_ERR (MST_STATUS_NAK | \
- MST_STATUS_AL | \
- MST_STATUS_IP | \
- MST_STATUS_TSS)
-#define MST_TX_BYTES_XFRD 0x50
-#define MST_RX_BYTES_XFRD 0x54
-#define SCL_HIGH_PERIOD 0x80
-#define SCL_LOW_PERIOD 0x84
-#define SPIKE_FLTR_LEN 0x88
-#define SDA_SETUP_TIME 0x8c
-#define SDA_HOLD_TIME 0x90
+#define TX_FIFO_SIZE 8
+#define RX_FIFO_SIZE 8
+
+struct i2c_regs {
+ __le32 global_control;
+ __le32 interrupt_status;
+ __le32 interrupt_enable;
+ __le32 wait_timer_control;
+ __le32 ibml_timeout;
+ __le32 ibml_low_mext;
+ __le32 ibml_low_sext;
+ __le32 timer_clock_div;
+ __le32 i2c_bus_monitor;
+ __le32 soft_reset;
+ __le32 mst_command;
+#define CMD_MANUAL 0x08
+#define CMD_AUTO 0x09
+ __le32 mst_rx_xfer;
+ __le32 mst_tx_xfer;
+ __le32 mst_addr_1;
+ __le32 mst_addr_2;
+ __le32 mst_data;
+ __le32 mst_tx_fifo;
+ __le32 mst_rx_fifo;
+ __le32 mst_int_enable;
+ __le32 mst_int_status;
+#define MST_STATUS_RFL (1<<13) /* RX FIFO serivce */
+#define MST_STATUS_TFL (1<<12) /* TX FIFO service */
+#define MST_STATUS_SNS (1<<11) /* Manual mode done */
+#define MST_STATUS_SS (1<<10) /* Automatic mode done */
+#define MST_STATUS_SCC (1<<9) /* Stop complete */
+#define MST_STATUS_IP (1<<8) /* Invalid parameter */
+#define MST_STATUS_TSS (1<<7) /* Timeout */
+#define MST_STATUS_AL (1<<6) /* Arbitration lost */
+#define MST_STATUS_NAK (MST_STATUS_NA | MST_STATUS_ND)
+#define MST_STATUS_ND (1<<5) /* NAK on data phase */
+#define MST_STATUS_NA (1<<4) /* NAK on address phase */
+#define MST_STATUS_ERR (MST_STATUS_NAK | \
+ MST_STATUS_AL | \
+ MST_STATUS_IP | \
+ MST_STATUS_TSS)
+ __le32 mst_tx_bytes_xfrd;
+ __le32 mst_rx_bytes_xfrd;
+ __le32 slv_addr_dec_ctl;
+ __le32 slv_addr_1;
+ __le32 slv_addr_2;
+ __le32 slv_rx_ctl;
+ __le32 slv_data;
+ __le32 slv_rx_fifo;
+ __le32 slv_int_enable;
+ __le32 slv_int_status;
+ __le32 slv_read_dummy;
+ __le32 reserved;
+ __le32 scl_high_period;
+ __le32 scl_low_period;
+ __le32 spike_fltr_len;
+ __le32 sda_setup_time;
+ __le32 sda_hold_time;
+ __le32 smb_alert;
+ __le32 udid_w7;
+ __le32 udid_w6;
+ __le32 udid_w5;
+ __le32 udid_w4;
+ __le32 udid_w3;
+ __le32 udid_w2;
+ __le32 udid_w1;
+ __le32 udid_w0;
+ __le32 arppec_cfg_stat;
+ __le32 slv_arp_int_enable;
+ __le32 slv_arp_int_status;
+ __le32 mst_arp_int_enable;
+ __le32 mst_arp_int_status;
+};
+
/**
- * axxia_i2c_dev - I2C device context
- * @base: pointer to register struct
- * @msg: pointer to current message
- * @msg_xfrd: number of bytes transferred in msg
- * @msg_err: error code for completed message
- * @msg_complete: xfer completion object
- * @dev: device reference
- * @adapter: core i2c abstraction
- * @i2c_clk: clock reference for i2c input clock
- * @bus_clk_rate: current i2c bus clock rate
+ * I2C device context
*/
struct axxia_i2c_dev {
- void __iomem *base;
- struct i2c_msg *msg;
- size_t msg_xfrd;
- int msg_err;
- struct completion msg_complete;
+ /** device reference */
struct device *dev;
+ /** core i2c abstraction */
struct i2c_adapter adapter;
+ /* clock reference for i2c input clock */
struct clk *i2c_clk;
+ /* pointer to registers */
+ struct i2c_regs __iomem *regs;
+ /* xfer completion object */
+ struct completion msg_complete;
+ /* pointer to current message */
+ struct i2c_msg *msg;
+ /* number of bytes transferred in msg */
+ size_t msg_xfrd;
+ /* error code for completed message */
+ int msg_err;
+ /* IRQ number (or 0 if not using interrupt) */
+ int irq;
+ /* current i2c bus clock rate */
u32 bus_clk_rate;
};
-static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask)
+static void
+i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask)
{
- u32 int_en;
+ u32 int_mask = readl(&idev->regs->mst_int_enable);
- int_en = readl(idev->base + MST_INT_ENABLE);
- writel(int_en & ~mask, idev->base + MST_INT_ENABLE);
+ int_mask &= ~mask;
+ writel(int_mask, &idev->regs->mst_int_enable);
}
-static void i2c_int_enable(struct axxia_i2c_dev *idev, u32 mask)
+static void
+i2c_int_enable(struct axxia_i2c_dev *idev, u32 mask)
{
- u32 int_en;
+ u32 int_mask = readl(&idev->regs->mst_int_enable);
- int_en = readl(idev->base + MST_INT_ENABLE);
- writel(int_en | mask, idev->base + MST_INT_ENABLE);
+ int_mask |= mask;
+ writel(int_mask, &idev->regs->mst_int_enable);
}
/**
- * ns_to_clk - Convert time (ns) to clock cycles for the given clock frequency.
+ * Convert nanoseconds to clock cycles for the given clock frequency.
*/
-static u32 ns_to_clk(u64 ns, u32 clk_mhz)
+static u32
+ns_to_clk(u64 ns, u32 clk_mhz)
{
- return div_u64(ns * clk_mhz, 1000);
+ return div_u64(ns*clk_mhz, 1000);
}
-static int axxia_i2c_init(struct axxia_i2c_dev *idev)
+static int
+axxia_i2c_init(struct axxia_i2c_dev *idev)
{
u32 divisor = clk_get_rate(idev->i2c_clk) / idev->bus_clk_rate;
u32 clk_mhz = clk_get_rate(idev->i2c_clk) / 1000000;
@@ -142,9 +171,9 @@ static int axxia_i2c_init(struct axxia_i2c_dev *idev)
idev->bus_clk_rate, clk_mhz, divisor);
/* Reset controller */
- writel(0x01, idev->base + SOFT_RESET);
+ writel(0x01, &idev->regs->soft_reset);
timeout = jiffies + msecs_to_jiffies(100);
- while (readl(idev->base + SOFT_RESET) & 1) {
+ while (readl(&idev->regs->soft_reset) & 1) {
if (time_after(jiffies, timeout)) {
dev_warn(idev->dev, "Soft reset failed\n");
break;
@@ -152,35 +181,37 @@ static int axxia_i2c_init(struct axxia_i2c_dev *idev)
}
/* Enable Master Mode */
- writel(0x1, idev->base + GLOBAL_CONTROL);
+ writel(0x1, &idev->regs->global_control);
if (idev->bus_clk_rate <= 100000) {
/* Standard mode SCL 50/50, tSU:DAT = 250 ns */
- t_high = divisor * 1 / 2;
- t_low = divisor * 1 / 2;
+ t_high = divisor*1/2;
+ t_low = divisor*1/2;
t_setup = ns_to_clk(250, clk_mhz);
} else {
/* Fast mode SCL 33/66, tSU:DAT = 100 ns */
- t_high = divisor * 1 / 3;
- t_low = divisor * 2 / 3;
+ t_high = divisor*1/3;
+ t_low = divisor*2/3;
t_setup = ns_to_clk(100, clk_mhz);
}
/* SCL High Time */
- writel(t_high, idev->base + SCL_HIGH_PERIOD);
+ writel(t_high, &idev->regs->scl_high_period);
/* SCL Low Time */
- writel(t_low, idev->base + SCL_LOW_PERIOD);
+ writel(t_low, &idev->regs->scl_low_period);
/* SDA Setup Time */
- writel(t_setup, idev->base + SDA_SETUP_TIME);
+ writel(t_setup, &idev->regs->sda_setup_time);
/* SDA Hold Time, 300ns */
- writel(ns_to_clk(300, clk_mhz), idev->base + SDA_HOLD_TIME);
+ writel(ns_to_clk(300, clk_mhz), &idev->regs->sda_hold_time);
/* Filter <50ns spikes */
- writel(ns_to_clk(50, clk_mhz), idev->base + SPIKE_FLTR_LEN);
+ writel(ns_to_clk(50, clk_mhz), &idev->regs->spike_fltr_len);
/* Configure Time-Out Registers */
tmo_clk = ns_to_clk(SCL_WAIT_TIMEOUT_NS, clk_mhz);
- /* Find prescaler value that makes tmo_clk fit in 15-bits counter. */
+ /*
+ Find the prescaler value that makes tmo_clk fit in 15-bits counter.
+ */
for (prescale = 0; prescale < 15; ++prescale) {
if (tmo_clk <= 0x7fff)
break;
@@ -190,59 +221,72 @@ static int axxia_i2c_init(struct axxia_i2c_dev *idev)
tmo_clk = 0x7fff;
/* Prescale divider (log2) */
- writel(prescale, idev->base + TIMER_CLOCK_DIV);
+ writel(prescale, &idev->regs->timer_clock_div);
/* Timeout in divided clocks */
- writel(WT_EN | WT_VALUE(tmo_clk), idev->base + WAIT_TIMER_CONTROL);
+ writel((1<<15) | tmo_clk, &idev->regs->wait_timer_control);
/* Mask all master interrupt bits */
i2c_int_disable(idev, ~0);
/* Interrupt enable */
- writel(0x01, idev->base + INTERRUPT_ENABLE);
+ writel(0x01, &idev->regs->interrupt_enable);
+
+ dev_dbg(idev->dev, "SDA_SETUP: %08x\n",
+ readl(&idev->regs->sda_setup_time));
+ dev_dbg(idev->dev, "SDA_HOLD: %08x\n",
+ readl(&idev->regs->sda_hold_time));
+ dev_dbg(idev->dev, "SPIKE_FILTER_LEN: %08x\n",
+ readl(&idev->regs->spike_fltr_len));
+ dev_dbg(idev->dev, "TIMER_DIV: %08x\n",
+ readl(&idev->regs->timer_clock_div));
+ dev_dbg(idev->dev, "WAIT_TIMER: %08x\n",
+ readl(&idev->regs->wait_timer_control));
return 0;
}
-static int i2c_m_rd(const struct i2c_msg *msg)
+static int
+i2c_m_rd(const struct i2c_msg *msg)
{
return (msg->flags & I2C_M_RD) != 0;
}
-static int i2c_m_ten(const struct i2c_msg *msg)
+static int
+i2c_m_ten(const struct i2c_msg *msg)
{
return (msg->flags & I2C_M_TEN) != 0;
}
-static int i2c_m_recv_len(const struct i2c_msg *msg)
+static int
+i2c_m_recv_len(const struct i2c_msg *msg)
{
return (msg->flags & I2C_M_RECV_LEN) != 0;
}
-/**
- * axxia_i2c_empty_rx_fifo - Fetch data from RX FIFO and update SMBus block
- * transfer length if this is the first byte of such a transfer.
- */
-static int axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev)
+static int
+axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev)
{
struct i2c_msg *msg = idev->msg;
- size_t rx_fifo_avail = readl(idev->base + MST_RX_FIFO);
+ size_t rx_fifo_avail = readl(&idev->regs->mst_rx_fifo);
int bytes_to_transfer = min(rx_fifo_avail, msg->len - idev->msg_xfrd);
- while (bytes_to_transfer-- > 0) {
- int c = readl(idev->base + MST_DATA);
+ while (0 < bytes_to_transfer--) {
+ int c = readl(&idev->regs->mst_data);
if (idev->msg_xfrd == 0 && i2c_m_recv_len(msg)) {
/*
* Check length byte for SMBus block read
*/
- if (c <= 0 || c > I2C_SMBUS_BLOCK_MAX) {
+ if (c <= 0) {
idev->msg_err = -EPROTO;
i2c_int_disable(idev, ~0);
complete(&idev->msg_complete);
break;
+ } else if (c > I2C_SMBUS_BLOCK_MAX) {
+ c = I2C_SMBUS_BLOCK_MAX;
}
msg->len = 1 + c;
- writel(msg->len, idev->base + MST_RX_XFER);
+ writel(msg->len, &idev->regs->mst_rx_xfer);
}
msg->buf[idev->msg_xfrd++] = c;
}
@@ -250,38 +294,51 @@ static int axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev)
return 0;
}
-/**
- * axxia_i2c_fill_tx_fifo - Fill TX FIFO from current message buffer.
- * @return: Number of bytes left to transfer.
- */
-static int axxia_i2c_fill_tx_fifo(struct axxia_i2c_dev *idev)
+static int
+axxia_i2c_fill_tx_fifo(struct axxia_i2c_dev *idev)
{
struct i2c_msg *msg = idev->msg;
- size_t tx_fifo_avail = FIFO_SIZE - readl(idev->base + MST_TX_FIFO);
+ size_t tx_fifo_avail = TX_FIFO_SIZE - readl(&idev->regs->mst_tx_fifo);
int bytes_to_transfer = min(tx_fifo_avail, msg->len - idev->msg_xfrd);
- int ret = msg->len - idev->msg_xfrd - bytes_to_transfer;
- while (bytes_to_transfer-- > 0)
- writel(msg->buf[idev->msg_xfrd++], idev->base + MST_DATA);
+ while (0 < bytes_to_transfer--)
+ writel(msg->buf[idev->msg_xfrd++], &idev->regs->mst_data);
- return ret;
+ return 0;
}
-static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
+static char *
+status_str(u32 status)
{
- struct axxia_i2c_dev *idev = _dev;
- u32 status;
-
- if (!(readl(idev->base + INTERRUPT_STATUS) & INT_MST))
- return IRQ_NONE;
-
- /* Read interrupt status bits */
- status = readl(idev->base + MST_INT_STATUS);
+ static char buf[128];
+
+ buf[0] = '\0';
+
+ if (status & MST_STATUS_RFL)
+ strcat(buf, "RFL ");
+ if (status & MST_STATUS_TFL)
+ strcat(buf, "TFL ");
+ if (status & MST_STATUS_SNS)
+ strcat(buf, "SNS ");
+ if (status & MST_STATUS_SS)
+ strcat(buf, "SS ");
+ if (status & MST_STATUS_SCC)
+ strcat(buf, "SCC ");
+ if (status & MST_STATUS_TSS)
+ strcat(buf, "TSS ");
+ if (status & MST_STATUS_AL)
+ strcat(buf, "AL ");
+ if (status & MST_STATUS_ND)
+ strcat(buf, "ND ");
+ if (status & MST_STATUS_NA)
+ strcat(buf, "NA ");
+ return buf;
+}
- if (!idev->msg) {
- dev_warn(idev->dev, "unexpected interrupt\n");
- goto out;
- }
+static void
+axxia_i2c_service_irq(struct axxia_i2c_dev *idev)
+{
+ u32 status = readl(&idev->regs->mst_int_status);
/* RX FIFO needs service? */
if (i2c_m_rd(idev->msg) && (status & MST_STATUS_RFL))
@@ -289,123 +346,149 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
/* TX FIFO needs service? */
if (!i2c_m_rd(idev->msg) && (status & MST_STATUS_TFL)) {
- if (axxia_i2c_fill_tx_fifo(idev) == 0)
+ if (idev->msg_xfrd < idev->msg->len)
+ axxia_i2c_fill_tx_fifo(idev);
+ else
i2c_int_disable(idev, MST_STATUS_TFL);
}
if (status & MST_STATUS_SCC) {
- /* Stop completed */
+ /* Stop completed? */
i2c_int_disable(idev, ~0);
complete(&idev->msg_complete);
- } else if (status & MST_STATUS_SNS) {
- /* Transfer done */
- i2c_int_disable(idev, ~0);
+ } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) {
+ /* Transfer done? */
if (i2c_m_rd(idev->msg) && idev->msg_xfrd < idev->msg->len)
axxia_i2c_empty_rx_fifo(idev);
+ i2c_int_disable(idev, ~0);
complete(&idev->msg_complete);
} else if (unlikely(status & MST_STATUS_ERR)) {
- /* Transfer error */
+ /* Transfer error? */
+ idev->msg_err = status & MST_STATUS_ERR;
i2c_int_disable(idev, ~0);
- if (status & MST_STATUS_AL)
- idev->msg_err = -EAGAIN;
- else if (status & MST_STATUS_NAK)
- idev->msg_err = -ENXIO;
- else
- idev->msg_err = -EIO;
- dev_dbg(idev->dev, "error %#x, addr=%#x rx=%u/%u tx=%u/%u\n",
- status,
- idev->msg->addr,
- readl(idev->base + MST_RX_BYTES_XFRD),
- readl(idev->base + MST_RX_XFER),
- readl(idev->base + MST_TX_BYTES_XFRD),
- readl(idev->base + MST_TX_XFER));
+ dev_dbg(idev->dev, "error %s, rx=%u/%u tx=%u/%u\n",
+ status_str(status),
+ readl(&idev->regs->mst_rx_bytes_xfrd),
+ readl(&idev->regs->mst_rx_xfer),
+ readl(&idev->regs->mst_tx_bytes_xfrd),
+ readl(&idev->regs->mst_tx_xfer));
complete(&idev->msg_complete);
}
+}
+
+static irqreturn_t
+axxia_i2c_isr(int irq, void *_dev)
+{
+ struct axxia_i2c_dev *idev = _dev;
+
+ if ((readl(&idev->regs->interrupt_status) & 0x1) == 0)
+ return IRQ_NONE;
+
+ if (!idev->msg)
+ return IRQ_NONE;
+
+ axxia_i2c_service_irq(idev);
-out:
/* Clear interrupt */
- writel(INT_MST, idev->base + INTERRUPT_STATUS);
+ writel(0x01, &idev->regs->interrupt_status);
return IRQ_HANDLED;
}
-static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
+
+static int
+axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
{
u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS;
- u32 rx_xfer, tx_xfer;
u32 addr_1, addr_2;
int ret;
- if (msg->len > 255) {
- dev_warn(idev->dev, "unsupported length %u\n", msg->len);
+ if (msg->len == 0 || msg->len > 255)
return -EINVAL;
- }
- idev->msg = msg;
+ idev->msg = msg;
idev->msg_xfrd = 0;
- idev->msg_err = 0;
+ idev->msg_err = 0;
reinit_completion(&idev->msg_complete);
+ if (i2c_m_rd(msg)) {
+ /* TX 0 bytes */
+ writel(0, &idev->regs->mst_tx_xfer);
+ /* RX # bytes */
+ if (i2c_m_recv_len(msg))
+ writel(I2C_SMBUS_BLOCK_MAX, &idev->regs->mst_rx_xfer);
+ else
+ writel(msg->len, &idev->regs->mst_rx_xfer);
+ } else {
+ /* TX # bytes */
+ writel(msg->len, &idev->regs->mst_tx_xfer);
+ /* RX 0 bytes */
+ writel(0, &idev->regs->mst_rx_xfer);
+ }
+
if (i2c_m_ten(msg)) {
/* 10-bit address
- * addr_1: 5'b11110 | addr[9:8] | (R/nW)
+ * addr_1: 5'b11110 | addr[9:8] | (R/W)
* addr_2: addr[7:0]
*/
addr_1 = 0xF0 | ((msg->addr >> 7) & 0x06);
addr_2 = msg->addr & 0xFF;
} else {
/* 7-bit address
- * addr_1: addr[6:0] | (R/nW)
+ * addr_1: addr[6:0] | (R/W)
* addr_2: dont care
*/
addr_1 = (msg->addr << 1) & 0xFF;
addr_2 = 0;
}
+ if (i2c_m_rd(msg))
+ addr_1 |= 1;
+ writel(addr_1, &idev->regs->mst_addr_1);
+ writel(addr_2, &idev->regs->mst_addr_2);
if (i2c_m_rd(msg)) {
- /* I2C read transfer */
- rx_xfer = i2c_m_recv_len(msg) ? I2C_SMBUS_BLOCK_MAX : msg->len;
- tx_xfer = 0;
- addr_1 |= 1; /* Set the R/nW bit of the address */
+ int_mask |= MST_STATUS_RFL;
} else {
- /* I2C write transfer */
- rx_xfer = 0;
- tx_xfer = msg->len;
+ axxia_i2c_fill_tx_fifo(idev);
+ if (idev->msg_xfrd < msg->len)
+ int_mask |= MST_STATUS_TFL;
}
- writel(rx_xfer, idev->base + MST_RX_XFER);
- writel(tx_xfer, idev->base + MST_TX_XFER);
- writel(addr_1, idev->base + MST_ADDR_1);
- writel(addr_2, idev->base + MST_ADDR_2);
-
- if (i2c_m_rd(msg))
- int_mask |= MST_STATUS_RFL;
- else if (axxia_i2c_fill_tx_fifo(idev) != 0)
- int_mask |= MST_STATUS_TFL;
-
/* Start manual mode */
- writel(CMD_MANUAL, idev->base + MST_COMMAND);
-
- i2c_int_enable(idev, int_mask);
-
- ret = wait_for_completion_timeout(&idev->msg_complete,
- I2C_XFER_TIMEOUT);
-
- i2c_int_disable(idev, int_mask);
+ writel(0x8, &idev->regs->mst_command);
+
+ if (idev->irq > 0) {
+ i2c_int_enable(idev, int_mask);
+ ret = wait_for_completion_timeout(&idev->msg_complete,
+ I2C_XFER_TIMEOUT);
+ i2c_int_disable(idev, int_mask);
+ WARN_ON(readl(&idev->regs->mst_command) & 0x8);
+ } else {
+ unsigned long tmo = jiffies + I2C_XFER_TIMEOUT;
- if (readl(idev->base + MST_COMMAND) & CMD_BUSY)
- dev_warn(idev->dev, "busy after xfer\n");
+ do {
+ /* Poll interrupt status */
+ axxia_i2c_service_irq(idev);
+ ret = try_wait_for_completion(&idev->msg_complete);
+ } while (!ret && time_before(jiffies, tmo));
+ }
- if (ret == 0)
- idev->msg_err = -ETIMEDOUT;
+ if (ret == 0) {
+ dev_warn(idev->dev, "xfer timeout (%#x)\n", msg->addr);
+ axxia_i2c_init(idev);
+ return -ETIMEDOUT;
+ }
- if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO)
+ if (unlikely(idev->msg_err != 0)) {
axxia_i2c_init(idev);
+ return -EIO;
+ }
- return idev->msg_err;
+ return 0;
}
-static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
+static int
+axxia_i2c_stop(struct axxia_i2c_dev *idev)
{
u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC;
int ret;
@@ -413,7 +496,7 @@ static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
reinit_completion(&idev->msg_complete);
/* Issue stop */
- writel(0xb, idev->base + MST_COMMAND);
+ writel(0xb, &idev->regs->mst_command);
i2c_int_enable(idev, int_mask);
ret = wait_for_completion_timeout(&idev->msg_complete,
I2C_STOP_TIMEOUT);
@@ -421,8 +504,7 @@ static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
if (ret == 0)
return -ETIMEDOUT;
- if (readl(idev->base + MST_COMMAND) & CMD_BUSY)
- dev_warn(idev->dev, "busy after stop\n");
+ WARN_ON(readl(&idev->regs->mst_command) & 0x8);
return 0;
}
@@ -434,33 +516,36 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
int i;
int ret = 0;
- for (i = 0; ret == 0 && i < num; ++i)
+ for (i = 0; ret == 0 && i < num; i++)
ret = axxia_i2c_xfer_msg(idev, &msgs[i]);
axxia_i2c_stop(idev);
- return ret ? : i;
+ return ret ?: i;
}
-static u32 axxia_i2c_func(struct i2c_adapter *adap)
+static u32
+axxia_i2c_func(struct i2c_adapter *adap)
{
- u32 caps = (I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA);
+ u32 caps = (I2C_FUNC_I2C |
+ I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_BLOCK_DATA);
return caps;
}
static const struct i2c_algorithm axxia_i2c_algo = {
- .master_xfer = axxia_i2c_xfer,
- .functionality = axxia_i2c_func,
+ .master_xfer = axxia_i2c_xfer,
+ .functionality = axxia_i2c_func,
};
-static int axxia_i2c_probe(struct platform_device *pdev)
+static int
+axxia_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct axxia_i2c_dev *idev = NULL;
struct resource *res;
void __iomem *base;
- int irq;
int ret = 0;
idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
@@ -472,82 +557,99 @@ static int axxia_i2c_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "missing interrupt resource\n");
- return irq;
- }
+ idev->irq = platform_get_irq(pdev, 0);
+ if (idev->irq < 0)
+ dev_info(&pdev->dev, "No IRQ specified, using polling mode\n");
idev->i2c_clk = devm_clk_get(&pdev->dev, "i2c");
if (IS_ERR(idev->i2c_clk)) {
- dev_err(&pdev->dev, "missing clock\n");
+ dev_err(&pdev->dev, "missing I2C bus clock");
return PTR_ERR(idev->i2c_clk);
}
- idev->base = base;
+ idev->regs = (struct i2c_regs __iomem *) base;
idev->dev = &pdev->dev;
init_completion(&idev->msg_complete);
of_property_read_u32(np, "clock-frequency", &idev->bus_clk_rate);
if (idev->bus_clk_rate == 0)
- idev->bus_clk_rate = 100000; /* default clock rate */
+ idev->bus_clk_rate = 100000; /* default clock rate */
ret = axxia_i2c_init(idev);
if (ret) {
- dev_err(&pdev->dev, "failed to initialize\n");
+ dev_err(&pdev->dev, "Failed to initialize i2c controller");
return ret;
}
- ret = devm_request_irq(&pdev->dev, irq, axxia_i2c_isr, 0,
- pdev->name, idev);
- if (ret) {
- dev_err(&pdev->dev, "failed to claim IRQ%d\n", irq);
- return ret;
+ if (idev->irq >= 0) {
+ ret = devm_request_irq(&pdev->dev, idev->irq, axxia_i2c_isr, 0,
+ pdev->name, idev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't claim irq %d\n", idev->irq);
+ return ret;
+ }
}
- clk_prepare_enable(idev->i2c_clk);
+ clk_enable(idev->i2c_clk);
i2c_set_adapdata(&idev->adapter, idev);
strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name));
idev->adapter.owner = THIS_MODULE;
+ idev->adapter.class = I2C_CLASS_HWMON;
idev->adapter.algo = &axxia_i2c_algo;
idev->adapter.dev.parent = &pdev->dev;
idev->adapter.dev.of_node = pdev->dev.of_node;
- platform_set_drvdata(pdev, idev);
-
ret = i2c_add_adapter(&idev->adapter);
if (ret) {
- dev_err(&pdev->dev, "failed to add adapter\n");
+ dev_err(&pdev->dev, "Failed to add I2C adapter\n");
return ret;
}
+ platform_set_drvdata(pdev, idev);
+
return 0;
}
-static int axxia_i2c_remove(struct platform_device *pdev)
+static int
+axxia_i2c_remove(struct platform_device *pdev)
{
struct axxia_i2c_dev *idev = platform_get_drvdata(pdev);
- clk_disable_unprepare(idev->i2c_clk);
i2c_del_adapter(&idev->adapter);
-
return 0;
}
+#ifdef CONFIG_PM
+static int axxia_i2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return -EOPNOTSUPP;
+}
+
+static int axxia_i2c_resume(struct platform_device *pdev)
+{
+ return -EOPNOTSUPP;
+}
+#else
+#define axxia_i2c_suspend NULL
+#define axxia_i2c_resume NULL
+#endif
+
/* Match table for of_platform binding */
static const struct of_device_id axxia_i2c_of_match[] = {
{ .compatible = "lsi,api2c", },
{},
};
-
MODULE_DEVICE_TABLE(of, axxia_i2c_of_match);
static struct platform_driver axxia_i2c_driver = {
- .probe = axxia_i2c_probe,
- .remove = axxia_i2c_remove,
- .driver = {
- .name = "axxia-i2c",
+ .probe = axxia_i2c_probe,
+ .remove = axxia_i2c_remove,
+ .suspend = axxia_i2c_suspend,
+ .resume = axxia_i2c_resume,
+ .driver = {
+ .name = "axxia-i2c",
+ .owner = THIS_MODULE,
.of_match_table = axxia_i2c_of_match,
},
};
diff --git a/include/linux/i2c-axxia.h b/include/linux/i2c-axxia.h
new file mode 100644
index 0000000..435cd33
--- /dev/null
+++ b/include/linux/i2c-axxia.h
@@ -0,0 +1,39 @@
+#ifndef __I2C_AXXIA_H__
+#define __I2C_AXXIA_H__
+
+#include <linux/platform_device.h>
+
+/*
+ * Version 2 of the I2C peripheral unit has a different register
+ * layout and extra registers. The ID register in the V2 peripheral
+ * unit on the AXXIA4430 reports the same ID as the V1 peripheral
+ * unit on the AXXIA3530, so we must inform the driver which IP
+ * version we know it is running on from platform / cpu-specific
+ * code using these constants in the hwmod class definition.
+ */
+
+#define AXXIA_I2C_IP_VERSION_1 1 /* ACP34xx */
+#define AXXIA_I2C_IP_VERSION_2 2 /* AXM55xx */
+
+/* struct axxia_i2c_bus_platform_data .flags meanings */
+#define AXXIA_I2C_FLAGS_NONE (0x00000000)
+
+
+/*
+ * Maximum byte size of I2C bus name string including null terminator
+ */
+#define MAX_AXXIA_I2C_HWMOD_NAME_LEN 16
+
+
+struct axxia_i2c_bus_platform_data {
+ struct device_node *node;
+ char name[MAX_AXXIA_I2C_HWMOD_NAME_LEN];
+ u32 index;
+ u32 rev;
+ u32 flags;
+ u32 bus_nr;
+ struct resource dev_space;
+ struct resource int_space;
+};
+
+#endif
--
1.7.9.5
More information about the linux-yocto
mailing list