[linux-yocto] [PATCH 08/42] drivers/i2c: Changes to support axxia BSP

Cristian Bercaru cristian.bercaru at windriver.com
Thu Jun 11 01:31:53 PDT 2015


From: Charlie Paul <cpaul.windriver at gmail.com>

Driver changes to support the AXXIA 55000 board.

Signed-off-by: Charlie Paul <cpaul.windriver at gmail.com>
---
 drivers/i2c/busses/Kconfig       |   16 +
 drivers/i2c/busses/Makefile      |    1 +
 drivers/i2c/busses/acp3400-i2c.c |  515 ++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-axxia.c   |  657 ++++++++++++++++++++++++++++++++++++++
 include/linux/i2c-axxia.h        |   39 +++
 5 files changed, 1228 insertions(+)
 create mode 100644 drivers/i2c/busses/acp3400-i2c.c
 create mode 100644 drivers/i2c/busses/i2c-axxia.c
 create mode 100644 include/linux/i2c-axxia.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bfacb9e..783b22e 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1013,4 +1013,20 @@ 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
+
+
 endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index a08931f..9711617 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC)	+= i2c-powermac.o
 # Embedded system I2C/SMBus host controller drivers
 obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
 obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
+obj-$(CONFIG_I2C_AXXIA)		+= i2c-axxia.o
 obj-$(CONFIG_I2C_BCM2835)	+= i2c-bcm2835.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CBUS_GPIO)	+= i2c-cbus-gpio.o
diff --git a/drivers/i2c/busses/acp3400-i2c.c b/drivers/i2c/busses/acp3400-i2c.c
new file mode 100644
index 0000000..b4febc0
--- /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
new file mode 100644
index 0000000..97b1b5a
--- /dev/null
+++ b/drivers/i2c/busses/i2c-axxia.c
@@ -0,0 +1,657 @@
+/*
+ * 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/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>
+
+#define SCL_WAIT_TIMEOUT_NS 25000000
+#define I2C_XFER_TIMEOUT    (msecs_to_jiffies(500))
+#define I2C_STOP_TIMEOUT    (msecs_to_jiffies(100))
+#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;
+};
+
+
+/**
+ * I2C device context
+ */
+struct axxia_i2c_dev {
+	/** 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)
+{
+	u32 int_mask = readl(&idev->regs->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)
+{
+	u32 int_mask = readl(&idev->regs->mst_int_enable);
+	int_mask |= mask;
+	writel(int_mask, &idev->regs->mst_int_enable);
+}
+
+/**
+ * Convert nanoseconds to clock cycles for the given clock frequency.
+ */
+static u32
+ns_to_clk(u64 ns, u32 clk_mhz)
+{
+	return div_u64(ns*clk_mhz, 1000);
+}
+
+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;
+	u32 t_setup;
+	u32 t_high, t_low;
+	u32 tmo_clk;
+	u32 prescale;
+	unsigned long timeout;
+
+	dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n",
+		idev->bus_clk_rate, clk_mhz, divisor);
+
+	/* Reset controller */
+	writel(0x01, &idev->regs->soft_reset);
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (readl(&idev->regs->soft_reset) & 1) {
+		if (time_after(jiffies, timeout)) {
+			dev_warn(idev->dev, "Soft reset failed\n");
+			break;
+		}
+	}
+
+	/* Enable Master Mode */
+	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_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_setup = ns_to_clk(100, clk_mhz);
+	}
+
+	/* SCL High Time */
+	writel(t_high, &idev->regs->scl_high_period);
+	/* SCL Low Time */
+	writel(t_low, &idev->regs->scl_low_period);
+	/* SDA Setup Time */
+	writel(t_setup, &idev->regs->sda_setup_time);
+	/* SDA Hold Time, 300ns */
+	writel(ns_to_clk(300, clk_mhz), &idev->regs->sda_hold_time);
+	/* Filter <50ns spikes */
+	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 the prescaler value that makes tmo_clk fit in 15-bits counter.
+	 */
+	for (prescale = 0; prescale < 15; ++prescale) {
+		if (tmo_clk <= 0x7fff)
+			break;
+		tmo_clk >>= 1;
+	}
+	if (tmo_clk > 0x7fff)
+		tmo_clk = 0x7fff;
+
+	/* Prescale divider (log2) */
+	writel(prescale, &idev->regs->timer_clock_div);
+	/* Timeout in divided clocks */
+	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->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)
+{
+	return (msg->flags & I2C_M_RD) != 0;
+}
+
+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)
+{
+	return (msg->flags & I2C_M_RECV_LEN) != 0;
+}
+
+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->regs->mst_rx_fifo);
+	int bytes_to_transfer = min(rx_fifo_avail, msg->len - idev->msg_xfrd);
+
+	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) {
+				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->regs->mst_rx_xfer);
+		}
+		msg->buf[idev->msg_xfrd++] = c;
+	}
+
+	return 0;
+}
+
+static int
+axxia_i2c_fill_tx_fifo(struct axxia_i2c_dev *idev)
+{
+	struct i2c_msg *msg = idev->msg;
+	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);
+
+	while (0 < bytes_to_transfer--)
+		writel(msg->buf[idev->msg_xfrd++], &idev->regs->mst_data);
+
+	return 0;
+}
+
+static char *
+status_str(u32 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;
+}
+
+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))
+		axxia_i2c_empty_rx_fifo(idev);
+
+	/* TX FIFO needs service? */
+	if (!i2c_m_rd(idev->msg) && (status & MST_STATUS_TFL)) {
+		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? */
+		i2c_int_disable(idev, ~0);
+		complete(&idev->msg_complete);
+	} 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? */
+		idev->msg_err = status & MST_STATUS_ERR;
+		i2c_int_disable(idev, ~0);
+		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);
+
+	/* Clear interrupt */
+	writel(0x01, &idev->regs->interrupt_status);
+
+	return IRQ_HANDLED;
+}
+
+
+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 addr_1, addr_2;
+	int ret;
+
+	if (msg->len == 0 || msg->len > 255)
+		return -EINVAL;
+
+	idev->msg      = msg;
+	idev->msg_xfrd = 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/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/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)) {
+		int_mask |= MST_STATUS_RFL;
+	} else {
+		axxia_i2c_fill_tx_fifo(idev);
+		if (idev->msg_xfrd < msg->len)
+			int_mask |= MST_STATUS_TFL;
+	}
+
+	/* Start manual mode */
+	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;
+
+		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) {
+		dev_warn(idev->dev, "xfer timeout (%#x)\n", msg->addr);
+		axxia_i2c_init(idev);
+		return -ETIMEDOUT;
+	}
+
+	if (unlikely(idev->msg_err != 0)) {
+		axxia_i2c_init(idev);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int
+axxia_i2c_stop(struct axxia_i2c_dev *idev)
+{
+	u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC;
+	int ret;
+
+	reinit_completion(&idev->msg_complete);
+
+	/* Issue stop */
+	writel(0xb, &idev->regs->mst_command);
+	i2c_int_enable(idev, int_mask);
+	ret = wait_for_completion_timeout(&idev->msg_complete,
+					  I2C_STOP_TIMEOUT);
+	i2c_int_disable(idev, int_mask);
+	if (ret == 0)
+		return -ETIMEDOUT;
+
+	WARN_ON(readl(&idev->regs->mst_command) & 0x8);
+
+	return 0;
+}
+
+static int
+axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct axxia_i2c_dev *idev = i2c_get_adapdata(adap);
+	int i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && i < num; i++)
+		ret = axxia_i2c_xfer_msg(idev, &msgs[i]);
+
+	axxia_i2c_stop(idev);
+
+	return ret ?: i;
+}
+
+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);
+	return caps;
+}
+
+static const struct i2c_algorithm axxia_i2c_algo = {
+	.master_xfer	= axxia_i2c_xfer,
+	.functionality	= axxia_i2c_func,
+};
+
+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 ret = 0;
+
+	idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
+	if (!idev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	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 I2C bus clock");
+		return PTR_ERR(idev->i2c_clk);
+	}
+
+	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 */
+
+	ret = axxia_i2c_init(idev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to initialize i2c controller");
+		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_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;
+
+	ret = i2c_add_adapter(&idev->adapter);
+	if (ret) {
+		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)
+{
+	struct axxia_i2c_dev *idev = platform_get_drvdata(pdev);
+	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,
+	.suspend = axxia_i2c_suspend,
+	.resume  = axxia_i2c_resume,
+	.driver  = {
+		.name  = "axxia-i2c",
+		.owner = THIS_MODULE,
+		.of_match_table = axxia_i2c_of_match,
+	},
+};
+
+module_platform_driver(axxia_i2c_driver);
+
+MODULE_DESCRIPTION("Axxia I2C Bus driver");
+MODULE_AUTHOR("Anders Berg <anders.berg at lsi.com>");
+MODULE_LICENSE("GPL v2");
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