[PATCH v2 2/2] i2c: xlr: add interrupt support for Sigma Designs chips

2015-12-15 Thread Mans Rullgard
The Sigma Designs variant of this controller has the ability to generate
interrupts.  This is controlled using two additional registers, oddly
enough overlapping with the defined but unused HDSTATIM.

This patch adds support for using this feature instead of busy-looping
if an IRQ is specified.

Signed-off-by: Mans Rullgard 
---
Changes:
- rebase onto v2 of previous patch
---
 drivers/i2c/busses/i2c-xlr.c | 119 +++
 1 file changed, 119 insertions(+)

diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 2cfaba7..613c3a4 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -19,6 +19,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 /* XLR I2C REGISTERS */
 #define XLR_I2C_CFG0x00
@@ -32,6 +34,10 @@
 #define XLR_I2C_BYTECNT0x08
 #define XLR_I2C_HDSTATIM   0x09
 
+/* Sigma Designs additional registers */
+#define XLR_I2C_INT_EN 0x09
+#define XLR_I2C_INT_STAT   0x0a
+
 /* XLR I2C REGISTERS FLAGS */
 #define XLR_I2C_BUS_BUSY   0x01
 #define XLR_I2C_SDOEMPTY   0x02
@@ -65,7 +71,10 @@ static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned 
int reg)
return __raw_readl(base + reg);
 }
 
+#define XLR_I2C_FLAG_IRQ   1
+
 struct xlr_i2c_config {
+   u32 flags;  /* optional feature support */
u32 status_busy;/* value of STATUS[0] when busy */
u32 cfg_extra;  /* extra CFG bits to set */
 };
@@ -73,7 +82,11 @@ struct xlr_i2c_config {
 struct xlr_i2c_private {
struct i2c_adapter adap;
u32 __iomem *iobase;
+   int irq;
+   int pos;
+   struct i2c_msg *msg;
const struct xlr_i2c_config *cfg;
+   wait_queue_head_t wait;
struct clk *clk;
 };
 
@@ -82,6 +95,74 @@ static int xlr_i2c_busy(struct xlr_i2c_private *priv, u32 
status)
return (status & XLR_I2C_BUS_BUSY) == priv->cfg->status_busy;
 }
 
+static int xlr_i2c_idle(struct xlr_i2c_private *priv)
+{
+   return !xlr_i2c_busy(priv, xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS));
+}
+
+static int xlr_i2c_wait(struct xlr_i2c_private *priv, unsigned long timeout)
+{
+   int status;
+   int t;
+
+   t = wait_event_timeout(priv->wait, xlr_i2c_idle(priv),
+   msecs_to_jiffies(timeout));
+   if (!t)
+   return -ETIMEDOUT;
+
+   status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+   return status & XLR_I2C_ACK_ERR ? -EIO : 0;
+}
+
+static void xlr_i2c_tx_irq(struct xlr_i2c_private *priv, u32 status)
+{
+   struct i2c_msg *msg = priv->msg;
+
+   if (status & XLR_I2C_SDOEMPTY)
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT,
+   msg->buf[priv->pos++]);
+}
+
+static void xlr_i2c_rx_irq(struct xlr_i2c_private *priv, u32 status)
+{
+   struct i2c_msg *msg = priv->msg;
+
+   if (status & XLR_I2C_RXRDY)
+   msg->buf[priv->pos++] =
+   xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
+}
+
+static irqreturn_t xlr_i2c_irq(int irq, void *dev_id)
+{
+   struct xlr_i2c_private *priv = dev_id;
+   struct i2c_msg *msg = priv->msg;
+   u32 int_stat, status;
+
+   int_stat = xlr_i2c_rdreg(priv->iobase, XLR_I2C_INT_STAT);
+   if (!int_stat)
+   return IRQ_NONE;
+
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, int_stat);
+
+   if (!msg)
+   return IRQ_HANDLED;
+
+   status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+   if (priv->pos < msg->len) {
+   if (msg->flags & I2C_M_RD)
+   xlr_i2c_rx_irq(priv, status);
+   else
+   xlr_i2c_tx_irq(priv, status);
+   }
+
+   if (!xlr_i2c_busy(priv, status))
+   wake_up(&priv->wait);
+
+   return IRQ_HANDLED;
+}
+
 static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 len,
u8 *buf, u16 addr)
 {
@@ -116,10 +197,15 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 
len,
pos = 2;
}
 
+   priv->pos = pos;
+
 retry:
/* retry can only happen on the first byte */
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
 
+   if (priv->irq > 0)
+   return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
+
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
@@ -163,6 +249,8 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 
len, u8 *buf, u16 addr)
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
 
+   priv->pos = 0;
+
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
@@ -170,6 +258,

[PATCH v2 1/2] i2c: xlr: fix extra read/write at end of rx transfer

2015-12-15 Thread Mans Rullgard
The BYTECNT register holds the transfer size minus one.  Setting it to
the correct value removes the need for a dummy read/write at the end of
each transfer.  As zero-length transfers are not supported, do not
advertise I2C_FUNC_SMBUS_QUICK.

In other words, this patch makes the driver transfer the number of bytes
requested unless this is zero, which is not supported by the hardware
and is thus refused.

Signed-off-by: Mans Rullgard 
---
Changes:
- refuse zero-length transfers and stop advertising I2C_FUNC_SMBUS_QUICK
---
 drivers/i2c/busses/i2c-xlr.c | 50 
 1 file changed, 27 insertions(+), 23 deletions(-)

diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 10fb916..2cfaba7 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -89,38 +89,43 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 
len,
unsigned long timeout, stoptime, checktime;
u32 i2c_status;
int pos, timedout;
-   u8 offset, byte;
+   u8 offset;
+   u32 xfer;
+
+   if (!len)
+   return -EOPNOTSUPP;
 
offset = buf[0];
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
 
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
-   pos = 1;
-retry:
+
if (len == 1) {
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
-   XLR_I2C_STARTXFR_ND);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
+   xfer = XLR_I2C_STARTXFR_ND;
+   pos = 1;
} else {
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]);
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
-   XLR_I2C_STARTXFR_WR);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
+   xfer = XLR_I2C_STARTXFR_WR;
+   pos = 2;
}
 
+retry:
+   /* retry can only happen on the first byte */
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
+
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
 
-   if (i2c_status & XLR_I2C_SDOEMPTY) {
-   pos++;
-   /* need to do a empty dataout after the last byte */
-   byte = (pos < len) ? buf[pos] : 0;
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
+   if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
 
/* reset timeout on successful xmit */
stoptime = jiffies + timeout;
@@ -149,11 +154,13 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 
len, u8 *buf, u16 addr)
u32 i2c_status;
unsigned long timeout, stoptime, checktime;
int nbytes, timedout;
-   u8 byte;
+
+   if (!len)
+   return -EOPNOTSUPP;
 
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
 
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
@@ -167,14 +174,11 @@ retry:
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
if (i2c_status & XLR_I2C_RXRDY) {
-   if (nbytes > len)
+   if (nbytes >= len)
return -EIO;/* should not happen */
 
-   /* we need to do a dummy datain when nbytes == len */
-   byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
-   if (nbytes < len)
-   buf[nbytes] = byte;
-   nbytes++;
+   buf[nbytes++] =
+   xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
 
/* reset timeout on successful read */
stoptime = jiffies + timeout;
@@ -228,7 +232,7 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
 static u32 xlr_func(struct i2c_adapter *adap)
 {
/* Emulate SMBUS over I2C */
-   return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+   return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC

[PATCH 3/3] i2c: xlr: add interrupt support for Sigma Designs chips

2015-11-01 Thread Mans Rullgard
The Sigma Designs variant of this controller has the ability to generate
interrupts.  This is controlled using two additional registers, oddly
enough overlapping with the defined but unused HDSTATIM.

This patch adds support for using this feature instead of busy-looping
if an IRQ is specified.

Signed-off-by: Mans Rullgard 
---
 drivers/i2c/busses/i2c-xlr.c | 119 +++
 1 file changed, 119 insertions(+)

diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index c1afc3e..dd48c1e 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -19,6 +19,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 /* XLR I2C REGISTERS */
 #define XLR_I2C_CFG0x00
@@ -32,6 +34,10 @@
 #define XLR_I2C_BYTECNT0x08
 #define XLR_I2C_HDSTATIM   0x09
 
+/* Sigma Designs additional registers */
+#define XLR_I2C_INT_EN 0x09
+#define XLR_I2C_INT_STAT   0x0a
+
 /* XLR I2C REGISTERS FLAGS */
 #define XLR_I2C_BUS_BUSY   0x01
 #define XLR_I2C_SDOEMPTY   0x02
@@ -65,7 +71,10 @@ static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned 
int reg)
return __raw_readl(base + reg);
 }
 
+#define XLR_I2C_FLAG_IRQ   1
+
 struct xlr_i2c_config {
+   u32 flags;  /* optional feature support */
u32 status_busy;/* value of STATUS[0] when busy */
u32 cfg_extra;  /* extra CFG bits to set */
 };
@@ -73,7 +82,11 @@ struct xlr_i2c_config {
 struct xlr_i2c_private {
struct i2c_adapter adap;
u32 __iomem *iobase;
+   int irq;
+   int pos;
+   struct i2c_msg *msg;
const struct xlr_i2c_config *cfg;
+   wait_queue_head_t wait;
struct clk *clk;
 };
 
@@ -82,6 +95,74 @@ static int xlr_i2c_busy(struct xlr_i2c_private *priv, u32 
status)
return (status & XLR_I2C_BUS_BUSY) == priv->cfg->status_busy;
 }
 
+static int xlr_i2c_idle(struct xlr_i2c_private *priv)
+{
+   return !xlr_i2c_busy(priv, xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS));
+}
+
+static int xlr_i2c_wait(struct xlr_i2c_private *priv, unsigned long timeout)
+{
+   int status;
+   int t;
+
+   t = wait_event_timeout(priv->wait, xlr_i2c_idle(priv),
+   msecs_to_jiffies(timeout));
+   if (!t)
+   return -ETIMEDOUT;
+
+   status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+   return status & XLR_I2C_ACK_ERR ? -EIO : 0;
+}
+
+static void xlr_i2c_tx_irq(struct xlr_i2c_private *priv, u32 status)
+{
+   struct i2c_msg *msg = priv->msg;
+
+   if (status & XLR_I2C_SDOEMPTY)
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT,
+   msg->buf[priv->pos++]);
+}
+
+static void xlr_i2c_rx_irq(struct xlr_i2c_private *priv, u32 status)
+{
+   struct i2c_msg *msg = priv->msg;
+
+   if (status & XLR_I2C_RXRDY)
+   msg->buf[priv->pos++] =
+   xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
+}
+
+static irqreturn_t xlr_i2c_irq(int irq, void *dev_id)
+{
+   struct xlr_i2c_private *priv = dev_id;
+   struct i2c_msg *msg = priv->msg;
+   u32 int_stat, status;
+
+   int_stat = xlr_i2c_rdreg(priv->iobase, XLR_I2C_INT_STAT);
+   if (!int_stat)
+   return IRQ_NONE;
+
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, int_stat);
+
+   if (!msg)
+   return IRQ_HANDLED;
+
+   status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+   if (priv->pos < msg->len) {
+   if (msg->flags & I2C_M_RD)
+   xlr_i2c_rx_irq(priv, status);
+   else
+   xlr_i2c_tx_irq(priv, status);
+   }
+
+   if (!xlr_i2c_busy(priv, status))
+   wake_up(&priv->wait);
+
+   return IRQ_HANDLED;
+}
+
 static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 len,
u8 *buf, u16 addr)
 {
@@ -119,10 +200,15 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 
len,
pos = 2;
}
 
+   priv->pos = pos;
+
 retry:
/* retry can only happen on the first byte */
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
 
+   if (priv->irq > 0)
+   return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
+
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
@@ -170,6 +256,8 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 
len, u8 *buf, u16 addr)
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
 
+   priv->pos = 0;
+
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
@@ -177,6 +265,9 @@ static int xlr_i2c_rx(struct xlr_i2c_private *p

[PATCH 1/3] i2c: xlr: add support for Sigma Designs controller variant

2015-11-01 Thread Mans Rullgard
Sigma Designs chips use a variant of this controller with the following
differences:

- The BUSY bit in the STATUS register is inverted
- Bit 8 of the CONFIG register must be set
- The controller can generate interrupts

This patch adds support for the first two of these.  It also calculates
and sets the correct clock divisor if a clk is provided.  The bus
frequency is optionally speficied in the device tree node.

Signed-off-by: Mans Rullgard 
---
 drivers/i2c/busses/Kconfig   |  6 ++--
 drivers/i2c/busses/i2c-xlr.c | 81 +---
 2 files changed, 80 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index f764e3a..fdedf59 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -944,11 +944,11 @@ config I2C_XILINX
  will be called xilinx_i2c.
 
 config I2C_XLR
-   tristate "XLR I2C support"
-   depends on CPU_XLR
+   tristate "Netlogic XLR and Sigma Designs I2C support"
+   depends on CPU_XLR || ARCH_TANGOX
help
  This driver enables support for the on-chip I2C interface of
- the Netlogic XLR/XLS MIPS processors.
+ the Netlogic XLR/XLS MIPS processors and Sigma Designs SOCs.
 
  This driver can also be built as a module.  If so, the module
  will be called i2c-xlr.
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 8b36bcf..10fb916 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -17,6 +17,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 /* XLR I2C REGISTERS */
 #define XLR_I2C_CFG0x00
@@ -63,11 +65,23 @@ static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned 
int reg)
return __raw_readl(base + reg);
 }
 
+struct xlr_i2c_config {
+   u32 status_busy;/* value of STATUS[0] when busy */
+   u32 cfg_extra;  /* extra CFG bits to set */
+};
+
 struct xlr_i2c_private {
struct i2c_adapter adap;
u32 __iomem *iobase;
+   const struct xlr_i2c_config *cfg;
+   struct clk *clk;
 };
 
+static int xlr_i2c_busy(struct xlr_i2c_private *priv, u32 status)
+{
+   return (status & XLR_I2C_BUS_BUSY) == priv->cfg->status_busy;
+}
+
 static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 len,
u8 *buf, u16 addr)
 {
@@ -80,7 +94,8 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 len,
offset = buf[0];
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_ADDR);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
+   XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
 
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
@@ -121,7 +136,7 @@ retry:
if (i2c_status & XLR_I2C_ACK_ERR)
return -EIO;
 
-   if ((i2c_status & XLR_I2C_BUS_BUSY) == 0 && pos >= len)
+   if (!xlr_i2c_busy(priv, i2c_status) && pos >= len)
return 0;
}
dev_err(&adap->dev, "I2C transmit timeout\n");
@@ -136,7 +151,8 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 
len, u8 *buf, u16 addr)
int nbytes, timedout;
u8 byte;
 
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
+   XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
 
@@ -174,7 +190,7 @@ retry:
if (i2c_status & XLR_I2C_ACK_ERR)
return -EIO;
 
-   if ((i2c_status & XLR_I2C_BUS_BUSY) == 0)
+   if (!xlr_i2c_busy(priv, i2c_status))
return 0;
}
 
@@ -190,6 +206,10 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
int ret = 0;
struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
 
+   ret = clk_enable(priv->clk);
+   if (ret)
+   return ret;
+
for (i = 0; ret == 0 && i < num; i++) {
msg = &msgs[i];
if (msg->flags & I2C_M_RD)
@@ -200,6 +220,8 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
msg->addr);
}
 
+   clk_disable(priv->clk);
+
return (ret != 0) ? ret : num;
 }
 
@@ -214,22 +236,70 @@ static struct i2c_algorithm xlr_i2c_algo = {
.functionality  = xlr_func,
 };
 
+static const struct xlr_i2c_config xlr_i2c_config_default = {
+   .status_busy= XLR_I2C_BUS_BUSY,
+   .cfg_extra  = 0,
+};
+
+static const struct xlr

[PATCH 2/3] i2c: xlr: fix extra read/write at end of rx transfer

2015-11-01 Thread Mans Rullgard
The BYTECNT register holds the transfer size minus one.  Setting it
to the correct value requires a dummy read/write only for zero-length
transfers as it is impossible to request one from the hardware.  If a
zero-length transfer is requested, changing the length to 1 and setting
"buf" to a dummy location allows making the main loops less convoluted.

In other words, this patch makes the driver transfer the number of bytes
requested unless this is zero, which is not supported by the hardware,
in which case one byte is transferred instead.

Signed-off-by: Mans Rullgard 
---
 drivers/i2c/busses/i2c-xlr.c | 51 +++-
 1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 10fb916..c1afc3e 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -90,37 +90,45 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv,  u16 
len,
u32 i2c_status;
int pos, timedout;
u8 offset, byte;
+   u32 xfer;
+
+   if (!len) {
+   byte = 0;
+   buf = &byte;
+   len = 1;
+   }
 
offset = buf[0];
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
 
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
-   pos = 1;
-retry:
+
if (len == 1) {
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
-   XLR_I2C_STARTXFR_ND);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
+   xfer = XLR_I2C_STARTXFR_ND;
+   pos = 1;
} else {
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]);
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
-   XLR_I2C_STARTXFR_WR);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
+   xfer = XLR_I2C_STARTXFR_WR;
+   pos = 2;
}
 
+retry:
+   /* retry can only happen on the first byte */
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
+
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
 
-   if (i2c_status & XLR_I2C_SDOEMPTY) {
-   pos++;
-   /* need to do a empty dataout after the last byte */
-   byte = (pos < len) ? buf[pos] : 0;
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
+   if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
 
/* reset timeout on successful xmit */
stoptime = jiffies + timeout;
@@ -151,9 +159,15 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 
len, u8 *buf, u16 addr)
int nbytes, timedout;
u8 byte;
 
+   if (!len) {
+   byte = 0;
+   buf = &byte;
+   len = 1;
+   }
+
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
-   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
+   xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
 
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
@@ -167,14 +181,11 @@ retry:
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
if (i2c_status & XLR_I2C_RXRDY) {
-   if (nbytes > len)
+   if (nbytes >= len)
return -EIO;/* should not happen */
 
-   /* we need to do a dummy datain when nbytes == len */
-   byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
-   if (nbytes < len)
-   buf[nbytes] = byte;
-   nbytes++;
+   buf[nbytes++] =
+   xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
 
/* reset timeout on successful read */
stoptime = jiffies + timeout;
-- 
2.6.2

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 1/2] devicetree: i2c: add binding for Sigma Designs SMP8642 I2C master

2015-10-31 Thread Mans Rullgard
This adds a binding for the Sigma Designs SMP8642 built-in I2C master
controller.

Signed-off-by: Mans Rullgard 
---
 .../devicetree/bindings/i2c/sigma,smp8642-i2c.txt  | 24 ++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/sigma,smp8642-i2c.txt

diff --git a/Documentation/devicetree/bindings/i2c/sigma,smp8642-i2c.txt 
b/Documentation/devicetree/bindings/i2c/sigma,smp8642-i2c.txt
new file mode 100644
index 000..117fc8b
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/sigma,smp8642-i2c.txt
@@ -0,0 +1,24 @@
+Sigma Designs SMP8642 I2C master
+
+Required properties:
+- compatible: should be "sigma,smp8642-i2c"
+- reg: physical address space of the device
+- interrupts: the interrupt of the device
+- clocks: phandle of the clock for the device
+- #address-cells: should be <1>
+- #size-cells: should be <0>
+
+Optional properties:
+- clock-frequency: frequency of bus clock in Hz, default 100kHz
+
+Example:
+
+i2c@10480 {
+   compatible = "sigma,smp8642-i2c";
+   reg = <0x10480 0x2c>;
+   interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
+   clocks = <&sys_clk>;
+   clock-frequency = <10>;
+   #address-cells = <1>;
+   #size-cells = <0>;
+};
-- 
2.6.2

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 2/2] i2c: add driver for Sigma Designs SMP8642 I2C master

2015-10-31 Thread Mans Rullgard
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 irqre