This adds a driver for the Sigma Designs SMP8642 built-in I2C master
controller. The hardware is very similar to the I2C controller in the
Ralink RT3050 chip with the addition of interrupt generation and an
inverted busy/idle status bit. There are typically two controllers with
a shared IRQ.
Signed-off-by: Mans Rullgard
---
drivers/i2c/busses/Kconfig | 8 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-tangox.c | 330
3 files changed, 339 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-tangox.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 08b8617..f764e3a 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -974,6 +974,14 @@ config I2C_RCAR
This driver can also be built as a module. If so, the module
will be called i2c-rcar.
+config I2C_TANGOX
+ tristate "SMP86xx I2C master support"
+ depends on ARCH_TANGOX
+ help
+ This driver supports the Sigma Designs SMP86xx built-in I2c master.
+
+ If built as a module, it will be called i2c-tangox.
+
comment "External I2C/SMBus adapter drivers"
config I2C_DIOLAN_U2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6df3b30..d7a9f94 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
+obj-$(CONFIG_I2C_TANGOX) += i2c-tangox.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-tangox.c b/drivers/i2c/busses/i2c-tangox.c
new file mode 100644
index 000..586b3cb
--- /dev/null
+++ b/drivers/i2c/busses/i2c-tangox.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2015 Mans Rullgard
+ *
+ * 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.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define TANGOX_I2C_CONFIG 0x00
+#define TANGOX_I2C_CLKDIV 0x04
+#define TANGOX_I2C_DEVADDR 0x08
+#define TANGOX_I2C_ADDR0x0c
+#define TANGOX_I2C_DATAOUT 0x10
+#define TANGOX_I2C_DATAIN 0x14
+#define TANGOX_I2C_STATUS 0x18
+#define TANGOX_I2C_STARTXFER 0x1c
+#define TANGOX_I2C_BYTECNT 0x20
+#define TANGOX_I2C_INT_EN 0x24
+#define TANGOX_I2C_INT_STAT0x28
+
+#define TANGOX_I2C_CFG_EN (1 << 8)
+#define TANGOX_I2C_CFG_ADDRLEN(x) ((x) << 5)
+#define TANGOX_I2C_CFG_DEVADLEN(x) ((x) << 2)
+#define TANGOX_I2C_CFG_ADDRDIS (1 << 1)
+#define TANGOX_I2C_CFG_DEVADDIS(1 << 0)
+
+#define TANGOX_I2C_STATUS_IDLE (1 << 0)
+#define TANGOX_I2C_STATUS_SDOEMPTY (1 << 1)
+#define TANGOX_I2C_STATUS_DATARDY (1 << 2)
+#define TANGOX_I2C_STATUS_ACKERR (1 << 3)
+#define TANGOX_I2C_STATUS_STARTERR (1 << 4)
+
+#define TANGOX_I2C_XFER_WR 0
+#define TANGOX_I2C_XFER_RD 1
+#define TANGOX_I2C_XFER_NODATA 2
+
+#define TANGOX_I2C_CFG_WR 0x1f8
+#define TANGOX_I2C_CFG_RD 0x1fa
+
+#define TANGOX_I2C_TIMEOUT(len)msecs_to_jiffies(10 * len)
+
+struct tangox_i2c {
+ struct i2c_adapter adap;
+ void __iomem *base;
+ struct i2c_msg *msg;
+ int pos;
+ wait_queue_head_t wait;
+ struct clk *clk;
+};
+
+static int tangox_i2c_idle(struct tangox_i2c *ti2c)
+{
+ return readl(ti2c->base + TANGOX_I2C_STATUS) & TANGOX_I2C_STATUS_IDLE;
+}
+
+static int tangox_i2c_wait(struct tangox_i2c *ti2c, unsigned long timeout)
+{
+ int status;
+ int t;
+
+ t = wait_event_timeout(ti2c->wait, tangox_i2c_idle(ti2c), timeout);
+ if (!t)
+ return -ETIMEDOUT;
+
+ status = readl(ti2c->base + TANGOX_I2C_STATUS);
+
+ return status & TANGOX_I2C_STATUS_ACKERR ? -EIO : 0;
+}
+
+static void tangox_i2c_tx_irq(struct tangox_i2c *ti2c, u32 status)
+{
+ struct i2c_msg *msg = ti2c->msg;
+
+ if (status & TANGOX_I2C_STATUS_SDOEMPTY)
+ writel(msg->buf[ti2c->pos++], ti2c->base + TANGOX_I2C_DATAOUT);
+}
+
+static void tangox_i2c_rx_irq(struct tangox_i2c *ti2c, u32 status)
+{
+ struct i2c_msg *msg = ti2c->msg;
+
+ if (status & TANGOX_I2C_STATUS_DATARDY)
+ msg->buf[ti2c->pos++] = readl(ti2c->base + TANGOX_I2C_DATAIN);
+}
+
+static irqreturn_t tangox_i2c_irq(int irq, void *dev_id)
+{
+ struct tangox_i2c *ti2c = dev_id;
+