* [PATCH] i2c: add Renesas R-Car I2C driver
@ 2012-07-25 6:36 Kuninori Morimoto
[not found] ` <871uk04aa2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
0 siblings, 1 reply; 21+ messages in thread
From: Kuninori Morimoto @ 2012-07-25 6:36 UTC (permalink / raw)
To: Wolfram Sang, Jean Delvare, Ben Dooks
Cc: Magnus, Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
R-Car I2C is similar with SH7760 I2C.
But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
This patch creates new Renesas R-Car I2C driver.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-rcar.c | 741 +++++++++++++++++++++++++++++++++++++++++
include/linux/i2c/i2c-rcar.h | 10 +
4 files changed, 762 insertions(+), 0 deletions(-)
create mode 100644 drivers/i2c/busses/i2c-rcar.c
create mode 100644 include/linux/i2c/i2c-rcar.h
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 7244c8b..3175c81 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -704,6 +704,16 @@ config I2C_XLR
This driver can also be built as a module. If so, the module
will be called i2c-xlr.
+config I2C_RCAR
+ tristate "Renesas R-Car I2C Controller"
+ depends on ARCH_SHMOBILE && I2C
+ help
+ If you say yes to this option, support will be included for the
+ R-Car I2C controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-rcar.
+
comment "External I2C/SMBus adapter drivers"
config I2C_DIOLAN_U2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ce3c2be..e98ff51 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
+obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
new file mode 100644
index 0000000..2993cab
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -0,0 +1,741 @@
+/*
+ * drivers/i2c/busses/i2c-rcar.c
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
+ *
+ * This file is based on the drivers/i2c/busses/i2c-sh7760.c
+ * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
+ *
+ * This file used out-of-tree driver i2c-rcar.c
+ * Copyright (C) 2011-2012 Renesas Electronics Corporation
+ *
+ * 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
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c/i2c-rcar.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* register offsets */
+#define ICSCR 0x00 /* slave ctrl */
+#define ICMCR 0x04 /* master ctrl */
+#define ICSSR 0x08 /* slave status */
+#define ICMSR 0x0C /* master status */
+#define ICSIER 0x10 /* slave irq enable */
+#define ICMIER 0x14 /* master irq enable */
+#define ICCCR 0x18 /* clock dividers */
+#define ICSAR 0x1C /* slave address */
+#define ICMAR 0x20 /* master address */
+#define ICRXTX 0x24 /* data port */
+
+/* ICMCR */
+#define MDBS (1 << 7) /* non-fifo mode switch */
+#define FSCL (1 << 6) /* override SCL pin */
+#define FSDA (1 << 5) /* override SDA pin */
+#define OBPC (1 << 4) /* override pins */
+#define MIE (1 << 3) /* master if enable */
+#define TSBE (1 << 2)
+#define FSB (1 << 1) /* force stop bit */
+#define ESG (1 << 0) /* en startbit gen */
+
+/* ICMSR */
+#define MNR (1 << 6) /* nack received */
+#define MAL (1 << 5) /* arbitration lost */
+#define MST (1 << 4) /* sent a stop */
+#define MDE (1 << 3)
+#define MDT (1 << 2)
+#define MDR (1 << 1)
+#define MAT (1 << 0) /* slave addr xfer done */
+
+/* ICMIE */
+#define MNRE (1 << 6) /* nack irq en */
+#define MALE (1 << 5) /* arblos irq en */
+#define MSTE (1 << 4) /* stop irq en */
+#define MDEE (1 << 3)
+#define MDTE (1 << 2)
+#define MDRE (1 << 1)
+#define MATE (1 << 0) /* address sent irq en */
+
+
+enum {
+ RCAR_BUS_PHASE_ADDR,
+ RCAR_BUS_PHASE_DATA,
+ RCAR_BUS_PHASE_STOP,
+};
+
+enum {
+ RCAR_IRQ_CLOSE,
+ RCAR_IRQ_OPEN_FOR_SEND,
+ RCAR_IRQ_OPEN_FOR_RECV,
+ RCAR_IRQ_OPEN_FOR_STOP,
+};
+
+/*
+ * flags
+ */
+#define ID_LAST_MSG (1 << 0)
+#define ID_IOERROR (1 << 1)
+#define ID_DONE (1 << 2)
+#define ID_ARBLOST (1 << 3)
+#define ID_NACK (1 << 4)
+
+struct rcar_i2c_priv {
+ void __iomem *io;
+ struct i2c_adapter adap;
+ struct i2c_msg *msg;
+
+ spinlock_t lock;
+ wait_queue_head_t wait;
+
+ int pos;
+ int irq;
+ u32 icccr;
+ u32 flags;
+};
+
+#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
+#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
+
+#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
+#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
+
+/*
+ * basic functions
+ */
+static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
+{
+ __raw_writel(val, priv->io + reg);
+}
+
+static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
+{
+ return __raw_readl(priv->io + reg);
+}
+
+#if 0 /* no user at this point */
+static u32 rcar_i2c_bset(struct rcar_i2c_priv *priv, int reg,
+ u32 mask, u32 data)
+{
+ u32 val = rcar_i2c_read(priv, reg);
+
+ val &= ~mask;
+ val |= (mask & data);
+
+ rcar_i2c_write(priv, reg, val);
+}
+#endif
+
+static void rcar_i2c_soft_reset(struct rcar_i2c_priv *priv)
+{
+ /*
+ * reset slave mode.
+ * slave mode is not used on this driver
+ */
+ rcar_i2c_write(priv, ICSIER, 0);
+ rcar_i2c_write(priv, ICSAR, 0);
+ rcar_i2c_write(priv, ICSCR, 0);
+ rcar_i2c_write(priv, ICSSR, 0);
+
+ /* reset master mode */
+ rcar_i2c_write(priv, ICMIER, 0);
+ rcar_i2c_write(priv, ICMCR, 0);
+ rcar_i2c_write(priv, ICMSR, 0);
+ rcar_i2c_write(priv, ICMAR, 0);
+}
+
+static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
+{
+ u32 val = MNRE | MALE | MSTE | MATE; /* default */
+
+ switch (open) {
+ case RCAR_IRQ_OPEN_FOR_SEND:
+ val |= MDEE; /* default + send */
+ break;
+ case RCAR_IRQ_OPEN_FOR_RECV:
+ val |= MDRE; /* default + read */
+ break;
+ case RCAR_IRQ_OPEN_FOR_STOP:
+ val = MSTE; /* stop irq only */
+ break;
+ case RCAR_IRQ_CLOSE:
+ default:
+ val = 0; /* all close */
+ break;
+ }
+ rcar_i2c_write(priv, ICMIER, val);
+}
+
+static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
+{
+ rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
+}
+
+/*
+ * bus control functions
+ */
+static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < 1024; i++) {
+ if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
+ return 0;
+ udelay(1);
+ }
+
+ return -EBUSY;
+}
+
+static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
+{
+ switch (phase) {
+ case RCAR_BUS_PHASE_ADDR:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
+ break;
+ case RCAR_BUS_PHASE_DATA:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE);
+ break;
+ case RCAR_BUS_PHASE_STOP:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
+ break;
+ }
+
+ return;
+}
+
+/*
+ * clock function
+ */
+static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
+ u32 bus_speed,
+ struct device *dev)
+{
+ struct clk *clkp = clk_get(NULL, "peripheral_clk");
+ u32 scgd, cdf;
+ u32 round, ick;
+
+ if (!clkp) {
+ dev_err(dev, "there is no peripheral_clk\n");
+ return -EIO;
+ }
+
+ /*
+ * use 95% bus speed for safety.
+ */
+ bus_speed = bus_speed * 95 / 100;
+
+ /*
+ * calculate SCL clock
+ * see
+ * ICCCR
+ *
+ * ick = clkp / (1 + CDF)
+ * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
+ *
+ * ick : I2C internal clock < 20 MHz
+ * ticf : I2C SCL falling time = 35 ns here
+ * tr : I2C SCL rising time = 200 ns here
+ * intd : LSI internal delay = 50 ns here
+ * clkp : peripheral_clk
+ * F[] : integer up-valuation
+ */
+ for (cdf = 0; cdf < 4; cdf++) {
+ ick = clk_get_rate(clkp) / (1 + cdf);
+ if (ick < 20000000)
+ goto ick_find;
+ }
+ dev_err(dev, "there is no best CDF\n");
+
+ return -EIO;
+
+ick_find:
+ /*
+ * it is impossible to calculate large scale
+ * number on u32
+ *
+ * F[(ticf + tr + intd) * ick]
+ * = F[(35 + 200 + 50)ns * ick]
+ * = F[285 * ick / 1000000000]
+ * = F[(ick / 1000000) * 285 / 1000]
+ */
+ round = (ick + 500000) / 1000000 * 285;
+ round = (round + 500) / 1000;
+
+ /*
+ * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
+ * -> (20 + SCGD * 8 + round) = ick / SCL
+ * -> SCGD * 8 = (ick / SCL) - 20 - round
+ * -> SCGD = F[((ick / SCL) - 20 - round) / 8]
+ * -> SCGD = ((ick / SCL) - 20 - round + 4) / 8
+ */
+ scgd = ((ick / bus_speed) - 20 - round + 4) / 8;
+
+ dev_dbg(dev, "clk %u(%lu), round %u, SCL %d, CDF:0x%x, SCGD: 0x%x\n",
+ ick / (20 + scgd * 8 + round),
+ clk_get_rate(clkp), round, bus_speed, cdf, scgd);
+
+ /*
+ * keep icccr value
+ */
+ priv->icccr = (scgd << 2 | cdf);
+
+ return 0;
+}
+
+static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
+{
+ rcar_i2c_write(priv, ICCCR, priv->icccr);
+}
+
+/*
+ * status functions
+ */
+static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
+{
+ return rcar_i2c_read(priv, ICMSR);
+}
+
+#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
+static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
+{
+ rcar_i2c_write(priv, ICMSR, ~bit);
+}
+
+/*
+ * recv/send functions
+ */
+static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
+{
+ rcar_i2c_set_addr(priv, 1);
+ rcar_i2c_status_clear(priv);
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
+
+ return 0;
+}
+
+static int rcar_i2c_send(struct rcar_i2c_priv *priv)
+{
+ int ret;
+
+ /*
+ * It should check bus status when send case
+ */
+ ret = rcar_i2c_bus_barrier(priv);
+ if (ret < 0)
+ return ret;
+
+ rcar_i2c_set_addr(priv, 0);
+ rcar_i2c_status_clear(priv);
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
+
+ return 0;
+}
+
+#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
+#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
+
+/*
+ * interrupt functions
+ */
+static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ /*
+ * FIXME
+ * sometimes, unknown interrupt happend.
+ * Do nothing
+ */
+ if (!(msr & MDE))
+ return 0;
+
+ /*
+ * If address transfer phase finised,
+ * goto data phase.
+ */
+ if (msr & MAT)
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
+
+ if (priv->pos < msg->len) {
+ /*
+ * Prepare next data to ICRXTX register.
+ * This data will go to _SHIFT_ register.
+ *
+ * *
+ * [ICRXTX] -> [SHIFT] -> [I2C bus]
+ */
+ rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
+ priv->pos++;
+
+ } else {
+ /*
+ * The last data was pushed to ICRXTX on _PREV_ empty irq.
+ * It is on _SHIFT_ register, and will sent to I2C bus.
+ *
+ * *
+ * [ICRXTX] -> [SHIFT] -> [I2C bus]
+ */
+
+ if (priv->flags & ID_LAST_MSG)
+ /*
+ * If current msg is the _LAST_ msg,
+ * prepare stop condition here.
+ * ID_DONE will be set on STOP irq.
+ */
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ else
+ /*
+ * If current msg is _NOT_ last msg,
+ * it doesn't call stop phase.
+ * thus, there is no STOP irq.
+ * return ID_DONE here.
+ */
+ return ID_DONE;
+ }
+
+ rcar_i2c_send_restart(priv);
+
+ return 0;
+}
+
+static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ /*
+ * FIXME
+ * sometimes, unknown interrupt happend.
+ * Do nothing
+ */
+ if (!(msr & MDR))
+ return 0;
+
+ if (msr & MAT) {
+ /*
+ * Address transfer phase finished,
+ * but, there is no data at this point.
+ * Do nothing.
+ */
+ } else if (priv->pos < msg->len) {
+ /*
+ * get received data
+ */
+ msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
+ priv->pos++;
+ }
+
+ /*
+ * If next received data is the last data,
+ * I2C driver should send NACK (or STOP).
+ * But this chip can't send NACK, thus, go to STOP phase.
+ * Otherwise, go to data phase.
+ */
+ if (priv->pos + 1 >= msg->len)
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ else
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
+
+ rcar_i2c_recv_restart(priv);
+
+ return 0;
+}
+
+static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
+{
+ struct rcar_i2c_priv *priv = ptr;
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ u32 msr;
+
+ /*-------------- spin lock -----------------*/
+ spin_lock(&priv->lock);
+
+ msr = rcar_i2c_status_get(priv);
+
+ /*
+ * Arbitration lost
+ */
+ if (msr & MAL) {
+ /*
+ * CAUTION
+ *
+ * When arbitration lost, device become _slave_ mode.
+ */
+ dev_dbg(dev, "Arbitration Lost\n");
+ rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
+ goto out;
+ }
+
+ /*
+ * Stop
+ */
+ if (msr & MST) {
+ dev_dbg(dev, "Stop\n");
+ rcar_i2c_flags_set(priv, ID_DONE);
+ goto out;
+ }
+
+ /*
+ * Nack
+ */
+ if (msr & MNR) {
+ dev_dbg(dev, "Nack\n");
+
+ /* go to stop phase */
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
+ rcar_i2c_flags_set(priv, ID_NACK);
+ goto out;
+ }
+
+ /*
+ * recv/send
+ */
+ if (rcar_i2c_is_recv(priv))
+ rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
+ else
+ rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
+
+out:
+ if (rcar_i2c_flags_has(priv, ID_DONE)) {
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
+ rcar_i2c_status_clear(priv);
+ wake_up(&priv->wait);
+ }
+
+ spin_unlock(&priv->lock);
+ /*-------------- spin unlock -----------------*/
+
+ return IRQ_HANDLED;
+}
+
+static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ unsigned long flags;
+ int i, ret, timeout;
+
+ /*================== enable i2c device ===================*/
+ pm_runtime_get_sync(dev);
+
+ /*-------------- spin lock -----------------*/
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rcar_i2c_soft_reset(priv);
+ rcar_i2c_clock_start(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ /*-------------- spin unlock -----------------*/
+
+ ret = -EINVAL;
+ for (i = 0; i < num; i++) {
+ /*-------------- spin lock -----------------*/
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* init each data */
+ priv->msg = &msgs[i];
+ priv->pos = 0;
+ priv->flags = 0;
+ if (priv->msg == &msgs[num - 1])
+ rcar_i2c_flags_set(priv, ID_LAST_MSG);
+
+ /* start send/recv */
+ if (rcar_i2c_is_recv(priv))
+ ret = rcar_i2c_recv(priv);
+ else
+ ret = rcar_i2c_send(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ /*-------------- spin unlock -----------------*/
+
+ if (ret < 0)
+ break;
+
+ /*
+ * wait result
+ */
+ timeout = wait_event_timeout(priv->wait,
+ rcar_i2c_flags_has(priv, ID_DONE),
+ 5 * HZ);
+ if (!timeout) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ /*
+ * error handling
+ */
+ if (rcar_i2c_flags_has(priv, ID_NACK)) {
+ ret = -EREMOTEIO;
+
+ if (msgs->flags & I2C_M_IGNORE_NAK)
+ ret = 0;
+ break;
+ }
+
+ if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
+ ret = -EIO;
+ break;
+ }
+
+ ret = i + 1; /* The number of transfer */
+ }
+
+ pm_runtime_put_sync(dev);
+ /*================== disable i2c device ===================*/
+
+ if (ret < 0)
+ dev_err(dev, "error %d : %x\n", ret, priv->flags);
+
+ return ret;
+}
+
+static u32 rcar_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm rcar_i2c_algo = {
+ .master_xfer = rcar_i2c_master_xfer,
+ .functionality = rcar_i2c_func,
+};
+
+static int __devinit rcar_i2c_probe(struct platform_device *pdev)
+{
+ struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
+ struct rcar_i2c_priv *priv;
+ struct i2c_adapter *adap;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ u32 bus_speed;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no mmio resources\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(dev, "no mem for private data\n");
+ return -ENOMEM;
+ }
+
+ bus_speed = 100000; /* default 100 kHz */
+ if (pdata && pdata->bus_speed)
+ bus_speed = pdata->bus_speed;
+ ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
+ if (ret < 0)
+ goto error_free;
+
+ priv->io = devm_ioremap(dev, res->start, resource_size(res));
+ if (!priv->io) {
+ dev_err(dev, "cannot ioremap\n");
+ ret = -ENODEV;
+ goto error_free;
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ init_waitqueue_head(&priv->wait);
+ spin_lock_init(&priv->lock);
+
+ adap = &priv->adap;
+ adap->nr = pdev->id;
+ adap->algo = &rcar_i2c_algo;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->retries = 3;
+ adap->dev.parent = dev;
+ i2c_set_adapdata(adap, priv);
+ strlcpy(adap->name, pdev->name, sizeof(adap->name));
+
+ if (request_irq(priv->irq, rcar_i2c_irq, 0,
+ dev_name(dev), priv)) {
+ dev_err(dev, "cannot get irq %d\n", priv->irq);
+ ret = -EBUSY;
+ goto error_unmap;
+ }
+
+ ret = i2c_add_numbered_adapter(adap);
+ if (ret < 0) {
+ dev_err(dev, "reg adap failed: %d\n", ret);
+ goto error_irq;
+ }
+
+ pm_runtime_enable(dev);
+ platform_set_drvdata(pdev, priv);
+
+ dev_info(dev, "probed\n");
+
+ return 0;
+
+error_irq:
+ free_irq(priv->irq, priv);
+error_unmap:
+ devm_iounmap(dev, priv->io);
+error_free:
+ devm_kfree(dev, priv);
+
+ return ret;
+}
+
+static int __devexit rcar_i2c_remove(struct platform_device *pdev)
+{
+ struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ i2c_del_adapter(&priv->adap);
+ free_irq(priv->irq, priv);
+ devm_iounmap(dev, priv->io);
+ devm_kfree(dev, priv);
+ platform_set_drvdata(pdev, NULL);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static struct platform_driver rcar_i2c_drv = {
+ .driver = {
+ .name = "i2c-rcar",
+ .owner = THIS_MODULE,
+ },
+ .probe = rcar_i2c_probe,
+ .remove = __devexit_p(rcar_i2c_remove),
+};
+
+module_platform_driver(rcar_i2c_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
new file mode 100644
index 0000000..496f5c2
--- /dev/null
+++ b/include/linux/i2c/i2c-rcar.h
@@ -0,0 +1,10 @@
+#ifndef __I2C_R_CAR_H__
+#define __I2C_R_CAR_H__
+
+#include <linux/platform_device.h>
+
+struct i2c_rcar_platform_data {
+ u32 bus_speed;
+};
+
+#endif /* __I2C_R_CAR_H__ */
--
1.7.5.4
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <871uk04aa2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
@ 2012-08-28 0:19 ` Kuninori Morimoto
2012-08-28 4:48 ` Shubhrajyoti Datta
2012-08-28 6:26 ` [PATCH] i2c: add Renesas R-Car I2C driver phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
2 siblings, 0 replies; 21+ messages in thread
From: Kuninori Morimoto @ 2012-08-28 0:19 UTC (permalink / raw)
To: Wolfram Sang, Jean Delvare, Ben Dooks
Cc: Magnus, Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Hi
Does this patch go to next linux kernel (v3.7) ?
Or was rejected ?
At Tue, 24 Jul 2012 23:36:41 -0700 (PDT),
【RSO】森本 wrote:
>
> R-Car I2C is similar with SH7760 I2C.
> But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> This patch creates new Renesas R-Car I2C driver.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> ---
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-rcar.c | 741 +++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c/i2c-rcar.h | 10 +
> 4 files changed, 762 insertions(+), 0 deletions(-)
> create mode 100644 drivers/i2c/busses/i2c-rcar.c
> create mode 100644 include/linux/i2c/i2c-rcar.h
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 7244c8b..3175c81 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -704,6 +704,16 @@ config I2C_XLR
> This driver can also be built as a module. If so, the module
> will be called i2c-xlr.
>
> +config I2C_RCAR
> + tristate "Renesas R-Car I2C Controller"
> + depends on ARCH_SHMOBILE && I2C
> + help
> + If you say yes to this option, support will be included for the
> + R-Car I2C controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-rcar.
> +
> comment "External I2C/SMBus adapter drivers"
>
> config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index ce3c2be..e98ff51 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> new file mode 100644
> index 0000000..2993cab
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-rcar.c
> @@ -0,0 +1,741 @@
> +/*
> + * drivers/i2c/busses/i2c-rcar.c
> + *
> + * Copyright (C) 2012 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
> + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
> + *
> + * This file used out-of-tree driver i2c-rcar.c
> + * Copyright (C) 2011-2012 Renesas Electronics Corporation
> + *
> + * 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
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c/i2c-rcar.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +/* register offsets */
> +#define ICSCR 0x00 /* slave ctrl */
> +#define ICMCR 0x04 /* master ctrl */
> +#define ICSSR 0x08 /* slave status */
> +#define ICMSR 0x0C /* master status */
> +#define ICSIER 0x10 /* slave irq enable */
> +#define ICMIER 0x14 /* master irq enable */
> +#define ICCCR 0x18 /* clock dividers */
> +#define ICSAR 0x1C /* slave address */
> +#define ICMAR 0x20 /* master address */
> +#define ICRXTX 0x24 /* data port */
> +
> +/* ICMCR */
> +#define MDBS (1 << 7) /* non-fifo mode switch */
> +#define FSCL (1 << 6) /* override SCL pin */
> +#define FSDA (1 << 5) /* override SDA pin */
> +#define OBPC (1 << 4) /* override pins */
> +#define MIE (1 << 3) /* master if enable */
> +#define TSBE (1 << 2)
> +#define FSB (1 << 1) /* force stop bit */
> +#define ESG (1 << 0) /* en startbit gen */
> +
> +/* ICMSR */
> +#define MNR (1 << 6) /* nack received */
> +#define MAL (1 << 5) /* arbitration lost */
> +#define MST (1 << 4) /* sent a stop */
> +#define MDE (1 << 3)
> +#define MDT (1 << 2)
> +#define MDR (1 << 1)
> +#define MAT (1 << 0) /* slave addr xfer done */
> +
> +/* ICMIE */
> +#define MNRE (1 << 6) /* nack irq en */
> +#define MALE (1 << 5) /* arblos irq en */
> +#define MSTE (1 << 4) /* stop irq en */
> +#define MDEE (1 << 3)
> +#define MDTE (1 << 2)
> +#define MDRE (1 << 1)
> +#define MATE (1 << 0) /* address sent irq en */
> +
> +
> +enum {
> + RCAR_BUS_PHASE_ADDR,
> + RCAR_BUS_PHASE_DATA,
> + RCAR_BUS_PHASE_STOP,
> +};
> +
> +enum {
> + RCAR_IRQ_CLOSE,
> + RCAR_IRQ_OPEN_FOR_SEND,
> + RCAR_IRQ_OPEN_FOR_RECV,
> + RCAR_IRQ_OPEN_FOR_STOP,
> +};
> +
> +/*
> + * flags
> + */
> +#define ID_LAST_MSG (1 << 0)
> +#define ID_IOERROR (1 << 1)
> +#define ID_DONE (1 << 2)
> +#define ID_ARBLOST (1 << 3)
> +#define ID_NACK (1 << 4)
> +
> +struct rcar_i2c_priv {
> + void __iomem *io;
> + struct i2c_adapter adap;
> + struct i2c_msg *msg;
> +
> + spinlock_t lock;
> + wait_queue_head_t wait;
> +
> + int pos;
> + int irq;
> + u32 icccr;
> + u32 flags;
> +};
> +
> +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
> +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
> +
> +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
> +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
> +
> +/*
> + * basic functions
> + */
> +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> +{
> + __raw_writel(val, priv->io + reg);
> +}
> +
> +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> +{
> + return __raw_readl(priv->io + reg);
> +}
> +
> +#if 0 /* no user at this point */
> +static u32 rcar_i2c_bset(struct rcar_i2c_priv *priv, int reg,
> + u32 mask, u32 data)
> +{
> + u32 val = rcar_i2c_read(priv, reg);
> +
> + val &= ~mask;
> + val |= (mask & data);
> +
> + rcar_i2c_write(priv, reg, val);
> +}
> +#endif
> +
> +static void rcar_i2c_soft_reset(struct rcar_i2c_priv *priv)
> +{
> + /*
> + * reset slave mode.
> + * slave mode is not used on this driver
> + */
> + rcar_i2c_write(priv, ICSIER, 0);
> + rcar_i2c_write(priv, ICSAR, 0);
> + rcar_i2c_write(priv, ICSCR, 0);
> + rcar_i2c_write(priv, ICSSR, 0);
> +
> + /* reset master mode */
> + rcar_i2c_write(priv, ICMIER, 0);
> + rcar_i2c_write(priv, ICMCR, 0);
> + rcar_i2c_write(priv, ICMSR, 0);
> + rcar_i2c_write(priv, ICMAR, 0);
> +}
> +
> +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
> +{
> + u32 val = MNRE | MALE | MSTE | MATE; /* default */
> +
> + switch (open) {
> + case RCAR_IRQ_OPEN_FOR_SEND:
> + val |= MDEE; /* default + send */
> + break;
> + case RCAR_IRQ_OPEN_FOR_RECV:
> + val |= MDRE; /* default + read */
> + break;
> + case RCAR_IRQ_OPEN_FOR_STOP:
> + val = MSTE; /* stop irq only */
> + break;
> + case RCAR_IRQ_CLOSE:
> + default:
> + val = 0; /* all close */
> + break;
> + }
> + rcar_i2c_write(priv, ICMIER, val);
> +}
> +
> +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
> +{
> + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
> +}
> +
> +/*
> + * bus control functions
> + */
> +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
> +{
> + int i;
> +
> + for (i = 0; i < 1024; i++) {
> + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
> + return 0;
> + udelay(1);
> + }
> +
> + return -EBUSY;
> +}
> +
> +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> +{
> + switch (phase) {
> + case RCAR_BUS_PHASE_ADDR:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> + break;
> + case RCAR_BUS_PHASE_DATA:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> + break;
> + case RCAR_BUS_PHASE_STOP:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> + break;
> + }
> +
> + return;
> +}
> +
> +/*
> + * clock function
> + */
> +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> + u32 bus_speed,
> + struct device *dev)
> +{
> + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> + u32 scgd, cdf;
> + u32 round, ick;
> +
> + if (!clkp) {
> + dev_err(dev, "there is no peripheral_clk\n");
> + return -EIO;
> + }
> +
> + /*
> + * use 95% bus speed for safety.
> + */
> + bus_speed = bus_speed * 95 / 100;
> +
> + /*
> + * calculate SCL clock
> + * see
> + * ICCCR
> + *
> + * ick = clkp / (1 + CDF)
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + *
> + * ick : I2C internal clock < 20 MHz
> + * ticf : I2C SCL falling time = 35 ns here
> + * tr : I2C SCL rising time = 200 ns here
> + * intd : LSI internal delay = 50 ns here
> + * clkp : peripheral_clk
> + * F[] : integer up-valuation
> + */
> + for (cdf = 0; cdf < 4; cdf++) {
> + ick = clk_get_rate(clkp) / (1 + cdf);
> + if (ick < 20000000)
> + goto ick_find;
> + }
> + dev_err(dev, "there is no best CDF\n");
> +
> + return -EIO;
> +
> +ick_find:
> + /*
> + * it is impossible to calculate large scale
> + * number on u32
> + *
> + * F[(ticf + tr + intd) * ick]
> + * = F[(35 + 200 + 50)ns * ick]
> + * = F[285 * ick / 1000000000]
> + * = F[(ick / 1000000) * 285 / 1000]
> + */
> + round = (ick + 500000) / 1000000 * 285;
> + round = (round + 500) / 1000;
> +
> + /*
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + * -> (20 + SCGD * 8 + round) = ick / SCL
> + * -> SCGD * 8 = (ick / SCL) - 20 - round
> + * -> SCGD = F[((ick / SCL) - 20 - round) / 8]
> + * -> SCGD = ((ick / SCL) - 20 - round + 4) / 8
> + */
> + scgd = ((ick / bus_speed) - 20 - round + 4) / 8;
> +
> + dev_dbg(dev, "clk %u(%lu), round %u, SCL %d, CDF:0x%x, SCGD: 0x%x\n",
> + ick / (20 + scgd * 8 + round),
> + clk_get_rate(clkp), round, bus_speed, cdf, scgd);
> +
> + /*
> + * keep icccr value
> + */
> + priv->icccr = (scgd << 2 | cdf);
> +
> + return 0;
> +}
> +
> +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_write(priv, ICCCR, priv->icccr);
> +}
> +
> +/*
> + * status functions
> + */
> +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
> +{
> + return rcar_i2c_read(priv, ICMSR);
> +}
> +
> +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
> +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
> +{
> + rcar_i2c_write(priv, ICMSR, ~bit);
> +}
> +
> +/*
> + * recv/send functions
> + */
> +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_set_addr(priv, 1);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
> +{
> + int ret;
> +
> + /*
> + * It should check bus status when send case
> + */
> + ret = rcar_i2c_bus_barrier(priv);
> + if (ret < 0)
> + return ret;
> +
> + rcar_i2c_set_addr(priv, 0);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
> +
> + return 0;
> +}
> +
> +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
> +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
> +
> +/*
> + * interrupt functions
> + */
> +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happend.
> + * Do nothing
> + */
> + if (!(msr & MDE))
> + return 0;
> +
> + /*
> + * If address transfer phase finised,
> + * goto data phase.
> + */
> + if (msr & MAT)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + if (priv->pos < msg->len) {
> + /*
> + * Prepare next data to ICRXTX register.
> + * This data will go to _SHIFT_ register.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
> + priv->pos++;
> +
> + } else {
> + /*
> + * The last data was pushed to ICRXTX on _PREV_ empty irq.
> + * It is on _SHIFT_ register, and will sent to I2C bus.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> +
> + if (priv->flags & ID_LAST_MSG)
> + /*
> + * If current msg is the _LAST_ msg,
> + * prepare stop condition here.
> + * ID_DONE will be set on STOP irq.
> + */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + /*
> + * If current msg is _NOT_ last msg,
> + * it doesn't call stop phase.
> + * thus, there is no STOP irq.
> + * return ID_DONE here.
> + */
> + return ID_DONE;
> + }
> +
> + rcar_i2c_send_restart(priv);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happend.
> + * Do nothing
> + */
> + if (!(msr & MDR))
> + return 0;
> +
> + if (msr & MAT) {
> + /*
> + * Address transfer phase finished,
> + * but, there is no data at this point.
> + * Do nothing.
> + */
> + } else if (priv->pos < msg->len) {
> + /*
> + * get received data
> + */
> + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
> + priv->pos++;
> + }
> +
> + /*
> + * If next received data is the last data,
> + * I2C driver should send NACK (or STOP).
> + * But this chip can't send NACK, thus, go to STOP phase.
> + * Otherwise, go to data phase.
> + */
> + if (priv->pos + 1 >= msg->len)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + rcar_i2c_recv_restart(priv);
> +
> + return 0;
> +}
> +
> +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
> +{
> + struct rcar_i2c_priv *priv = ptr;
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + u32 msr;
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock(&priv->lock);
> +
> + msr = rcar_i2c_status_get(priv);
> +
> + /*
> + * Arbitration lost
> + */
> + if (msr & MAL) {
> + /*
> + * CAUTION
> + *
> + * When arbitration lost, device become _slave_ mode.
> + */
> + dev_dbg(dev, "Arbitration Lost\n");
> + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
> + goto out;
> + }
> +
> + /*
> + * Stop
> + */
> + if (msr & MST) {
> + dev_dbg(dev, "Stop\n");
> + rcar_i2c_flags_set(priv, ID_DONE);
> + goto out;
> + }
> +
> + /*
> + * Nack
> + */
> + if (msr & MNR) {
> + dev_dbg(dev, "Nack\n");
> +
> + /* go to stop phase */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
> + rcar_i2c_flags_set(priv, ID_NACK);
> + goto out;
> + }
> +
> + /*
> + * recv/send
> + */
> + if (rcar_i2c_is_recv(priv))
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
> + else
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
> +
> +out:
> + if (rcar_i2c_flags_has(priv, ID_DONE)) {
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
> + rcar_i2c_status_clear(priv);
> + wake_up(&priv->wait);
> + }
> +
> + spin_unlock(&priv->lock);
> + /*-------------- spin unlock -----------------*/
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs,
> + int num)
> +{
> + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + unsigned long flags;
> + int i, ret, timeout;
> +
> + /*================== enable i2c device ===================*/
> + pm_runtime_get_sync(dev);
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + rcar_i2c_soft_reset(priv);
> + rcar_i2c_clock_start(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + ret = -EINVAL;
> + for (i = 0; i < num; i++) {
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + /* init each data */
> + priv->msg = &msgs[i];
> + priv->pos = 0;
> + priv->flags = 0;
> + if (priv->msg == &msgs[num - 1])
> + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> +
> + /* start send/recv */
> + if (rcar_i2c_is_recv(priv))
> + ret = rcar_i2c_recv(priv);
> + else
> + ret = rcar_i2c_send(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + if (ret < 0)
> + break;
> +
> + /*
> + * wait result
> + */
> + timeout = wait_event_timeout(priv->wait,
> + rcar_i2c_flags_has(priv, ID_DONE),
> + 5 * HZ);
> + if (!timeout) {
> + ret = -ETIMEDOUT;
> + break;
> + }
> +
> + /*
> + * error handling
> + */
> + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> + ret = -EREMOTEIO;
> +
> + if (msgs->flags & I2C_M_IGNORE_NAK)
> + ret = 0;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> + ret = -EAGAIN;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> + ret = -EIO;
> + break;
> + }
> +
> + ret = i + 1; /* The number of transfer */
> + }
> +
> + pm_runtime_put_sync(dev);
> + /*================== disable i2c device ===================*/
> +
> + if (ret < 0)
> + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> +
> + return ret;
> +}
> +
> +static u32 rcar_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm rcar_i2c_algo = {
> + .master_xfer = rcar_i2c_master_xfer,
> + .functionality = rcar_i2c_func,
> +};
> +
> +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> +{
> + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> + struct rcar_i2c_priv *priv;
> + struct i2c_adapter *adap;
> + struct resource *res;
> + struct device *dev = &pdev->dev;
> + u32 bus_speed;
> + int ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "no mmio resources\n");
> + return -ENODEV;
> + }
> +
> + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(dev, "no mem for private data\n");
> + return -ENOMEM;
> + }
> +
> + bus_speed = 100000; /* default 100 kHz */
> + if (pdata && pdata->bus_speed)
> + bus_speed = pdata->bus_speed;
> + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> + if (ret < 0)
> + goto error_free;
> +
> + priv->io = devm_ioremap(dev, res->start, resource_size(res));
> + if (!priv->io) {
> + dev_err(dev, "cannot ioremap\n");
> + ret = -ENODEV;
> + goto error_free;
> + }
> +
> + priv->irq = platform_get_irq(pdev, 0);
> + init_waitqueue_head(&priv->wait);
> + spin_lock_init(&priv->lock);
> +
> + adap = &priv->adap;
> + adap->nr = pdev->id;
> + adap->algo = &rcar_i2c_algo;
> + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> + adap->retries = 3;
> + adap->dev.parent = dev;
> + i2c_set_adapdata(adap, priv);
> + strlcpy(adap->name, pdev->name, sizeof(adap->name));
> +
> + if (request_irq(priv->irq, rcar_i2c_irq, 0,
> + dev_name(dev), priv)) {
> + dev_err(dev, "cannot get irq %d\n", priv->irq);
> + ret = -EBUSY;
> + goto error_unmap;
> + }
> +
> + ret = i2c_add_numbered_adapter(adap);
> + if (ret < 0) {
> + dev_err(dev, "reg adap failed: %d\n", ret);
> + goto error_irq;
> + }
> +
> + pm_runtime_enable(dev);
> + platform_set_drvdata(pdev, priv);
> +
> + dev_info(dev, "probed\n");
> +
> + return 0;
> +
> +error_irq:
> + free_irq(priv->irq, priv);
> +error_unmap:
> + devm_iounmap(dev, priv->io);
> +error_free:
> + devm_kfree(dev, priv);
> +
> + return ret;
> +}
> +
> +static int __devexit rcar_i2c_remove(struct platform_device *pdev)
> +{
> + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> +
> + i2c_del_adapter(&priv->adap);
> + free_irq(priv->irq, priv);
> + devm_iounmap(dev, priv->io);
> + devm_kfree(dev, priv);
> + platform_set_drvdata(pdev, NULL);
> + pm_runtime_disable(dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver rcar_i2c_drv = {
> + .driver = {
> + .name = "i2c-rcar",
> + .owner = THIS_MODULE,
> + },
> + .probe = rcar_i2c_probe,
> + .remove = __devexit_p(rcar_i2c_remove),
> +};
> +
> +module_platform_driver(rcar_i2c_drv);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
> +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
> diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
> new file mode 100644
> index 0000000..496f5c2
> --- /dev/null
> +++ b/include/linux/i2c/i2c-rcar.h
> @@ -0,0 +1,10 @@
> +#ifndef __I2C_R_CAR_H__
> +#define __I2C_R_CAR_H__
> +
> +#include <linux/platform_device.h>
> +
> +struct i2c_rcar_platform_data {
> + u32 bus_speed;
> +};
> +
> +#endif /* __I2C_R_CAR_H__ */
> --
> 1.7.5.4
>
Best regards
---
Kuninori Morimoto
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <871uk04aa2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-08-28 0:19 ` Kuninori Morimoto
@ 2012-08-28 4:48 ` Shubhrajyoti Datta
[not found] ` <CAM=Q2ctETiVdyKBd_MxY2Ts=1XMPa_cdEJRkQpEySL6du9oLmw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-08-28 6:26 ` [PATCH] i2c: add Renesas R-Car I2C driver phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
2 siblings, 1 reply; 21+ messages in thread
From: Shubhrajyoti Datta @ 2012-08-28 4:48 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Wolfram Sang, Jean Delvare, Ben Dooks, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
On Wed, Jul 25, 2012 at 12:06 PM, Kuninori Morimoto
<kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
> R-Car I2C is similar with SH7760 I2C.
> But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> This patch creates new Renesas R-Car I2C driver.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> ---
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-rcar.c | 741 +++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c/i2c-rcar.h | 10 +
> 4 files changed, 762 insertions(+), 0 deletions(-)
> create mode 100644 drivers/i2c/busses/i2c-rcar.c
> create mode 100644 include/linux/i2c/i2c-rcar.h
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 7244c8b..3175c81 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -704,6 +704,16 @@ config I2C_XLR
> This driver can also be built as a module. If so, the module
> will be called i2c-xlr.
>
> +config I2C_RCAR
> + tristate "Renesas R-Car I2C Controller"
> + depends on ARCH_SHMOBILE && I2C
> + help
> + If you say yes to this option, support will be included for the
> + R-Car I2C controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-rcar.
> +
> comment "External I2C/SMBus adapter drivers"
>
> config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index ce3c2be..e98ff51 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> new file mode 100644
> index 0000000..2993cab
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-rcar.c
> @@ -0,0 +1,741 @@
> +/*
> + * drivers/i2c/busses/i2c-rcar.c
> + *
> + * Copyright (C) 2012 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
> + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
> + *
> + * This file used out-of-tree driver i2c-rcar.c
> + * Copyright (C) 2011-2012 Renesas Electronics Corporation
> + *
> + * 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
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c/i2c-rcar.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +/* register offsets */
> +#define ICSCR 0x00 /* slave ctrl */
> +#define ICMCR 0x04 /* master ctrl */
> +#define ICSSR 0x08 /* slave status */
> +#define ICMSR 0x0C /* master status */
> +#define ICSIER 0x10 /* slave irq enable */
> +#define ICMIER 0x14 /* master irq enable */
> +#define ICCCR 0x18 /* clock dividers */
> +#define ICSAR 0x1C /* slave address */
> +#define ICMAR 0x20 /* master address */
> +#define ICRXTX 0x24 /* data port */
> +
> +/* ICMCR */
> +#define MDBS (1 << 7) /* non-fifo mode switch */
> +#define FSCL (1 << 6) /* override SCL pin */
> +#define FSDA (1 << 5) /* override SDA pin */
> +#define OBPC (1 << 4) /* override pins */
> +#define MIE (1 << 3) /* master if enable */
> +#define TSBE (1 << 2)
> +#define FSB (1 << 1) /* force stop bit */
> +#define ESG (1 << 0) /* en startbit gen */
> +
> +/* ICMSR */
> +#define MNR (1 << 6) /* nack received */
> +#define MAL (1 << 5) /* arbitration lost */
> +#define MST (1 << 4) /* sent a stop */
> +#define MDE (1 << 3)
> +#define MDT (1 << 2)
> +#define MDR (1 << 1)
> +#define MAT (1 << 0) /* slave addr xfer done */
> +
> +/* ICMIE */
> +#define MNRE (1 << 6) /* nack irq en */
> +#define MALE (1 << 5) /* arblos irq en */
> +#define MSTE (1 << 4) /* stop irq en */
> +#define MDEE (1 << 3)
> +#define MDTE (1 << 2)
> +#define MDRE (1 << 1)
> +#define MATE (1 << 0) /* address sent irq en */
> +
> +
> +enum {
> + RCAR_BUS_PHASE_ADDR,
> + RCAR_BUS_PHASE_DATA,
> + RCAR_BUS_PHASE_STOP,
> +};
> +
> +enum {
> + RCAR_IRQ_CLOSE,
> + RCAR_IRQ_OPEN_FOR_SEND,
> + RCAR_IRQ_OPEN_FOR_RECV,
> + RCAR_IRQ_OPEN_FOR_STOP,
> +};
> +
> +/*
> + * flags
> + */
> +#define ID_LAST_MSG (1 << 0)
> +#define ID_IOERROR (1 << 1)
> +#define ID_DONE (1 << 2)
> +#define ID_ARBLOST (1 << 3)
> +#define ID_NACK (1 << 4)
> +
> +struct rcar_i2c_priv {
> + void __iomem *io;
> + struct i2c_adapter adap;
> + struct i2c_msg *msg;
> +
> + spinlock_t lock;
> + wait_queue_head_t wait;
> +
> + int pos;
> + int irq;
> + u32 icccr;
> + u32 flags;
> +};
> +
> +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
> +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
> +
> +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
> +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
> +
> +/*
> + * basic functions
> + */
> +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> +{
> + __raw_writel(val, priv->io + reg);
> +}
> +
> +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> +{
> + return __raw_readl(priv->io + reg);
> +}
> +
> +#if 0 /* no user at this point */
Could it be removed then?
BTW feel free to ignore such comments.
> +static u32 rcar_i2c_bset(struct rcar_i2c_priv *priv, int reg,
> + u32 mask, u32 data)
> +{
> + u32 val = rcar_i2c_read(priv, reg);
> +
> + val &= ~mask;
> + val |= (mask & data);
> +
> + rcar_i2c_write(priv, reg, val);
> +}
> +#endif
> +
> +static void rcar_i2c_soft_reset(struct rcar_i2c_priv *priv)
> +{
> + /*
> + * reset slave mode.
> + * slave mode is not used on this driver
> + */
> + rcar_i2c_write(priv, ICSIER, 0);
> + rcar_i2c_write(priv, ICSAR, 0);
> + rcar_i2c_write(priv, ICSCR, 0);
> + rcar_i2c_write(priv, ICSSR, 0);
> +
> + /* reset master mode */
> + rcar_i2c_write(priv, ICMIER, 0);
> + rcar_i2c_write(priv, ICMCR, 0);
> + rcar_i2c_write(priv, ICMSR, 0);
> + rcar_i2c_write(priv, ICMAR, 0);
> +}
> +
> +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
> +{
> + u32 val = MNRE | MALE | MSTE | MATE; /* default */
> +
> + switch (open) {
> + case RCAR_IRQ_OPEN_FOR_SEND:
> + val |= MDEE; /* default + send */
> + break;
> + case RCAR_IRQ_OPEN_FOR_RECV:
> + val |= MDRE; /* default + read */
> + break;
> + case RCAR_IRQ_OPEN_FOR_STOP:
> + val = MSTE; /* stop irq only */
> + break;
> + case RCAR_IRQ_CLOSE:
> + default:
> + val = 0; /* all close */
> + break;
> + }
> + rcar_i2c_write(priv, ICMIER, val);
> +}
> +
> +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
> +{
> + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
> +}
> +
> +/*
> + * bus control functions
> + */
> +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
> +{
> + int i;
> +
> + for (i = 0; i < 1024; i++) {
> + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
Did not understand this would be grateful if you could explain.
> + return 0;
> + udelay(1);
> + }
> +
> + return -EBUSY;
> +}
> +
> +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> +{
> + switch (phase) {
> + case RCAR_BUS_PHASE_ADDR:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> + break;
> + case RCAR_BUS_PHASE_DATA:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> + break;
> + case RCAR_BUS_PHASE_STOP:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> + break;
> + }
> +
> + return;
> +}
> +
> +/*
> + * clock function
> + */
> +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> + u32 bus_speed,
> + struct device *dev)
> +{
> + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> + u32 scgd, cdf;
> + u32 round, ick;
> +
> + if (!clkp) {
> + dev_err(dev, "there is no peripheral_clk\n");
> + return -EIO;
> + }
> +
> + /*
> + * use 95% bus speed for safety.
> + */
> + bus_speed = bus_speed * 95 / 100;
> +
> + /*
> + * calculate SCL clock
> + * see
> + * ICCCR
> + *
> + * ick = clkp / (1 + CDF)
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + *
> + * ick : I2C internal clock < 20 MHz
> + * ticf : I2C SCL falling time = 35 ns here
> + * tr : I2C SCL rising time = 200 ns here
> + * intd : LSI internal delay = 50 ns here
> + * clkp : peripheral_clk
> + * F[] : integer up-valuation
> + */
> + for (cdf = 0; cdf < 4; cdf++) {
> + ick = clk_get_rate(clkp) / (1 + cdf);
> + if (ick < 20000000)
> + goto ick_find;
> + }
> + dev_err(dev, "there is no best CDF\n");
> +
> + return -EIO;
> +
> +ick_find:
> + /*
> + * it is impossible to calculate large scale
> + * number on u32
> + *
> + * F[(ticf + tr + intd) * ick]
> + * = F[(35 + 200 + 50)ns * ick]
> + * = F[285 * ick / 1000000000]
> + * = F[(ick / 1000000) * 285 / 1000]
> + */
> + round = (ick + 500000) / 1000000 * 285;
> + round = (round + 500) / 1000;
> +
> + /*
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + * -> (20 + SCGD * 8 + round) = ick / SCL
> + * -> SCGD * 8 = (ick / SCL) - 20 - round
> + * -> SCGD = F[((ick / SCL) - 20 - round) / 8]
> + * -> SCGD = ((ick / SCL) - 20 - round + 4) / 8
> + */
> + scgd = ((ick / bus_speed) - 20 - round + 4) / 8;
> +
> + dev_dbg(dev, "clk %u(%lu), round %u, SCL %d, CDF:0x%x, SCGD: 0x%x\n",
> + ick / (20 + scgd * 8 + round),
> + clk_get_rate(clkp), round, bus_speed, cdf, scgd);
> +
> + /*
> + * keep icccr value
> + */
> + priv->icccr = (scgd << 2 | cdf);
> +
> + return 0;
> +}
> +
> +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_write(priv, ICCCR, priv->icccr);
> +}
> +
> +/*
> + * status functions
> + */
> +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
> +{
> + return rcar_i2c_read(priv, ICMSR);
> +}
> +
> +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
> +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
> +{
> + rcar_i2c_write(priv, ICMSR, ~bit);
> +}
> +
> +/*
> + * recv/send functions
> + */
> +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_set_addr(priv, 1);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
> +{
> + int ret;
> +
> + /*
> + * It should check bus status when send case
> + */
> + ret = rcar_i2c_bus_barrier(priv);
> + if (ret < 0)
> + return ret;
> +
> + rcar_i2c_set_addr(priv, 0);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
> +
> + return 0;
> +}
> +
> +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
> +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
> +
> +/*
> + * interrupt functions
> + */
> +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happend.
> + * Do nothing
> + */
> + if (!(msr & MDE))
> + return 0;
> +
> + /*
> + * If address transfer phase finised,
> + * goto data phase.
> + */
> + if (msr & MAT)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + if (priv->pos < msg->len) {
> + /*
> + * Prepare next data to ICRXTX register.
> + * This data will go to _SHIFT_ register.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
> + priv->pos++;
> +
> + } else {
> + /*
> + * The last data was pushed to ICRXTX on _PREV_ empty irq.
> + * It is on _SHIFT_ register, and will sent to I2C bus.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> +
> + if (priv->flags & ID_LAST_MSG)
> + /*
> + * If current msg is the _LAST_ msg,
> + * prepare stop condition here.
> + * ID_DONE will be set on STOP irq.
> + */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + /*
> + * If current msg is _NOT_ last msg,
> + * it doesn't call stop phase.
> + * thus, there is no STOP irq.
> + * return ID_DONE here.
> + */
> + return ID_DONE;
> + }
> +
> + rcar_i2c_send_restart(priv);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happend.
> + * Do nothing
> + */
> + if (!(msr & MDR))
> + return 0;
> +
> + if (msr & MAT) {
> + /*
> + * Address transfer phase finished,
> + * but, there is no data at this point.
> + * Do nothing.
> + */
> + } else if (priv->pos < msg->len) {
> + /*
> + * get received data
> + */
> + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
> + priv->pos++;
> + }
> +
> + /*
> + * If next received data is the last data,
> + * I2C driver should send NACK (or STOP).
> + * But this chip can't send NACK, thus, go to STOP phase.
> + * Otherwise, go to data phase.
> + */
> + if (priv->pos + 1 >= msg->len)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + rcar_i2c_recv_restart(priv);
> +
> + return 0;
> +}
> +
> +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
> +{
> + struct rcar_i2c_priv *priv = ptr;
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + u32 msr;
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock(&priv->lock);
> +
> + msr = rcar_i2c_status_get(priv);
> +
> + /*
> + * Arbitration lost
> + */
> + if (msr & MAL) {
> + /*
> + * CAUTION
> + *
> + * When arbitration lost, device become _slave_ mode.
> + */
> + dev_dbg(dev, "Arbitration Lost\n");
> + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
> + goto out;
> + }
> +
> + /*
> + * Stop
> + */
> + if (msr & MST) {
> + dev_dbg(dev, "Stop\n");
> + rcar_i2c_flags_set(priv, ID_DONE);
> + goto out;
> + }
> +
> + /*
> + * Nack
> + */
> + if (msr & MNR) {
> + dev_dbg(dev, "Nack\n");
> +
> + /* go to stop phase */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
stop is sent regardless of IGNORE nak? Do you think it would be an issue?
> + rcar_i2c_flags_set(priv, ID_NACK);
> + goto out;
> + }
> +
> + /*
> + * recv/send
> + */
> + if (rcar_i2c_is_recv(priv))
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
> + else
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
> +
> +out:
> + if (rcar_i2c_flags_has(priv, ID_DONE)) {
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
> + rcar_i2c_status_clear(priv);
> + wake_up(&priv->wait);
> + }
> +
> + spin_unlock(&priv->lock);
> + /*-------------- spin unlock -----------------*/
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs,
> + int num)
> +{
> + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + unsigned long flags;
> + int i, ret, timeout;
> +
> + /*================== enable i2c device ===================*/
> + pm_runtime_get_sync(dev);
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + rcar_i2c_soft_reset(priv);
could you explain this.
> + rcar_i2c_clock_start(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + ret = -EINVAL;
> + for (i = 0; i < num; i++) {
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + /* init each data */
> + priv->msg = &msgs[i];
> + priv->pos = 0;
> + priv->flags = 0;
> + if (priv->msg == &msgs[num - 1])
> + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> +
> + /* start send/recv */
> + if (rcar_i2c_is_recv(priv))
> + ret = rcar_i2c_recv(priv);
> + else
> + ret = rcar_i2c_send(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + if (ret < 0)
> + break;
> +
> + /*
> + * wait result
> + */
> + timeout = wait_event_timeout(priv->wait,
> + rcar_i2c_flags_has(priv, ID_DONE),
> + 5 * HZ);
> + if (!timeout) {
> + ret = -ETIMEDOUT;
> + break;
> + }
> +
> + /*
> + * error handling
> + */
> + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> + ret = -EREMOTEIO;
> +
> + if (msgs->flags & I2C_M_IGNORE_NAK)
you have the IGNORE_NAK support should we update the func flags then(magling)?
> + ret = 0;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> + ret = -EAGAIN;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> + ret = -EIO;
> + break;
> + }
> +
> + ret = i + 1; /* The number of transfer */
> + }
> +
> + pm_runtime_put_sync(dev);
> + /*================== disable i2c device ===================*/
> +
> + if (ret < 0)
> + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> +
> + return ret;
> +}
> +
> +static u32 rcar_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm rcar_i2c_algo = {
> + .master_xfer = rcar_i2c_master_xfer,
> + .functionality = rcar_i2c_func,
> +};
> +
> +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> +{
> + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> + struct rcar_i2c_priv *priv;
> + struct i2c_adapter *adap;
> + struct resource *res;
> + struct device *dev = &pdev->dev;
> + u32 bus_speed;
> + int ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "no mmio resources\n");
> + return -ENODEV;
> + }
> +
> + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(dev, "no mem for private data\n");
> + return -ENOMEM;
> + }
> +
> + bus_speed = 100000; /* default 100 kHz */
> + if (pdata && pdata->bus_speed)
> + bus_speed = pdata->bus_speed;
> + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> + if (ret < 0)
> + goto error_free;
> +
> + priv->io = devm_ioremap(dev, res->start, resource_size(res));
> + if (!priv->io) {
> + dev_err(dev, "cannot ioremap\n");
> + ret = -ENODEV;
> + goto error_free;
> + }
> +
> + priv->irq = platform_get_irq(pdev, 0);
> + init_waitqueue_head(&priv->wait);
> + spin_lock_init(&priv->lock);
> +
> + adap = &priv->adap;
> + adap->nr = pdev->id;
> + adap->algo = &rcar_i2c_algo;
> + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> + adap->retries = 3;
There was a discurssion about the retries being the responsibility of
the driver.
Not sure though.
> + adap->dev.parent = dev;
> + i2c_set_adapdata(adap, priv);
> + strlcpy(adap->name, pdev->name, sizeof(adap->name));
> +
> + if (request_irq(priv->irq, rcar_i2c_irq, 0,
> + dev_name(dev), priv)) {
> + dev_err(dev, "cannot get irq %d\n", priv->irq);
> + ret = -EBUSY;
> + goto error_unmap;
> + }
> +
> + ret = i2c_add_numbered_adapter(adap);
> + if (ret < 0) {
> + dev_err(dev, "reg adap failed: %d\n", ret);
> + goto error_irq;
> + }
> +
> + pm_runtime_enable(dev);
> + platform_set_drvdata(pdev, priv);
> +
> + dev_info(dev, "probed\n");
> +
> + return 0;
> +
> +error_irq:
> + free_irq(priv->irq, priv);
> +error_unmap:
> + devm_iounmap(dev, priv->io);
> +error_free:
> + devm_kfree(dev, priv);
why not let dev res handle these.
> +
> + return ret;
> +}
> +
> +static int __devexit rcar_i2c_remove(struct platform_device *pdev)
> +{
> + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> +
> + i2c_del_adapter(&priv->adap);
> + free_irq(priv->irq, priv);
> + devm_iounmap(dev, priv->io);
> + devm_kfree(dev, priv);
these may not be needed.
> + platform_set_drvdata(pdev, NULL);
not sure if this is needed.
> + pm_runtime_disable(dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver rcar_i2c_drv = {
> + .driver = {
> + .name = "i2c-rcar",
> + .owner = THIS_MODULE,
> + },
> + .probe = rcar_i2c_probe,
> + .remove = __devexit_p(rcar_i2c_remove),
> +};
> +
> +module_platform_driver(rcar_i2c_drv);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
> +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
> diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
> new file mode 100644
> index 0000000..496f5c2
> --- /dev/null
> +++ b/include/linux/i2c/i2c-rcar.h
> @@ -0,0 +1,10 @@
> +#ifndef __I2C_R_CAR_H__
> +#define __I2C_R_CAR_H__
> +
> +#include <linux/platform_device.h>
> +
> +struct i2c_rcar_platform_data {
> + u32 bus_speed;
> +};
> +
> +#endif /* __I2C_R_CAR_H__ */
> --
> 1.7.5.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <CAM=Q2ctETiVdyKBd_MxY2Ts=1XMPa_cdEJRkQpEySL6du9oLmw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2012-08-28 5:33 ` Kuninori Morimoto
[not found] ` <878vczd1hw.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
0 siblings, 1 reply; 21+ messages in thread
From: Kuninori Morimoto @ 2012-08-28 5:33 UTC (permalink / raw)
To: Shubhrajyoti Datta
Cc: Wolfram Sang, Jean Delvare, Ben Dooks, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Hi Shubhrajyoti
Thank you for checking patch.
I create v2 patch and post it soon.
There is not big reason, but I would like to keep adap->retries for now.
> On Wed, Jul 25, 2012 at 12:06 PM, Kuninori Morimoto
> <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
> > R-Car I2C is similar with SH7760 I2C.
> > But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> > Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> > This patch creates new Renesas R-Car I2C driver.
> >
> > Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> > ---
> > drivers/i2c/busses/Kconfig | 10 +
> > drivers/i2c/busses/Makefile | 1 +
> > drivers/i2c/busses/i2c-rcar.c | 741 +++++++++++++++++++++++++++++++++++++++++
> > include/linux/i2c/i2c-rcar.h | 10 +
> > 4 files changed, 762 insertions(+), 0 deletions(-)
> > create mode 100644 drivers/i2c/busses/i2c-rcar.c
> > create mode 100644 include/linux/i2c/i2c-rcar.h
> >
> > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> > index 7244c8b..3175c81 100644
> > --- a/drivers/i2c/busses/Kconfig
> > +++ b/drivers/i2c/busses/Kconfig
> > @@ -704,6 +704,16 @@ config I2C_XLR
> > This driver can also be built as a module. If so, the module
> > will be called i2c-xlr.
> >
> > +config I2C_RCAR
> > + tristate "Renesas R-Car I2C Controller"
> > + depends on ARCH_SHMOBILE && I2C
> > + help
> > + If you say yes to this option, support will be included for the
> > + R-Car I2C controller.
> > +
> > + This driver can also be built as a module. If so, the module
> > + will be called i2c-rcar.
> > +
> > comment "External I2C/SMBus adapter drivers"
> >
> > config I2C_DIOLAN_U2C
> > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> > index ce3c2be..e98ff51 100644
> > --- a/drivers/i2c/busses/Makefile
> > +++ b/drivers/i2c/busses/Makefile
> > @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> > obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> > obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> > obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> > +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
> >
> > # External I2C/SMBus adapter drivers
> > obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> > diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> > new file mode 100644
> > index 0000000..2993cab
> > --- /dev/null
> > +++ b/drivers/i2c/busses/i2c-rcar.c
> > @@ -0,0 +1,741 @@
> > +/*
> > + * drivers/i2c/busses/i2c-rcar.c
> > + *
> > + * Copyright (C) 2012 Renesas Solutions Corp.
> > + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> > + *
> > + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
> > + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
> > + *
> > + * This file used out-of-tree driver i2c-rcar.c
> > + * Copyright (C) 2011-2012 Renesas Electronics Corporation
> > + *
> > + * 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
> > + *
> > + * 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.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> > + */
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/err.h>
> > +#include <linux/init.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/i2c.h>
> > +#include <linux/i2c/i2c-rcar.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +
> > +/* register offsets */
> > +#define ICSCR 0x00 /* slave ctrl */
> > +#define ICMCR 0x04 /* master ctrl */
> > +#define ICSSR 0x08 /* slave status */
> > +#define ICMSR 0x0C /* master status */
> > +#define ICSIER 0x10 /* slave irq enable */
> > +#define ICMIER 0x14 /* master irq enable */
> > +#define ICCCR 0x18 /* clock dividers */
> > +#define ICSAR 0x1C /* slave address */
> > +#define ICMAR 0x20 /* master address */
> > +#define ICRXTX 0x24 /* data port */
> > +
> > +/* ICMCR */
> > +#define MDBS (1 << 7) /* non-fifo mode switch */
> > +#define FSCL (1 << 6) /* override SCL pin */
> > +#define FSDA (1 << 5) /* override SDA pin */
> > +#define OBPC (1 << 4) /* override pins */
> > +#define MIE (1 << 3) /* master if enable */
> > +#define TSBE (1 << 2)
> > +#define FSB (1 << 1) /* force stop bit */
> > +#define ESG (1 << 0) /* en startbit gen */
> > +
> > +/* ICMSR */
> > +#define MNR (1 << 6) /* nack received */
> > +#define MAL (1 << 5) /* arbitration lost */
> > +#define MST (1 << 4) /* sent a stop */
> > +#define MDE (1 << 3)
> > +#define MDT (1 << 2)
> > +#define MDR (1 << 1)
> > +#define MAT (1 << 0) /* slave addr xfer done */
> > +
> > +/* ICMIE */
> > +#define MNRE (1 << 6) /* nack irq en */
> > +#define MALE (1 << 5) /* arblos irq en */
> > +#define MSTE (1 << 4) /* stop irq en */
> > +#define MDEE (1 << 3)
> > +#define MDTE (1 << 2)
> > +#define MDRE (1 << 1)
> > +#define MATE (1 << 0) /* address sent irq en */
> > +
> > +
> > +enum {
> > + RCAR_BUS_PHASE_ADDR,
> > + RCAR_BUS_PHASE_DATA,
> > + RCAR_BUS_PHASE_STOP,
> > +};
> > +
> > +enum {
> > + RCAR_IRQ_CLOSE,
> > + RCAR_IRQ_OPEN_FOR_SEND,
> > + RCAR_IRQ_OPEN_FOR_RECV,
> > + RCAR_IRQ_OPEN_FOR_STOP,
> > +};
> > +
> > +/*
> > + * flags
> > + */
> > +#define ID_LAST_MSG (1 << 0)
> > +#define ID_IOERROR (1 << 1)
> > +#define ID_DONE (1 << 2)
> > +#define ID_ARBLOST (1 << 3)
> > +#define ID_NACK (1 << 4)
> > +
> > +struct rcar_i2c_priv {
> > + void __iomem *io;
> > + struct i2c_adapter adap;
> > + struct i2c_msg *msg;
> > +
> > + spinlock_t lock;
> > + wait_queue_head_t wait;
> > +
> > + int pos;
> > + int irq;
> > + u32 icccr;
> > + u32 flags;
> > +};
> > +
> > +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
> > +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
> > +
> > +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
> > +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
> > +
> > +/*
> > + * basic functions
> > + */
> > +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> > +{
> > + __raw_writel(val, priv->io + reg);
> > +}
> > +
> > +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> > +{
> > + return __raw_readl(priv->io + reg);
> > +}
> > +
> > +#if 0 /* no user at this point */
>
> Could it be removed then?
> BTW feel free to ignore such comments.
>
> > +static u32 rcar_i2c_bset(struct rcar_i2c_priv *priv, int reg,
> > + u32 mask, u32 data)
> > +{
> > + u32 val = rcar_i2c_read(priv, reg);
> > +
> > + val &= ~mask;
> > + val |= (mask & data);
> > +
> > + rcar_i2c_write(priv, reg, val);
> > +}
> > +#endif
> > +
> > +static void rcar_i2c_soft_reset(struct rcar_i2c_priv *priv)
> > +{
> > + /*
> > + * reset slave mode.
> > + * slave mode is not used on this driver
> > + */
> > + rcar_i2c_write(priv, ICSIER, 0);
> > + rcar_i2c_write(priv, ICSAR, 0);
> > + rcar_i2c_write(priv, ICSCR, 0);
> > + rcar_i2c_write(priv, ICSSR, 0);
> > +
> > + /* reset master mode */
> > + rcar_i2c_write(priv, ICMIER, 0);
> > + rcar_i2c_write(priv, ICMCR, 0);
> > + rcar_i2c_write(priv, ICMSR, 0);
> > + rcar_i2c_write(priv, ICMAR, 0);
> > +}
> > +
> > +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
> > +{
> > + u32 val = MNRE | MALE | MSTE | MATE; /* default */
> > +
> > + switch (open) {
> > + case RCAR_IRQ_OPEN_FOR_SEND:
> > + val |= MDEE; /* default + send */
> > + break;
> > + case RCAR_IRQ_OPEN_FOR_RECV:
> > + val |= MDRE; /* default + read */
> > + break;
> > + case RCAR_IRQ_OPEN_FOR_STOP:
> > + val = MSTE; /* stop irq only */
> > + break;
> > + case RCAR_IRQ_CLOSE:
> > + default:
> > + val = 0; /* all close */
> > + break;
> > + }
> > + rcar_i2c_write(priv, ICMIER, val);
> > +}
> > +
> > +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
> > +{
> > + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
> > +}
> > +
> > +/*
> > + * bus control functions
> > + */
> > +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < 1024; i++) {
> > + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
> Did not understand this would be grateful if you could explain.
>
> > + return 0;
> > + udelay(1);
> > + }
> > +
> > + return -EBUSY;
> > +}
> > +
> > +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> > +{
> > + switch (phase) {
> > + case RCAR_BUS_PHASE_ADDR:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> > + break;
> > + case RCAR_BUS_PHASE_DATA:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> > + break;
> > + case RCAR_BUS_PHASE_STOP:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> > + break;
> > + }
> > +
> > + return;
> > +}
> > +
> > +/*
> > + * clock function
> > + */
> > +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> > + u32 bus_speed,
> > + struct device *dev)
> > +{
> > + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> > + u32 scgd, cdf;
> > + u32 round, ick;
> > +
> > + if (!clkp) {
> > + dev_err(dev, "there is no peripheral_clk\n");
> > + return -EIO;
> > + }
> > +
> > + /*
> > + * use 95% bus speed for safety.
> > + */
> > + bus_speed = bus_speed * 95 / 100;
> > +
> > + /*
> > + * calculate SCL clock
> > + * see
> > + * ICCCR
> > + *
> > + * ick = clkp / (1 + CDF)
> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> > + *
> > + * ick : I2C internal clock < 20 MHz
> > + * ticf : I2C SCL falling time = 35 ns here
> > + * tr : I2C SCL rising time = 200 ns here
> > + * intd : LSI internal delay = 50 ns here
> > + * clkp : peripheral_clk
> > + * F[] : integer up-valuation
> > + */
> > + for (cdf = 0; cdf < 4; cdf++) {
> > + ick = clk_get_rate(clkp) / (1 + cdf);
> > + if (ick < 20000000)
> > + goto ick_find;
> > + }
> > + dev_err(dev, "there is no best CDF\n");
> > +
> > + return -EIO;
> > +
> > +ick_find:
> > + /*
> > + * it is impossible to calculate large scale
> > + * number on u32
> > + *
> > + * F[(ticf + tr + intd) * ick]
> > + * = F[(35 + 200 + 50)ns * ick]
> > + * = F[285 * ick / 1000000000]
> > + * = F[(ick / 1000000) * 285 / 1000]
> > + */
> > + round = (ick + 500000) / 1000000 * 285;
> > + round = (round + 500) / 1000;
> > +
> > + /*
> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> > + * -> (20 + SCGD * 8 + round) = ick / SCL
> > + * -> SCGD * 8 = (ick / SCL) - 20 - round
> > + * -> SCGD = F[((ick / SCL) - 20 - round) / 8]
> > + * -> SCGD = ((ick / SCL) - 20 - round + 4) / 8
> > + */
> > + scgd = ((ick / bus_speed) - 20 - round + 4) / 8;
> > +
> > + dev_dbg(dev, "clk %u(%lu), round %u, SCL %d, CDF:0x%x, SCGD: 0x%x\n",
> > + ick / (20 + scgd * 8 + round),
> > + clk_get_rate(clkp), round, bus_speed, cdf, scgd);
> > +
> > + /*
> > + * keep icccr value
> > + */
> > + priv->icccr = (scgd << 2 | cdf);
> > +
> > + return 0;
> > +}
> > +
> > +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
> > +{
> > + rcar_i2c_write(priv, ICCCR, priv->icccr);
> > +}
> > +
> > +/*
> > + * status functions
> > + */
> > +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
> > +{
> > + return rcar_i2c_read(priv, ICMSR);
> > +}
> > +
> > +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
> > +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
> > +{
> > + rcar_i2c_write(priv, ICMSR, ~bit);
> > +}
> > +
> > +/*
> > + * recv/send functions
> > + */
> > +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
> > +{
> > + rcar_i2c_set_addr(priv, 1);
> > + rcar_i2c_status_clear(priv);
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
> > +{
> > + int ret;
> > +
> > + /*
> > + * It should check bus status when send case
> > + */
> > + ret = rcar_i2c_bus_barrier(priv);
> > + if (ret < 0)
> > + return ret;
> > +
> > + rcar_i2c_set_addr(priv, 0);
> > + rcar_i2c_status_clear(priv);
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
> > +
> > + return 0;
> > +}
> > +
> > +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
> > +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
> > +
> > +/*
> > + * interrupt functions
> > + */
> > +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> > +{
> > + struct i2c_msg *msg = priv->msg;
> > +
> > + /*
> > + * FIXME
> > + * sometimes, unknown interrupt happend.
> > + * Do nothing
> > + */
> > + if (!(msr & MDE))
> > + return 0;
> > +
> > + /*
> > + * If address transfer phase finised,
> > + * goto data phase.
> > + */
> > + if (msr & MAT)
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> > +
> > + if (priv->pos < msg->len) {
> > + /*
> > + * Prepare next data to ICRXTX register.
> > + * This data will go to _SHIFT_ register.
> > + *
> > + * *
> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> > + */
> > + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
> > + priv->pos++;
> > +
> > + } else {
> > + /*
> > + * The last data was pushed to ICRXTX on _PREV_ empty irq.
> > + * It is on _SHIFT_ register, and will sent to I2C bus.
> > + *
> > + * *
> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> > + */
> > +
> > + if (priv->flags & ID_LAST_MSG)
> > + /*
> > + * If current msg is the _LAST_ msg,
> > + * prepare stop condition here.
> > + * ID_DONE will be set on STOP irq.
> > + */
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + else
> > + /*
> > + * If current msg is _NOT_ last msg,
> > + * it doesn't call stop phase.
> > + * thus, there is no STOP irq.
> > + * return ID_DONE here.
> > + */
> > + return ID_DONE;
> > + }
> > +
> > + rcar_i2c_send_restart(priv);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> > +{
> > + struct i2c_msg *msg = priv->msg;
> > +
> > + /*
> > + * FIXME
> > + * sometimes, unknown interrupt happend.
> > + * Do nothing
> > + */
> > + if (!(msr & MDR))
> > + return 0;
> > +
> > + if (msr & MAT) {
> > + /*
> > + * Address transfer phase finished,
> > + * but, there is no data at this point.
> > + * Do nothing.
> > + */
> > + } else if (priv->pos < msg->len) {
> > + /*
> > + * get received data
> > + */
> > + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
> > + priv->pos++;
> > + }
> > +
> > + /*
> > + * If next received data is the last data,
> > + * I2C driver should send NACK (or STOP).
> > + * But this chip can't send NACK, thus, go to STOP phase.
> > + * Otherwise, go to data phase.
> > + */
> > + if (priv->pos + 1 >= msg->len)
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + else
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> > +
> > + rcar_i2c_recv_restart(priv);
> > +
> > + return 0;
> > +}
> > +
> > +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
> > +{
> > + struct rcar_i2c_priv *priv = ptr;
> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
> > + u32 msr;
> > +
> > + /*-------------- spin lock -----------------*/
> > + spin_lock(&priv->lock);
> > +
> > + msr = rcar_i2c_status_get(priv);
> > +
> > + /*
> > + * Arbitration lost
> > + */
> > + if (msr & MAL) {
> > + /*
> > + * CAUTION
> > + *
> > + * When arbitration lost, device become _slave_ mode.
> > + */
> > + dev_dbg(dev, "Arbitration Lost\n");
> > + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
> > + goto out;
> > + }
> > +
> > + /*
> > + * Stop
> > + */
> > + if (msr & MST) {
> > + dev_dbg(dev, "Stop\n");
> > + rcar_i2c_flags_set(priv, ID_DONE);
> > + goto out;
> > + }
> > +
> > + /*
> > + * Nack
> > + */
> > + if (msr & MNR) {
> > + dev_dbg(dev, "Nack\n");
> > +
> > + /* go to stop phase */
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
>
> stop is sent regardless of IGNORE nak? Do you think it would be an issue?
>
>
> > + rcar_i2c_flags_set(priv, ID_NACK);
> > + goto out;
> > + }
> > +
> > + /*
> > + * recv/send
> > + */
> > + if (rcar_i2c_is_recv(priv))
> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
> > + else
> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
> > +
> > +out:
> > + if (rcar_i2c_flags_has(priv, ID_DONE)) {
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
> > + rcar_i2c_status_clear(priv);
> > + wake_up(&priv->wait);
> > + }
> > +
> > + spin_unlock(&priv->lock);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> > + struct i2c_msg *msgs,
> > + int num)
> > +{
> > + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
> > + unsigned long flags;
> > + int i, ret, timeout;
> > +
> > + /*================== enable i2c device ===================*/
> > + pm_runtime_get_sync(dev);
> > +
> > + /*-------------- spin lock -----------------*/
> > + spin_lock_irqsave(&priv->lock, flags);
> > +
> > + rcar_i2c_soft_reset(priv);
> could you explain this.
>
> > + rcar_i2c_clock_start(priv);
> > +
> > + spin_unlock_irqrestore(&priv->lock, flags);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + ret = -EINVAL;
> > + for (i = 0; i < num; i++) {
> > + /*-------------- spin lock -----------------*/
> > + spin_lock_irqsave(&priv->lock, flags);
> > +
> > + /* init each data */
> > + priv->msg = &msgs[i];
> > + priv->pos = 0;
> > + priv->flags = 0;
> > + if (priv->msg == &msgs[num - 1])
> > + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> > +
> > + /* start send/recv */
> > + if (rcar_i2c_is_recv(priv))
> > + ret = rcar_i2c_recv(priv);
> > + else
> > + ret = rcar_i2c_send(priv);
> > +
> > + spin_unlock_irqrestore(&priv->lock, flags);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + if (ret < 0)
> > + break;
> > +
> > + /*
> > + * wait result
> > + */
> > + timeout = wait_event_timeout(priv->wait,
> > + rcar_i2c_flags_has(priv, ID_DONE),
> > + 5 * HZ);
> > + if (!timeout) {
> > + ret = -ETIMEDOUT;
> > + break;
> > + }
> > +
> > + /*
> > + * error handling
> > + */
> > + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> > + ret = -EREMOTEIO;
> > +
> > + if (msgs->flags & I2C_M_IGNORE_NAK)
> you have the IGNORE_NAK support should we update the func flags then(magling)?
>
> > + ret = 0;
> > + break;
> > + }
> > +
> > + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> > + ret = -EAGAIN;
> > + break;
> > + }
> > +
> > + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> > + ret = -EIO;
> > + break;
> > + }
> > +
> > + ret = i + 1; /* The number of transfer */
> > + }
> > +
> > + pm_runtime_put_sync(dev);
> > + /*================== disable i2c device ===================*/
> > +
> > + if (ret < 0)
> > + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> > +
> > + return ret;
> > +}
> > +
> > +static u32 rcar_i2c_func(struct i2c_adapter *adap)
> > +{
> > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> > +}
> > +
> > +static const struct i2c_algorithm rcar_i2c_algo = {
> > + .master_xfer = rcar_i2c_master_xfer,
> > + .functionality = rcar_i2c_func,
> > +};
> > +
> > +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> > +{
> > + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> > + struct rcar_i2c_priv *priv;
> > + struct i2c_adapter *adap;
> > + struct resource *res;
> > + struct device *dev = &pdev->dev;
> > + u32 bus_speed;
> > + int ret;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + if (!res) {
> > + dev_err(dev, "no mmio resources\n");
> > + return -ENODEV;
> > + }
> > +
> > + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> > + if (!priv) {
> > + dev_err(dev, "no mem for private data\n");
> > + return -ENOMEM;
> > + }
> > +
> > + bus_speed = 100000; /* default 100 kHz */
> > + if (pdata && pdata->bus_speed)
> > + bus_speed = pdata->bus_speed;
> > + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> > + if (ret < 0)
> > + goto error_free;
> > +
> > + priv->io = devm_ioremap(dev, res->start, resource_size(res));
> > + if (!priv->io) {
> > + dev_err(dev, "cannot ioremap\n");
> > + ret = -ENODEV;
> > + goto error_free;
> > + }
> > +
> > + priv->irq = platform_get_irq(pdev, 0);
> > + init_waitqueue_head(&priv->wait);
> > + spin_lock_init(&priv->lock);
> > +
> > + adap = &priv->adap;
> > + adap->nr = pdev->id;
> > + adap->algo = &rcar_i2c_algo;
> > + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> > + adap->retries = 3;
>
> There was a discurssion about the retries being the responsibility of
> the driver.
> Not sure though.
>
>
> > + adap->dev.parent = dev;
> > + i2c_set_adapdata(adap, priv);
> > + strlcpy(adap->name, pdev->name, sizeof(adap->name));
> > +
> > + if (request_irq(priv->irq, rcar_i2c_irq, 0,
> > + dev_name(dev), priv)) {
> > + dev_err(dev, "cannot get irq %d\n", priv->irq);
> > + ret = -EBUSY;
> > + goto error_unmap;
> > + }
> > +
> > + ret = i2c_add_numbered_adapter(adap);
> > + if (ret < 0) {
> > + dev_err(dev, "reg adap failed: %d\n", ret);
> > + goto error_irq;
> > + }
> > +
> > + pm_runtime_enable(dev);
> > + platform_set_drvdata(pdev, priv);
> > +
> > + dev_info(dev, "probed\n");
> > +
> > + return 0;
> > +
> > +error_irq:
> > + free_irq(priv->irq, priv);
> > +error_unmap:
> > + devm_iounmap(dev, priv->io);
> > +error_free:
> > + devm_kfree(dev, priv);
> why not let dev res handle these.
>
> > +
> > + return ret;
> > +}
> > +
> > +static int __devexit rcar_i2c_remove(struct platform_device *pdev)
> > +{
> > + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
> > + struct device *dev = &pdev->dev;
> > +
> > + i2c_del_adapter(&priv->adap);
> > + free_irq(priv->irq, priv);
> > + devm_iounmap(dev, priv->io);
> > + devm_kfree(dev, priv);
> these may not be needed.
>
> > + platform_set_drvdata(pdev, NULL);
> not sure if this is needed.
>
> > + pm_runtime_disable(dev);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver rcar_i2c_drv = {
> > + .driver = {
> > + .name = "i2c-rcar",
> > + .owner = THIS_MODULE,
> > + },
> > + .probe = rcar_i2c_probe,
> > + .remove = __devexit_p(rcar_i2c_remove),
> > +};
> > +
> > +module_platform_driver(rcar_i2c_drv);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
> > +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
> > diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
> > new file mode 100644
> > index 0000000..496f5c2
> > --- /dev/null
> > +++ b/include/linux/i2c/i2c-rcar.h
> > @@ -0,0 +1,10 @@
> > +#ifndef __I2C_R_CAR_H__
> > +#define __I2C_R_CAR_H__
> > +
> > +#include <linux/platform_device.h>
> > +
> > +struct i2c_rcar_platform_data {
> > + u32 bus_speed;
> > +};
> > +
> > +#endif /* __I2C_R_CAR_H__ */
> > --
> > 1.7.5.4
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> > the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
Best regards
---
Kuninori Morimoto
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <878vczd1hw.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
@ 2012-08-28 5:39 ` Shubhrajyoti Datta
[not found] ` <CAM=Q2cs5pFpVjqa_MFCLTFqdCo6SSm-1Ep=WtJ7fpABzWEGD9w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
0 siblings, 1 reply; 21+ messages in thread
From: Shubhrajyoti Datta @ 2012-08-28 5:39 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Wolfram Sang, Jean Delvare, Ben Dooks, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
On Tue, Aug 28, 2012 at 11:03 AM, Kuninori Morimoto
<kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
>
> Hi Shubhrajyoti
>
> Thank you for checking patch.
> I create v2 patch and post it soon.
> There is not big reason, but I would like to keep adap->retries for now.
>
Or maybe just wait for Jean's advice on the same.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <871uk04aa2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-08-28 0:19 ` Kuninori Morimoto
2012-08-28 4:48 ` Shubhrajyoti Datta
@ 2012-08-28 6:26 ` phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
[not found] ` <OF0AA2687D.3C2F6EFC-ON80257A56.003BB6CC-80257A68.0023665F-mWMTcI9IYFFWk0Htik3J/w@public.gmane.org>
2 siblings, 1 reply; 21+ messages in thread
From: phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ @ 2012-08-28 6:26 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Ben Dooks, Jean Delvare, Kuninori Morimoto, Linux-I2C, Magnus,
Wolfram Sang, Shubhrajyoti Datta
Hi Morimoto-san,
Apologies for the late review, I have some comments below.
Thanks,
Phil
<snip>
> +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32
val)
> +{
> + __raw_writel(val, priv->io + reg);
> +}
> +
> +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> +{
> + return __raw_readl(priv->io + reg);
> +}
I think we should use writel/readl here. We have a number of devices where
the virtual address map conflicts with register addresses for other
peripherals.
<snip>
> +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> +{
> + switch (phase) {
> + case RCAR_BUS_PHASE_ADDR:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> + break;
> + case RCAR_BUS_PHASE_DATA:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> + break;
> + case RCAR_BUS_PHASE_STOP:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> + break;
> + }
> +
> + return;
> +}
Don't need the return.
> +
> +/*
> + * clock function
> + */
> +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> + u32 bus_speed,
> + struct device *dev)
> +{
> + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> + u32 scgd, cdf;
> + u32 round, ick;
> +
> + if (!clkp) {
> + dev_err(dev, "there is no peripheral_clk\n");
> + return -EIO;
> + }
> +
> + /*
> + * use 95% bus speed for safety.
> + */
> + bus_speed = bus_speed * 95 / 100;
Why do you use 95% of the bus speed? Surely, either the hardware supports
a specific speed or it doesn't.
> +
> + /*
> + * calculate SCL clock
> + * see
> + * ICCCR
> + *
> + * ick = clkp / (1 + CDF)
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + *
> + * ick : I2C internal clock < 20 MHz
> + * ticf : I2C SCL falling time = 35 ns here
> + * tr : I2C SCL rising time = 200 ns here
> + * intd : LSI internal delay = 50 ns here
> + * clkp : peripheral_clk
> + * F[] : integer up-valuation
> + */
> + for (cdf = 0; cdf < 4; cdf++) {
> + ick = clk_get_rate(clkp) / (1 + cdf);
> + if (ick < 20000000)
> + goto ick_find;
> + }
> + dev_err(dev, "there is no best CDF\n");
> +
> + return -EIO;
> +
> +ick_find:
> + /*
> + * it is impossible to calculate large scale
> + * number on u32
> + *
> + * F[(ticf + tr + intd) * ick]
> + * = F[(35 + 200 + 50)ns * ick]
> + * = F[285 * ick / 1000000000]
> + * = F[(ick / 1000000) * 285 / 1000]
> + */
> + round = (ick + 500000) / 1000000 * 285;
> + round = (round + 500) / 1000;
Now that I see this, I guess that you used 95% bus clock due to rounding
errors here. If so, maybe it would be better to try to improve this
calculation.
<snip>
> +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happend.
typo: happend => happened
> + * Do nothing
> + */
> + if (!(msr & MDE))
> + return 0;
> +
> + /*
> + * If address transfer phase finised,
typo: finised => finished
<snip>
> +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happend.
typo: happend => happened
<snip>
> +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs,
> + int num)
> +{
> + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + unsigned long flags;
> + int i, ret, timeout;
> +
> + /*================== enable i2c device ===================*/
> + pm_runtime_get_sync(dev);
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + rcar_i2c_soft_reset(priv);
> + rcar_i2c_clock_start(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + ret = -EINVAL;
> + for (i = 0; i < num; i++) {
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + /* init each data */
> + priv->msg = &msgs[i];
> + priv->pos = 0;
> + priv->flags = 0;
> + if (priv->msg == &msgs[num - 1])
> + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> +
> + /* start send/recv */
> + if (rcar_i2c_is_recv(priv))
> + ret = rcar_i2c_recv(priv);
> + else
> + ret = rcar_i2c_send(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + if (ret < 0)
> + break;
> +
> + /*
> + * wait result
> + */
> + timeout = wait_event_timeout(priv->wait,
> + rcar_i2c_flags_has(priv, ID_DONE),
> + 5 * HZ);
> + if (!timeout) {
> + ret = -ETIMEDOUT;
> + break;
> + }
> +
> + /*
> + * error handling
> + */
> + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> + ret = -EREMOTEIO;
> +
> + if (msgs->flags & I2C_M_IGNORE_NAK)
> + ret = 0;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> + ret = -EAGAIN;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> + ret = -EIO;
> + break;
> + }
> +
> + ret = i + 1; /* The number of transfer */
> + }
> +
> + pm_runtime_put_sync(dev);
> + /*================== disable i2c device ===================*/
This comment should be above the previous line, unless you the comment is
meant to say "i2c device disabled"
> +
> + if (ret < 0)
> + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> +
> + return ret;
> +}
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <OF0AA2687D.3C2F6EFC-ON80257A56.003BB6CC-80257A68.0023665F-mWMTcI9IYFFWk0Htik3J/w@public.gmane.org>
@ 2012-08-28 7:00 ` Kuninori Morimoto
0 siblings, 0 replies; 21+ messages in thread
From: Kuninori Morimoto @ 2012-08-28 7:00 UTC (permalink / raw)
To: phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Cc: Ben Dooks, Jean Delvare, Kuninori Morimoto, Linux-I2C, Magnus,
Wolfram Sang, Shubhrajyoti Datta
Hi Phil
Thank you for your comments
> > +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32
> val)
> > +{
> > + __raw_writel(val, priv->io + reg);
> > +}
> > +
> > +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> > +{
> > + return __raw_readl(priv->io + reg);
> > +}
> I think we should use writel/readl here. We have a number of devices where
> the virtual address map conflicts with register addresses for other
> peripherals.
OK. I will fix it.
> > +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> > +{
> > + switch (phase) {
> > + case RCAR_BUS_PHASE_ADDR:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> > + break;
> > + case RCAR_BUS_PHASE_DATA:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> > + break;
> > + case RCAR_BUS_PHASE_STOP:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> > + break;
> > + }
> > +
> > + return;
> > +}
> Don't need the return.
indeed. thanks
> > + /*
> > + * use 95% bus speed for safety.
> > + */
> > + bus_speed = bus_speed * 95 / 100;
> Why do you use 95% of the bus speed? Surely, either the hardware supports
> a specific speed or it doesn't.
(snip)
> > + /*
> > + * it is impossible to calculate large scale
> > + * number on u32
> > + *
> > + * F[(ticf + tr + intd) * ick]
> > + * = F[(35 + 200 + 50)ns * ick]
> > + * = F[285 * ick / 1000000000]
> > + * = F[(ick / 1000000) * 285 / 1000]
> > + */
> > + round = (ick + 500000) / 1000000 * 285;
> > + round = (round + 500) / 1000;
> Now that I see this, I guess that you used 95% bus clock due to rounding
> errors here. If so, maybe it would be better to try to improve this
> calculation.
Indeed, thanks.
> typo: happend => happened
> typo: finised => finished
> typo: happend => happened
Haha :)
Sorry for my English.
> > +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> > + struct i2c_msg *msgs,
> > + int num)
> > +{
> > + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
> > + unsigned long flags;
> > + int i, ret, timeout;
> > +
> > + /*================== enable i2c device ===================*/
> > + pm_runtime_get_sync(dev);
(snip)
> > + pm_runtime_put_sync(dev);
> > + /*================== disable i2c device ===================*/
> This comment should be above the previous line, unless you the comment is
> meant to say "i2c device disabled"
I see.
Best regards
---
Kuninori Morimoto
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <CAM=Q2cs5pFpVjqa_MFCLTFqdCo6SSm-1Ep=WtJ7fpABzWEGD9w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2012-08-28 7:56 ` Kuninori Morimoto
2012-08-28 8:12 ` Jean Delvare
1 sibling, 0 replies; 21+ messages in thread
From: Kuninori Morimoto @ 2012-08-28 7:56 UTC (permalink / raw)
To: Shubhrajyoti Datta
Cc: Wolfram Sang, Jean Delvare, Ben Dooks, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Hi Shubhrajyoti
> > Thank you for checking patch.
> > I create v2 patch and post it soon.
> > There is not big reason, but I would like to keep adap->retries for now.
> >
> Or maybe just wait for Jean's advice on the same.
I see.
After Jean's advice, I will send v2 patch.
Best regards
---
Kuninori Morimoto
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: add Renesas R-Car I2C driver
[not found] ` <CAM=Q2cs5pFpVjqa_MFCLTFqdCo6SSm-1Ep=WtJ7fpABzWEGD9w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-08-28 7:56 ` Kuninori Morimoto
@ 2012-08-28 8:12 ` Jean Delvare
[not found] ` <20120828101259.409b410b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
1 sibling, 1 reply; 21+ messages in thread
From: Jean Delvare @ 2012-08-28 8:12 UTC (permalink / raw)
To: Shubhrajyoti Datta
Cc: Kuninori Morimoto, Wolfram Sang, Ben Dooks, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
On Tue, 28 Aug 2012 11:09:10 +0530, Shubhrajyoti Datta wrote:
> On Tue, Aug 28, 2012 at 11:03 AM, Kuninori Morimoto
> <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
> >
> > Hi Shubhrajyoti
> >
> > Thank you for checking patch.
> > I create v2 patch and post it soon.
> > There is not big reason, but I would like to keep adap->retries for now.
> >
> Or maybe just wait for Jean's advice on the same.
Using adap->retries is fine, it only covers automatic retries in
specific cases (arbitration loss). The other cases are indeed left for
the slave driver to handle.
--
Jean Delvare
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2] i2c: add Renesas R-Car I2C driver
[not found] ` <20120828101259.409b410b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
@ 2012-08-28 8:37 ` Kuninori Morimoto
[not found] ` <874nnnct03.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
0 siblings, 1 reply; 21+ messages in thread
From: Kuninori Morimoto @ 2012-08-28 8:37 UTC (permalink / raw)
To: Wolfram Sang, Shubhrajyoti Datta, Jean Delvare, Ben Dooks
Cc: Simon, Magnus, Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
R-Car I2C is similar with SH7760 I2C.
But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
This patch creates new Renesas R-Car I2C driver.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v1 -> v2
- removed #if 0 function
- add explanation on rcar_i2c_bus_barrier()
- removed IGNORE_NAK support
- rename rcar_i2c_soft_reset() -> rcar_i2c_init()
- removed devm_kfree/devm_iounmap
- __raw_writel/readl => writel/readl
- removed un-needed return from rcar_i2c_bus_phase()
- tidyup calculation method on rcar_i2c_clock_calculate()
- tidyup English type
- tidyup comment to "i2c device disabled"
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-rcar.c | 715 +++++++++++++++++++++++++++++++++++++++++
include/linux/i2c/i2c-rcar.h | 10 +
4 files changed, 736 insertions(+), 0 deletions(-)
create mode 100644 drivers/i2c/busses/i2c-rcar.c
create mode 100644 include/linux/i2c/i2c-rcar.h
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index b4aaa1b..51baa08 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -709,6 +709,16 @@ config I2C_XLR
This driver can also be built as a module. If so, the module
will be called i2c-xlr.
+config I2C_RCAR
+ tristate "Renesas R-Car I2C Controller"
+ depends on ARCH_SHMOBILE && I2C
+ help
+ If you say yes to this option, support will be included for the
+ R-Car I2C controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-rcar.
+
comment "External I2C/SMBus adapter drivers"
config I2C_DIOLAN_U2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ce3c2be..e98ff51 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
+obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
new file mode 100644
index 0000000..bd8fcf1
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -0,0 +1,715 @@
+/*
+ * drivers/i2c/busses/i2c-rcar.c
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
+ *
+ * This file is based on the drivers/i2c/busses/i2c-sh7760.c
+ * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
+ *
+ * This file used out-of-tree driver i2c-rcar.c
+ * Copyright (C) 2011-2012 Renesas Electronics Corporation
+ *
+ * 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
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c/i2c-rcar.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* register offsets */
+#define ICSCR 0x00 /* slave ctrl */
+#define ICMCR 0x04 /* master ctrl */
+#define ICSSR 0x08 /* slave status */
+#define ICMSR 0x0C /* master status */
+#define ICSIER 0x10 /* slave irq enable */
+#define ICMIER 0x14 /* master irq enable */
+#define ICCCR 0x18 /* clock dividers */
+#define ICSAR 0x1C /* slave address */
+#define ICMAR 0x20 /* master address */
+#define ICRXTX 0x24 /* data port */
+
+/* ICMCR */
+#define MDBS (1 << 7) /* non-fifo mode switch */
+#define FSCL (1 << 6) /* override SCL pin */
+#define FSDA (1 << 5) /* override SDA pin */
+#define OBPC (1 << 4) /* override pins */
+#define MIE (1 << 3) /* master if enable */
+#define TSBE (1 << 2)
+#define FSB (1 << 1) /* force stop bit */
+#define ESG (1 << 0) /* en startbit gen */
+
+/* ICMSR */
+#define MNR (1 << 6) /* nack received */
+#define MAL (1 << 5) /* arbitration lost */
+#define MST (1 << 4) /* sent a stop */
+#define MDE (1 << 3)
+#define MDT (1 << 2)
+#define MDR (1 << 1)
+#define MAT (1 << 0) /* slave addr xfer done */
+
+/* ICMIE */
+#define MNRE (1 << 6) /* nack irq en */
+#define MALE (1 << 5) /* arblos irq en */
+#define MSTE (1 << 4) /* stop irq en */
+#define MDEE (1 << 3)
+#define MDTE (1 << 2)
+#define MDRE (1 << 1)
+#define MATE (1 << 0) /* address sent irq en */
+
+
+enum {
+ RCAR_BUS_PHASE_ADDR,
+ RCAR_BUS_PHASE_DATA,
+ RCAR_BUS_PHASE_STOP,
+};
+
+enum {
+ RCAR_IRQ_CLOSE,
+ RCAR_IRQ_OPEN_FOR_SEND,
+ RCAR_IRQ_OPEN_FOR_RECV,
+ RCAR_IRQ_OPEN_FOR_STOP,
+};
+
+/*
+ * flags
+ */
+#define ID_LAST_MSG (1 << 0)
+#define ID_IOERROR (1 << 1)
+#define ID_DONE (1 << 2)
+#define ID_ARBLOST (1 << 3)
+#define ID_NACK (1 << 4)
+
+struct rcar_i2c_priv {
+ void __iomem *io;
+ struct i2c_adapter adap;
+ struct i2c_msg *msg;
+
+ spinlock_t lock;
+ wait_queue_head_t wait;
+
+ int pos;
+ int irq;
+ u32 icccr;
+ u32 flags;
+};
+
+#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
+#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
+
+#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
+#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
+
+/*
+ * basic functions
+ */
+static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
+{
+ writel(val, priv->io + reg);
+}
+
+static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
+{
+ return readl(priv->io + reg);
+}
+
+static void rcar_i2c_init(struct rcar_i2c_priv *priv)
+{
+ /*
+ * reset slave mode.
+ * slave mode is not used on this driver
+ */
+ rcar_i2c_write(priv, ICSIER, 0);
+ rcar_i2c_write(priv, ICSAR, 0);
+ rcar_i2c_write(priv, ICSCR, 0);
+ rcar_i2c_write(priv, ICSSR, 0);
+
+ /* reset master mode */
+ rcar_i2c_write(priv, ICMIER, 0);
+ rcar_i2c_write(priv, ICMCR, 0);
+ rcar_i2c_write(priv, ICMSR, 0);
+ rcar_i2c_write(priv, ICMAR, 0);
+}
+
+static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
+{
+ u32 val = MNRE | MALE | MSTE | MATE; /* default */
+
+ switch (open) {
+ case RCAR_IRQ_OPEN_FOR_SEND:
+ val |= MDEE; /* default + send */
+ break;
+ case RCAR_IRQ_OPEN_FOR_RECV:
+ val |= MDRE; /* default + read */
+ break;
+ case RCAR_IRQ_OPEN_FOR_STOP:
+ val = MSTE; /* stop irq only */
+ break;
+ case RCAR_IRQ_CLOSE:
+ default:
+ val = 0; /* all close */
+ break;
+ }
+ rcar_i2c_write(priv, ICMIER, val);
+}
+
+static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
+{
+ rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
+}
+
+/*
+ * bus control functions
+ */
+static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < 1024; i++) {
+ /* make sure that bus is not busy */
+ if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
+ return 0;
+ udelay(1);
+ }
+
+ return -EBUSY;
+}
+
+static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
+{
+ switch (phase) {
+ case RCAR_BUS_PHASE_ADDR:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
+ break;
+ case RCAR_BUS_PHASE_DATA:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE);
+ break;
+ case RCAR_BUS_PHASE_STOP:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
+ break;
+ }
+}
+
+/*
+ * clock function
+ */
+static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
+ u32 bus_speed,
+ struct device *dev)
+{
+ struct clk *clkp = clk_get(NULL, "peripheral_clk");
+ u32 scgd, cdf;
+ u32 round, ick;
+ u32 scl;
+
+ if (!clkp) {
+ dev_err(dev, "there is no peripheral_clk\n");
+ return -EIO;
+ }
+
+ /*
+ * calculate SCL clock
+ * see
+ * ICCCR
+ *
+ * ick = clkp / (1 + CDF)
+ * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
+ *
+ * ick : I2C internal clock < 20 MHz
+ * ticf : I2C SCL falling time = 35 ns here
+ * tr : I2C SCL rising time = 200 ns here
+ * intd : LSI internal delay = 50 ns here
+ * clkp : peripheral_clk
+ * F[] : integer up-valuation
+ */
+ for (cdf = 0; cdf < 4; cdf++) {
+ ick = clk_get_rate(clkp) / (1 + cdf);
+ if (ick < 20000000)
+ goto ick_find;
+ }
+ dev_err(dev, "there is no best CDF\n");
+ return -EIO;
+
+ick_find:
+ /*
+ * it is impossible to calculate large scale
+ * number on u32. separate it
+ *
+ * F[(ticf + tr + intd) * ick]
+ * = F[(35 + 200 + 50)ns * ick]
+ * = F[285 * ick / 1000000000]
+ * = F[(ick / 1000000) * 285 / 1000]
+ */
+ round = (ick + 500000) / 1000000 * 285;
+ round = (round + 500) / 1000;
+
+ /*
+ * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
+ *
+ * Calculation result (= SCL) should be less than
+ * bus_speed for hardware safety
+ */
+ for (scgd = 0; scgd < 0x40; scgd++) {
+ scl = ick / (20 + (scgd * 8) + round);
+ if (scl <= bus_speed)
+ goto scgd_find;
+ }
+ dev_err(dev, "it is impossible to calculate best SCL\n");
+ return -EIO;
+
+scgd_find:
+ dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
+ scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
+
+ /*
+ * keep icccr value
+ */
+ priv->icccr = (scgd << 2 | cdf);
+
+ return 0;
+}
+
+static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
+{
+ rcar_i2c_write(priv, ICCCR, priv->icccr);
+}
+
+/*
+ * status functions
+ */
+static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
+{
+ return rcar_i2c_read(priv, ICMSR);
+}
+
+#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
+static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
+{
+ rcar_i2c_write(priv, ICMSR, ~bit);
+}
+
+/*
+ * recv/send functions
+ */
+static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
+{
+ rcar_i2c_set_addr(priv, 1);
+ rcar_i2c_status_clear(priv);
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
+
+ return 0;
+}
+
+static int rcar_i2c_send(struct rcar_i2c_priv *priv)
+{
+ int ret;
+
+ /*
+ * It should check bus status when send case
+ */
+ ret = rcar_i2c_bus_barrier(priv);
+ if (ret < 0)
+ return ret;
+
+ rcar_i2c_set_addr(priv, 0);
+ rcar_i2c_status_clear(priv);
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
+
+ return 0;
+}
+
+#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
+#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
+
+/*
+ * interrupt functions
+ */
+static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ /*
+ * FIXME
+ * sometimes, unknown interrupt happened.
+ * Do nothing
+ */
+ if (!(msr & MDE))
+ return 0;
+
+ /*
+ * If address transfer phase finished,
+ * goto data phase.
+ */
+ if (msr & MAT)
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
+
+ if (priv->pos < msg->len) {
+ /*
+ * Prepare next data to ICRXTX register.
+ * This data will go to _SHIFT_ register.
+ *
+ * *
+ * [ICRXTX] -> [SHIFT] -> [I2C bus]
+ */
+ rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
+ priv->pos++;
+
+ } else {
+ /*
+ * The last data was pushed to ICRXTX on _PREV_ empty irq.
+ * It is on _SHIFT_ register, and will sent to I2C bus.
+ *
+ * *
+ * [ICRXTX] -> [SHIFT] -> [I2C bus]
+ */
+
+ if (priv->flags & ID_LAST_MSG)
+ /*
+ * If current msg is the _LAST_ msg,
+ * prepare stop condition here.
+ * ID_DONE will be set on STOP irq.
+ */
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ else
+ /*
+ * If current msg is _NOT_ last msg,
+ * it doesn't call stop phase.
+ * thus, there is no STOP irq.
+ * return ID_DONE here.
+ */
+ return ID_DONE;
+ }
+
+ rcar_i2c_send_restart(priv);
+
+ return 0;
+}
+
+static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ /*
+ * FIXME
+ * sometimes, unknown interrupt happened.
+ * Do nothing
+ */
+ if (!(msr & MDR))
+ return 0;
+
+ if (msr & MAT) {
+ /*
+ * Address transfer phase finished,
+ * but, there is no data at this point.
+ * Do nothing.
+ */
+ } else if (priv->pos < msg->len) {
+ /*
+ * get received data
+ */
+ msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
+ priv->pos++;
+ }
+
+ /*
+ * If next received data is the last data,
+ * I2C driver should send NACK (or STOP).
+ * But this chip can't send NACK, thus, go to STOP phase.
+ * Otherwise, go to data phase.
+ */
+ if (priv->pos + 1 >= msg->len)
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ else
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
+
+ rcar_i2c_recv_restart(priv);
+
+ return 0;
+}
+
+static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
+{
+ struct rcar_i2c_priv *priv = ptr;
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ u32 msr;
+
+ /*-------------- spin lock -----------------*/
+ spin_lock(&priv->lock);
+
+ msr = rcar_i2c_status_get(priv);
+
+ /*
+ * Arbitration lost
+ */
+ if (msr & MAL) {
+ /*
+ * CAUTION
+ *
+ * When arbitration lost, device become _slave_ mode.
+ */
+ dev_dbg(dev, "Arbitration Lost\n");
+ rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
+ goto out;
+ }
+
+ /*
+ * Stop
+ */
+ if (msr & MST) {
+ dev_dbg(dev, "Stop\n");
+ rcar_i2c_flags_set(priv, ID_DONE);
+ goto out;
+ }
+
+ /*
+ * Nack
+ */
+ if (msr & MNR) {
+ dev_dbg(dev, "Nack\n");
+
+ /* go to stop phase */
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
+ rcar_i2c_flags_set(priv, ID_NACK);
+ goto out;
+ }
+
+ /*
+ * recv/send
+ */
+ if (rcar_i2c_is_recv(priv))
+ rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
+ else
+ rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
+
+out:
+ if (rcar_i2c_flags_has(priv, ID_DONE)) {
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
+ rcar_i2c_status_clear(priv);
+ wake_up(&priv->wait);
+ }
+
+ spin_unlock(&priv->lock);
+ /*-------------- spin unlock -----------------*/
+
+ return IRQ_HANDLED;
+}
+
+static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ unsigned long flags;
+ int i, ret, timeout;
+
+ /*================== enable i2c device ===================*/
+ pm_runtime_get_sync(dev);
+
+ /*-------------- spin lock -----------------*/
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rcar_i2c_init(priv);
+ rcar_i2c_clock_start(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ /*-------------- spin unlock -----------------*/
+
+ ret = -EINVAL;
+ for (i = 0; i < num; i++) {
+ /*-------------- spin lock -----------------*/
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* init each data */
+ priv->msg = &msgs[i];
+ priv->pos = 0;
+ priv->flags = 0;
+ if (priv->msg == &msgs[num - 1])
+ rcar_i2c_flags_set(priv, ID_LAST_MSG);
+
+ /* start send/recv */
+ if (rcar_i2c_is_recv(priv))
+ ret = rcar_i2c_recv(priv);
+ else
+ ret = rcar_i2c_send(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ /*-------------- spin unlock -----------------*/
+
+ if (ret < 0)
+ break;
+
+ /*
+ * wait result
+ */
+ timeout = wait_event_timeout(priv->wait,
+ rcar_i2c_flags_has(priv, ID_DONE),
+ 5 * HZ);
+ if (!timeout) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ /*
+ * error handling
+ */
+ if (rcar_i2c_flags_has(priv, ID_NACK)) {
+ ret = -EREMOTEIO;
+ break;
+ }
+
+ if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
+ ret = -EIO;
+ break;
+ }
+
+ ret = i + 1; /* The number of transfer */
+ }
+
+ pm_runtime_put_sync(dev);
+ /*================== i2c device disabled ==================*/
+
+ if (ret < 0)
+ dev_err(dev, "error %d : %x\n", ret, priv->flags);
+
+ return ret;
+}
+
+static u32 rcar_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm rcar_i2c_algo = {
+ .master_xfer = rcar_i2c_master_xfer,
+ .functionality = rcar_i2c_func,
+};
+
+static int __devinit rcar_i2c_probe(struct platform_device *pdev)
+{
+ struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
+ struct rcar_i2c_priv *priv;
+ struct i2c_adapter *adap;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ u32 bus_speed;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no mmio resources\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(dev, "no mem for private data\n");
+ return -ENOMEM;
+ }
+
+ bus_speed = 100000; /* default 100 kHz */
+ if (pdata && pdata->bus_speed)
+ bus_speed = pdata->bus_speed;
+ ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
+ if (ret < 0)
+ return ret;
+
+ priv->io = devm_ioremap(dev, res->start, resource_size(res));
+ if (!priv->io) {
+ dev_err(dev, "cannot ioremap\n");
+ return -ENODEV;
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ init_waitqueue_head(&priv->wait);
+ spin_lock_init(&priv->lock);
+
+ adap = &priv->adap;
+ adap->nr = pdev->id;
+ adap->algo = &rcar_i2c_algo;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->retries = 3;
+ adap->dev.parent = dev;
+ i2c_set_adapdata(adap, priv);
+ strlcpy(adap->name, pdev->name, sizeof(adap->name));
+
+ if (request_irq(priv->irq, rcar_i2c_irq, 0,
+ dev_name(dev), priv)) {
+ dev_err(dev, "cannot get irq %d\n", priv->irq);
+ return -EBUSY;
+ }
+
+ ret = i2c_add_numbered_adapter(adap);
+ if (ret < 0) {
+ dev_err(dev, "reg adap failed: %d\n", ret);
+ goto error_irq;
+ }
+
+ pm_runtime_enable(dev);
+ platform_set_drvdata(pdev, priv);
+
+ dev_info(dev, "probed\n");
+
+ return 0;
+
+error_irq:
+ free_irq(priv->irq, priv);
+
+ return ret;
+}
+
+static int __devexit rcar_i2c_remove(struct platform_device *pdev)
+{
+ struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ i2c_del_adapter(&priv->adap);
+ free_irq(priv->irq, priv);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static struct platform_driver rcar_i2c_drv = {
+ .driver = {
+ .name = "i2c-rcar",
+ .owner = THIS_MODULE,
+ },
+ .probe = rcar_i2c_probe,
+ .remove = __devexit_p(rcar_i2c_remove),
+};
+
+module_platform_driver(rcar_i2c_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
new file mode 100644
index 0000000..496f5c2
--- /dev/null
+++ b/include/linux/i2c/i2c-rcar.h
@@ -0,0 +1,10 @@
+#ifndef __I2C_R_CAR_H__
+#define __I2C_R_CAR_H__
+
+#include <linux/platform_device.h>
+
+struct i2c_rcar_platform_data {
+ u32 bus_speed;
+};
+
+#endif /* __I2C_R_CAR_H__ */
--
1.7.5.4
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v2] i2c: add Renesas R-Car I2C driver
[not found] ` <874nnnct03.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
@ 2012-09-28 1:57 ` Kuninori Morimoto
[not found] ` <878vbudi3v.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-09-28 5:21 ` Shubhrajyoti Datta
1 sibling, 1 reply; 21+ messages in thread
From: Kuninori Morimoto @ 2012-09-28 1:57 UTC (permalink / raw)
To: Wolfram Sang, Shubhrajyoti Datta, Jean Delvare, Ben Dooks
Cc: Simon, Magnus, Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Please teach me current status of this patch ?
> R-Car I2C is similar with SH7760 I2C.
> But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> This patch creates new Renesas R-Car I2C driver.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> ---
> v1 -> v2
>
> - removed #if 0 function
> - add explanation on rcar_i2c_bus_barrier()
> - removed IGNORE_NAK support
> - rename rcar_i2c_soft_reset() -> rcar_i2c_init()
> - removed devm_kfree/devm_iounmap
> - __raw_writel/readl => writel/readl
> - removed un-needed return from rcar_i2c_bus_phase()
> - tidyup calculation method on rcar_i2c_clock_calculate()
> - tidyup English type
> - tidyup comment to "i2c device disabled"
>
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-rcar.c | 715 +++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c/i2c-rcar.h | 10 +
> 4 files changed, 736 insertions(+), 0 deletions(-)
> create mode 100644 drivers/i2c/busses/i2c-rcar.c
> create mode 100644 include/linux/i2c/i2c-rcar.h
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index b4aaa1b..51baa08 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -709,6 +709,16 @@ config I2C_XLR
> This driver can also be built as a module. If so, the module
> will be called i2c-xlr.
>
> +config I2C_RCAR
> + tristate "Renesas R-Car I2C Controller"
> + depends on ARCH_SHMOBILE && I2C
> + help
> + If you say yes to this option, support will be included for the
> + R-Car I2C controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-rcar.
> +
> comment "External I2C/SMBus adapter drivers"
>
> config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index ce3c2be..e98ff51 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> new file mode 100644
> index 0000000..bd8fcf1
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-rcar.c
> @@ -0,0 +1,715 @@
> +/*
> + * drivers/i2c/busses/i2c-rcar.c
> + *
> + * Copyright (C) 2012 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
> + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
> + *
> + * This file used out-of-tree driver i2c-rcar.c
> + * Copyright (C) 2011-2012 Renesas Electronics Corporation
> + *
> + * 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
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c/i2c-rcar.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +/* register offsets */
> +#define ICSCR 0x00 /* slave ctrl */
> +#define ICMCR 0x04 /* master ctrl */
> +#define ICSSR 0x08 /* slave status */
> +#define ICMSR 0x0C /* master status */
> +#define ICSIER 0x10 /* slave irq enable */
> +#define ICMIER 0x14 /* master irq enable */
> +#define ICCCR 0x18 /* clock dividers */
> +#define ICSAR 0x1C /* slave address */
> +#define ICMAR 0x20 /* master address */
> +#define ICRXTX 0x24 /* data port */
> +
> +/* ICMCR */
> +#define MDBS (1 << 7) /* non-fifo mode switch */
> +#define FSCL (1 << 6) /* override SCL pin */
> +#define FSDA (1 << 5) /* override SDA pin */
> +#define OBPC (1 << 4) /* override pins */
> +#define MIE (1 << 3) /* master if enable */
> +#define TSBE (1 << 2)
> +#define FSB (1 << 1) /* force stop bit */
> +#define ESG (1 << 0) /* en startbit gen */
> +
> +/* ICMSR */
> +#define MNR (1 << 6) /* nack received */
> +#define MAL (1 << 5) /* arbitration lost */
> +#define MST (1 << 4) /* sent a stop */
> +#define MDE (1 << 3)
> +#define MDT (1 << 2)
> +#define MDR (1 << 1)
> +#define MAT (1 << 0) /* slave addr xfer done */
> +
> +/* ICMIE */
> +#define MNRE (1 << 6) /* nack irq en */
> +#define MALE (1 << 5) /* arblos irq en */
> +#define MSTE (1 << 4) /* stop irq en */
> +#define MDEE (1 << 3)
> +#define MDTE (1 << 2)
> +#define MDRE (1 << 1)
> +#define MATE (1 << 0) /* address sent irq en */
> +
> +
> +enum {
> + RCAR_BUS_PHASE_ADDR,
> + RCAR_BUS_PHASE_DATA,
> + RCAR_BUS_PHASE_STOP,
> +};
> +
> +enum {
> + RCAR_IRQ_CLOSE,
> + RCAR_IRQ_OPEN_FOR_SEND,
> + RCAR_IRQ_OPEN_FOR_RECV,
> + RCAR_IRQ_OPEN_FOR_STOP,
> +};
> +
> +/*
> + * flags
> + */
> +#define ID_LAST_MSG (1 << 0)
> +#define ID_IOERROR (1 << 1)
> +#define ID_DONE (1 << 2)
> +#define ID_ARBLOST (1 << 3)
> +#define ID_NACK (1 << 4)
> +
> +struct rcar_i2c_priv {
> + void __iomem *io;
> + struct i2c_adapter adap;
> + struct i2c_msg *msg;
> +
> + spinlock_t lock;
> + wait_queue_head_t wait;
> +
> + int pos;
> + int irq;
> + u32 icccr;
> + u32 flags;
> +};
> +
> +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
> +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
> +
> +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
> +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
> +
> +/*
> + * basic functions
> + */
> +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> +{
> + writel(val, priv->io + reg);
> +}
> +
> +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> +{
> + return readl(priv->io + reg);
> +}
> +
> +static void rcar_i2c_init(struct rcar_i2c_priv *priv)
> +{
> + /*
> + * reset slave mode.
> + * slave mode is not used on this driver
> + */
> + rcar_i2c_write(priv, ICSIER, 0);
> + rcar_i2c_write(priv, ICSAR, 0);
> + rcar_i2c_write(priv, ICSCR, 0);
> + rcar_i2c_write(priv, ICSSR, 0);
> +
> + /* reset master mode */
> + rcar_i2c_write(priv, ICMIER, 0);
> + rcar_i2c_write(priv, ICMCR, 0);
> + rcar_i2c_write(priv, ICMSR, 0);
> + rcar_i2c_write(priv, ICMAR, 0);
> +}
> +
> +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
> +{
> + u32 val = MNRE | MALE | MSTE | MATE; /* default */
> +
> + switch (open) {
> + case RCAR_IRQ_OPEN_FOR_SEND:
> + val |= MDEE; /* default + send */
> + break;
> + case RCAR_IRQ_OPEN_FOR_RECV:
> + val |= MDRE; /* default + read */
> + break;
> + case RCAR_IRQ_OPEN_FOR_STOP:
> + val = MSTE; /* stop irq only */
> + break;
> + case RCAR_IRQ_CLOSE:
> + default:
> + val = 0; /* all close */
> + break;
> + }
> + rcar_i2c_write(priv, ICMIER, val);
> +}
> +
> +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
> +{
> + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
> +}
> +
> +/*
> + * bus control functions
> + */
> +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
> +{
> + int i;
> +
> + for (i = 0; i < 1024; i++) {
> + /* make sure that bus is not busy */
> + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
> + return 0;
> + udelay(1);
> + }
> +
> + return -EBUSY;
> +}
> +
> +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> +{
> + switch (phase) {
> + case RCAR_BUS_PHASE_ADDR:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> + break;
> + case RCAR_BUS_PHASE_DATA:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> + break;
> + case RCAR_BUS_PHASE_STOP:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> + break;
> + }
> +}
> +
> +/*
> + * clock function
> + */
> +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> + u32 bus_speed,
> + struct device *dev)
> +{
> + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> + u32 scgd, cdf;
> + u32 round, ick;
> + u32 scl;
> +
> + if (!clkp) {
> + dev_err(dev, "there is no peripheral_clk\n");
> + return -EIO;
> + }
> +
> + /*
> + * calculate SCL clock
> + * see
> + * ICCCR
> + *
> + * ick = clkp / (1 + CDF)
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + *
> + * ick : I2C internal clock < 20 MHz
> + * ticf : I2C SCL falling time = 35 ns here
> + * tr : I2C SCL rising time = 200 ns here
> + * intd : LSI internal delay = 50 ns here
> + * clkp : peripheral_clk
> + * F[] : integer up-valuation
> + */
> + for (cdf = 0; cdf < 4; cdf++) {
> + ick = clk_get_rate(clkp) / (1 + cdf);
> + if (ick < 20000000)
> + goto ick_find;
> + }
> + dev_err(dev, "there is no best CDF\n");
> + return -EIO;
> +
> +ick_find:
> + /*
> + * it is impossible to calculate large scale
> + * number on u32. separate it
> + *
> + * F[(ticf + tr + intd) * ick]
> + * = F[(35 + 200 + 50)ns * ick]
> + * = F[285 * ick / 1000000000]
> + * = F[(ick / 1000000) * 285 / 1000]
> + */
> + round = (ick + 500000) / 1000000 * 285;
> + round = (round + 500) / 1000;
> +
> + /*
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + *
> + * Calculation result (= SCL) should be less than
> + * bus_speed for hardware safety
> + */
> + for (scgd = 0; scgd < 0x40; scgd++) {
> + scl = ick / (20 + (scgd * 8) + round);
> + if (scl <= bus_speed)
> + goto scgd_find;
> + }
> + dev_err(dev, "it is impossible to calculate best SCL\n");
> + return -EIO;
> +
> +scgd_find:
> + dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
> + scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
> +
> + /*
> + * keep icccr value
> + */
> + priv->icccr = (scgd << 2 | cdf);
> +
> + return 0;
> +}
> +
> +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_write(priv, ICCCR, priv->icccr);
> +}
> +
> +/*
> + * status functions
> + */
> +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
> +{
> + return rcar_i2c_read(priv, ICMSR);
> +}
> +
> +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
> +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
> +{
> + rcar_i2c_write(priv, ICMSR, ~bit);
> +}
> +
> +/*
> + * recv/send functions
> + */
> +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_set_addr(priv, 1);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
> +{
> + int ret;
> +
> + /*
> + * It should check bus status when send case
> + */
> + ret = rcar_i2c_bus_barrier(priv);
> + if (ret < 0)
> + return ret;
> +
> + rcar_i2c_set_addr(priv, 0);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
> +
> + return 0;
> +}
> +
> +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
> +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
> +
> +/*
> + * interrupt functions
> + */
> +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happened.
> + * Do nothing
> + */
> + if (!(msr & MDE))
> + return 0;
> +
> + /*
> + * If address transfer phase finished,
> + * goto data phase.
> + */
> + if (msr & MAT)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + if (priv->pos < msg->len) {
> + /*
> + * Prepare next data to ICRXTX register.
> + * This data will go to _SHIFT_ register.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
> + priv->pos++;
> +
> + } else {
> + /*
> + * The last data was pushed to ICRXTX on _PREV_ empty irq.
> + * It is on _SHIFT_ register, and will sent to I2C bus.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> +
> + if (priv->flags & ID_LAST_MSG)
> + /*
> + * If current msg is the _LAST_ msg,
> + * prepare stop condition here.
> + * ID_DONE will be set on STOP irq.
> + */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + /*
> + * If current msg is _NOT_ last msg,
> + * it doesn't call stop phase.
> + * thus, there is no STOP irq.
> + * return ID_DONE here.
> + */
> + return ID_DONE;
> + }
> +
> + rcar_i2c_send_restart(priv);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happened.
> + * Do nothing
> + */
> + if (!(msr & MDR))
> + return 0;
> +
> + if (msr & MAT) {
> + /*
> + * Address transfer phase finished,
> + * but, there is no data at this point.
> + * Do nothing.
> + */
> + } else if (priv->pos < msg->len) {
> + /*
> + * get received data
> + */
> + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
> + priv->pos++;
> + }
> +
> + /*
> + * If next received data is the last data,
> + * I2C driver should send NACK (or STOP).
> + * But this chip can't send NACK, thus, go to STOP phase.
> + * Otherwise, go to data phase.
> + */
> + if (priv->pos + 1 >= msg->len)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + rcar_i2c_recv_restart(priv);
> +
> + return 0;
> +}
> +
> +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
> +{
> + struct rcar_i2c_priv *priv = ptr;
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + u32 msr;
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock(&priv->lock);
> +
> + msr = rcar_i2c_status_get(priv);
> +
> + /*
> + * Arbitration lost
> + */
> + if (msr & MAL) {
> + /*
> + * CAUTION
> + *
> + * When arbitration lost, device become _slave_ mode.
> + */
> + dev_dbg(dev, "Arbitration Lost\n");
> + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
> + goto out;
> + }
> +
> + /*
> + * Stop
> + */
> + if (msr & MST) {
> + dev_dbg(dev, "Stop\n");
> + rcar_i2c_flags_set(priv, ID_DONE);
> + goto out;
> + }
> +
> + /*
> + * Nack
> + */
> + if (msr & MNR) {
> + dev_dbg(dev, "Nack\n");
> +
> + /* go to stop phase */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
> + rcar_i2c_flags_set(priv, ID_NACK);
> + goto out;
> + }
> +
> + /*
> + * recv/send
> + */
> + if (rcar_i2c_is_recv(priv))
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
> + else
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
> +
> +out:
> + if (rcar_i2c_flags_has(priv, ID_DONE)) {
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
> + rcar_i2c_status_clear(priv);
> + wake_up(&priv->wait);
> + }
> +
> + spin_unlock(&priv->lock);
> + /*-------------- spin unlock -----------------*/
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs,
> + int num)
> +{
> + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + unsigned long flags;
> + int i, ret, timeout;
> +
> + /*================== enable i2c device ===================*/
> + pm_runtime_get_sync(dev);
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + rcar_i2c_init(priv);
> + rcar_i2c_clock_start(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + ret = -EINVAL;
> + for (i = 0; i < num; i++) {
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + /* init each data */
> + priv->msg = &msgs[i];
> + priv->pos = 0;
> + priv->flags = 0;
> + if (priv->msg == &msgs[num - 1])
> + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> +
> + /* start send/recv */
> + if (rcar_i2c_is_recv(priv))
> + ret = rcar_i2c_recv(priv);
> + else
> + ret = rcar_i2c_send(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + if (ret < 0)
> + break;
> +
> + /*
> + * wait result
> + */
> + timeout = wait_event_timeout(priv->wait,
> + rcar_i2c_flags_has(priv, ID_DONE),
> + 5 * HZ);
> + if (!timeout) {
> + ret = -ETIMEDOUT;
> + break;
> + }
> +
> + /*
> + * error handling
> + */
> + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> + ret = -EREMOTEIO;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> + ret = -EAGAIN;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> + ret = -EIO;
> + break;
> + }
> +
> + ret = i + 1; /* The number of transfer */
> + }
> +
> + pm_runtime_put_sync(dev);
> + /*================== i2c device disabled ==================*/
> +
> + if (ret < 0)
> + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> +
> + return ret;
> +}
> +
> +static u32 rcar_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm rcar_i2c_algo = {
> + .master_xfer = rcar_i2c_master_xfer,
> + .functionality = rcar_i2c_func,
> +};
> +
> +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> +{
> + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> + struct rcar_i2c_priv *priv;
> + struct i2c_adapter *adap;
> + struct resource *res;
> + struct device *dev = &pdev->dev;
> + u32 bus_speed;
> + int ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "no mmio resources\n");
> + return -ENODEV;
> + }
> +
> + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(dev, "no mem for private data\n");
> + return -ENOMEM;
> + }
> +
> + bus_speed = 100000; /* default 100 kHz */
> + if (pdata && pdata->bus_speed)
> + bus_speed = pdata->bus_speed;
> + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> + if (ret < 0)
> + return ret;
> +
> + priv->io = devm_ioremap(dev, res->start, resource_size(res));
> + if (!priv->io) {
> + dev_err(dev, "cannot ioremap\n");
> + return -ENODEV;
> + }
> +
> + priv->irq = platform_get_irq(pdev, 0);
> + init_waitqueue_head(&priv->wait);
> + spin_lock_init(&priv->lock);
> +
> + adap = &priv->adap;
> + adap->nr = pdev->id;
> + adap->algo = &rcar_i2c_algo;
> + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> + adap->retries = 3;
> + adap->dev.parent = dev;
> + i2c_set_adapdata(adap, priv);
> + strlcpy(adap->name, pdev->name, sizeof(adap->name));
> +
> + if (request_irq(priv->irq, rcar_i2c_irq, 0,
> + dev_name(dev), priv)) {
> + dev_err(dev, "cannot get irq %d\n", priv->irq);
> + return -EBUSY;
> + }
> +
> + ret = i2c_add_numbered_adapter(adap);
> + if (ret < 0) {
> + dev_err(dev, "reg adap failed: %d\n", ret);
> + goto error_irq;
> + }
> +
> + pm_runtime_enable(dev);
> + platform_set_drvdata(pdev, priv);
> +
> + dev_info(dev, "probed\n");
> +
> + return 0;
> +
> +error_irq:
> + free_irq(priv->irq, priv);
> +
> + return ret;
> +}
> +
> +static int __devexit rcar_i2c_remove(struct platform_device *pdev)
> +{
> + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> +
> + i2c_del_adapter(&priv->adap);
> + free_irq(priv->irq, priv);
> + pm_runtime_disable(dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver rcar_i2c_drv = {
> + .driver = {
> + .name = "i2c-rcar",
> + .owner = THIS_MODULE,
> + },
> + .probe = rcar_i2c_probe,
> + .remove = __devexit_p(rcar_i2c_remove),
> +};
> +
> +module_platform_driver(rcar_i2c_drv);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
> +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
> diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
> new file mode 100644
> index 0000000..496f5c2
> --- /dev/null
> +++ b/include/linux/i2c/i2c-rcar.h
> @@ -0,0 +1,10 @@
> +#ifndef __I2C_R_CAR_H__
> +#define __I2C_R_CAR_H__
> +
> +#include <linux/platform_device.h>
> +
> +struct i2c_rcar_platform_data {
> + u32 bus_speed;
> +};
> +
> +#endif /* __I2C_R_CAR_H__ */
> --
> 1.7.5.4
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2] i2c: add Renesas R-Car I2C driver
[not found] ` <878vbudi3v.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
@ 2012-09-28 3:49 ` Simon Horman
0 siblings, 0 replies; 21+ messages in thread
From: Simon Horman @ 2012-09-28 3:49 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Wolfram Sang, Shubhrajyoti Datta, Jean Delvare, Ben Dooks, Magnus,
Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Hi Morimoto-san, Hi Wolfram, Hi All,
I'm not aware of any objections to this patch from the shmobile side
of things, so to that end:
Acked-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
Wolfram, would you consider merging this change?
On Thu, Sep 27, 2012 at 06:57:47PM -0700, Kuninori Morimoto wrote:
>
> Please teach me current status of this patch ?
>
> > R-Car I2C is similar with SH7760 I2C.
> > But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> > Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> > This patch creates new Renesas R-Car I2C driver.
> >
> > Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> > ---
> > v1 -> v2
> >
> > - removed #if 0 function
> > - add explanation on rcar_i2c_bus_barrier()
> > - removed IGNORE_NAK support
> > - rename rcar_i2c_soft_reset() -> rcar_i2c_init()
> > - removed devm_kfree/devm_iounmap
> > - __raw_writel/readl => writel/readl
> > - removed un-needed return from rcar_i2c_bus_phase()
> > - tidyup calculation method on rcar_i2c_clock_calculate()
> > - tidyup English type
> > - tidyup comment to "i2c device disabled"
> >
> > drivers/i2c/busses/Kconfig | 10 +
> > drivers/i2c/busses/Makefile | 1 +
> > drivers/i2c/busses/i2c-rcar.c | 715 +++++++++++++++++++++++++++++++++++++++++
> > include/linux/i2c/i2c-rcar.h | 10 +
> > 4 files changed, 736 insertions(+), 0 deletions(-)
> > create mode 100644 drivers/i2c/busses/i2c-rcar.c
> > create mode 100644 include/linux/i2c/i2c-rcar.h
> >
> > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> > index b4aaa1b..51baa08 100644
> > --- a/drivers/i2c/busses/Kconfig
> > +++ b/drivers/i2c/busses/Kconfig
> > @@ -709,6 +709,16 @@ config I2C_XLR
> > This driver can also be built as a module. If so, the module
> > will be called i2c-xlr.
> >
> > +config I2C_RCAR
> > + tristate "Renesas R-Car I2C Controller"
> > + depends on ARCH_SHMOBILE && I2C
> > + help
> > + If you say yes to this option, support will be included for the
> > + R-Car I2C controller.
> > +
> > + This driver can also be built as a module. If so, the module
> > + will be called i2c-rcar.
> > +
> > comment "External I2C/SMBus adapter drivers"
> >
> > config I2C_DIOLAN_U2C
> > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> > index ce3c2be..e98ff51 100644
> > --- a/drivers/i2c/busses/Makefile
> > +++ b/drivers/i2c/busses/Makefile
> > @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> > obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> > obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> > obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> > +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
> >
> > # External I2C/SMBus adapter drivers
> > obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> > diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> > new file mode 100644
> > index 0000000..bd8fcf1
> > --- /dev/null
> > +++ b/drivers/i2c/busses/i2c-rcar.c
> > @@ -0,0 +1,715 @@
> > +/*
> > + * drivers/i2c/busses/i2c-rcar.c
> > + *
> > + * Copyright (C) 2012 Renesas Solutions Corp.
> > + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> > + *
> > + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
> > + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
> > + *
> > + * This file used out-of-tree driver i2c-rcar.c
> > + * Copyright (C) 2011-2012 Renesas Electronics Corporation
> > + *
> > + * 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
> > + *
> > + * 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.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> > + */
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/err.h>
> > +#include <linux/init.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/i2c.h>
> > +#include <linux/i2c/i2c-rcar.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +
> > +/* register offsets */
> > +#define ICSCR 0x00 /* slave ctrl */
> > +#define ICMCR 0x04 /* master ctrl */
> > +#define ICSSR 0x08 /* slave status */
> > +#define ICMSR 0x0C /* master status */
> > +#define ICSIER 0x10 /* slave irq enable */
> > +#define ICMIER 0x14 /* master irq enable */
> > +#define ICCCR 0x18 /* clock dividers */
> > +#define ICSAR 0x1C /* slave address */
> > +#define ICMAR 0x20 /* master address */
> > +#define ICRXTX 0x24 /* data port */
> > +
> > +/* ICMCR */
> > +#define MDBS (1 << 7) /* non-fifo mode switch */
> > +#define FSCL (1 << 6) /* override SCL pin */
> > +#define FSDA (1 << 5) /* override SDA pin */
> > +#define OBPC (1 << 4) /* override pins */
> > +#define MIE (1 << 3) /* master if enable */
> > +#define TSBE (1 << 2)
> > +#define FSB (1 << 1) /* force stop bit */
> > +#define ESG (1 << 0) /* en startbit gen */
> > +
> > +/* ICMSR */
> > +#define MNR (1 << 6) /* nack received */
> > +#define MAL (1 << 5) /* arbitration lost */
> > +#define MST (1 << 4) /* sent a stop */
> > +#define MDE (1 << 3)
> > +#define MDT (1 << 2)
> > +#define MDR (1 << 1)
> > +#define MAT (1 << 0) /* slave addr xfer done */
> > +
> > +/* ICMIE */
> > +#define MNRE (1 << 6) /* nack irq en */
> > +#define MALE (1 << 5) /* arblos irq en */
> > +#define MSTE (1 << 4) /* stop irq en */
> > +#define MDEE (1 << 3)
> > +#define MDTE (1 << 2)
> > +#define MDRE (1 << 1)
> > +#define MATE (1 << 0) /* address sent irq en */
> > +
> > +
> > +enum {
> > + RCAR_BUS_PHASE_ADDR,
> > + RCAR_BUS_PHASE_DATA,
> > + RCAR_BUS_PHASE_STOP,
> > +};
> > +
> > +enum {
> > + RCAR_IRQ_CLOSE,
> > + RCAR_IRQ_OPEN_FOR_SEND,
> > + RCAR_IRQ_OPEN_FOR_RECV,
> > + RCAR_IRQ_OPEN_FOR_STOP,
> > +};
> > +
> > +/*
> > + * flags
> > + */
> > +#define ID_LAST_MSG (1 << 0)
> > +#define ID_IOERROR (1 << 1)
> > +#define ID_DONE (1 << 2)
> > +#define ID_ARBLOST (1 << 3)
> > +#define ID_NACK (1 << 4)
> > +
> > +struct rcar_i2c_priv {
> > + void __iomem *io;
> > + struct i2c_adapter adap;
> > + struct i2c_msg *msg;
> > +
> > + spinlock_t lock;
> > + wait_queue_head_t wait;
> > +
> > + int pos;
> > + int irq;
> > + u32 icccr;
> > + u32 flags;
> > +};
> > +
> > +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
> > +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
> > +
> > +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
> > +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
> > +
> > +/*
> > + * basic functions
> > + */
> > +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> > +{
> > + writel(val, priv->io + reg);
> > +}
> > +
> > +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> > +{
> > + return readl(priv->io + reg);
> > +}
> > +
> > +static void rcar_i2c_init(struct rcar_i2c_priv *priv)
> > +{
> > + /*
> > + * reset slave mode.
> > + * slave mode is not used on this driver
> > + */
> > + rcar_i2c_write(priv, ICSIER, 0);
> > + rcar_i2c_write(priv, ICSAR, 0);
> > + rcar_i2c_write(priv, ICSCR, 0);
> > + rcar_i2c_write(priv, ICSSR, 0);
> > +
> > + /* reset master mode */
> > + rcar_i2c_write(priv, ICMIER, 0);
> > + rcar_i2c_write(priv, ICMCR, 0);
> > + rcar_i2c_write(priv, ICMSR, 0);
> > + rcar_i2c_write(priv, ICMAR, 0);
> > +}
> > +
> > +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
> > +{
> > + u32 val = MNRE | MALE | MSTE | MATE; /* default */
> > +
> > + switch (open) {
> > + case RCAR_IRQ_OPEN_FOR_SEND:
> > + val |= MDEE; /* default + send */
> > + break;
> > + case RCAR_IRQ_OPEN_FOR_RECV:
> > + val |= MDRE; /* default + read */
> > + break;
> > + case RCAR_IRQ_OPEN_FOR_STOP:
> > + val = MSTE; /* stop irq only */
> > + break;
> > + case RCAR_IRQ_CLOSE:
> > + default:
> > + val = 0; /* all close */
> > + break;
> > + }
> > + rcar_i2c_write(priv, ICMIER, val);
> > +}
> > +
> > +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
> > +{
> > + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
> > +}
> > +
> > +/*
> > + * bus control functions
> > + */
> > +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < 1024; i++) {
> > + /* make sure that bus is not busy */
> > + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
> > + return 0;
> > + udelay(1);
> > + }
> > +
> > + return -EBUSY;
> > +}
> > +
> > +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> > +{
> > + switch (phase) {
> > + case RCAR_BUS_PHASE_ADDR:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> > + break;
> > + case RCAR_BUS_PHASE_DATA:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> > + break;
> > + case RCAR_BUS_PHASE_STOP:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> > + break;
> > + }
> > +}
> > +
> > +/*
> > + * clock function
> > + */
> > +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> > + u32 bus_speed,
> > + struct device *dev)
> > +{
> > + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> > + u32 scgd, cdf;
> > + u32 round, ick;
> > + u32 scl;
> > +
> > + if (!clkp) {
> > + dev_err(dev, "there is no peripheral_clk\n");
> > + return -EIO;
> > + }
> > +
> > + /*
> > + * calculate SCL clock
> > + * see
> > + * ICCCR
> > + *
> > + * ick = clkp / (1 + CDF)
> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> > + *
> > + * ick : I2C internal clock < 20 MHz
> > + * ticf : I2C SCL falling time = 35 ns here
> > + * tr : I2C SCL rising time = 200 ns here
> > + * intd : LSI internal delay = 50 ns here
> > + * clkp : peripheral_clk
> > + * F[] : integer up-valuation
> > + */
> > + for (cdf = 0; cdf < 4; cdf++) {
> > + ick = clk_get_rate(clkp) / (1 + cdf);
> > + if (ick < 20000000)
> > + goto ick_find;
> > + }
> > + dev_err(dev, "there is no best CDF\n");
> > + return -EIO;
> > +
> > +ick_find:
> > + /*
> > + * it is impossible to calculate large scale
> > + * number on u32. separate it
> > + *
> > + * F[(ticf + tr + intd) * ick]
> > + * = F[(35 + 200 + 50)ns * ick]
> > + * = F[285 * ick / 1000000000]
> > + * = F[(ick / 1000000) * 285 / 1000]
> > + */
> > + round = (ick + 500000) / 1000000 * 285;
> > + round = (round + 500) / 1000;
> > +
> > + /*
> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> > + *
> > + * Calculation result (= SCL) should be less than
> > + * bus_speed for hardware safety
> > + */
> > + for (scgd = 0; scgd < 0x40; scgd++) {
> > + scl = ick / (20 + (scgd * 8) + round);
> > + if (scl <= bus_speed)
> > + goto scgd_find;
> > + }
> > + dev_err(dev, "it is impossible to calculate best SCL\n");
> > + return -EIO;
> > +
> > +scgd_find:
> > + dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
> > + scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
> > +
> > + /*
> > + * keep icccr value
> > + */
> > + priv->icccr = (scgd << 2 | cdf);
> > +
> > + return 0;
> > +}
> > +
> > +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
> > +{
> > + rcar_i2c_write(priv, ICCCR, priv->icccr);
> > +}
> > +
> > +/*
> > + * status functions
> > + */
> > +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
> > +{
> > + return rcar_i2c_read(priv, ICMSR);
> > +}
> > +
> > +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
> > +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
> > +{
> > + rcar_i2c_write(priv, ICMSR, ~bit);
> > +}
> > +
> > +/*
> > + * recv/send functions
> > + */
> > +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
> > +{
> > + rcar_i2c_set_addr(priv, 1);
> > + rcar_i2c_status_clear(priv);
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
> > +{
> > + int ret;
> > +
> > + /*
> > + * It should check bus status when send case
> > + */
> > + ret = rcar_i2c_bus_barrier(priv);
> > + if (ret < 0)
> > + return ret;
> > +
> > + rcar_i2c_set_addr(priv, 0);
> > + rcar_i2c_status_clear(priv);
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
> > +
> > + return 0;
> > +}
> > +
> > +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
> > +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
> > +
> > +/*
> > + * interrupt functions
> > + */
> > +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> > +{
> > + struct i2c_msg *msg = priv->msg;
> > +
> > + /*
> > + * FIXME
> > + * sometimes, unknown interrupt happened.
> > + * Do nothing
> > + */
> > + if (!(msr & MDE))
> > + return 0;
> > +
> > + /*
> > + * If address transfer phase finished,
> > + * goto data phase.
> > + */
> > + if (msr & MAT)
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> > +
> > + if (priv->pos < msg->len) {
> > + /*
> > + * Prepare next data to ICRXTX register.
> > + * This data will go to _SHIFT_ register.
> > + *
> > + * *
> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> > + */
> > + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
> > + priv->pos++;
> > +
> > + } else {
> > + /*
> > + * The last data was pushed to ICRXTX on _PREV_ empty irq.
> > + * It is on _SHIFT_ register, and will sent to I2C bus.
> > + *
> > + * *
> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> > + */
> > +
> > + if (priv->flags & ID_LAST_MSG)
> > + /*
> > + * If current msg is the _LAST_ msg,
> > + * prepare stop condition here.
> > + * ID_DONE will be set on STOP irq.
> > + */
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + else
> > + /*
> > + * If current msg is _NOT_ last msg,
> > + * it doesn't call stop phase.
> > + * thus, there is no STOP irq.
> > + * return ID_DONE here.
> > + */
> > + return ID_DONE;
> > + }
> > +
> > + rcar_i2c_send_restart(priv);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> > +{
> > + struct i2c_msg *msg = priv->msg;
> > +
> > + /*
> > + * FIXME
> > + * sometimes, unknown interrupt happened.
> > + * Do nothing
> > + */
> > + if (!(msr & MDR))
> > + return 0;
> > +
> > + if (msr & MAT) {
> > + /*
> > + * Address transfer phase finished,
> > + * but, there is no data at this point.
> > + * Do nothing.
> > + */
> > + } else if (priv->pos < msg->len) {
> > + /*
> > + * get received data
> > + */
> > + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
> > + priv->pos++;
> > + }
> > +
> > + /*
> > + * If next received data is the last data,
> > + * I2C driver should send NACK (or STOP).
> > + * But this chip can't send NACK, thus, go to STOP phase.
> > + * Otherwise, go to data phase.
> > + */
> > + if (priv->pos + 1 >= msg->len)
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + else
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> > +
> > + rcar_i2c_recv_restart(priv);
> > +
> > + return 0;
> > +}
> > +
> > +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
> > +{
> > + struct rcar_i2c_priv *priv = ptr;
> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
> > + u32 msr;
> > +
> > + /*-------------- spin lock -----------------*/
> > + spin_lock(&priv->lock);
> > +
> > + msr = rcar_i2c_status_get(priv);
> > +
> > + /*
> > + * Arbitration lost
> > + */
> > + if (msr & MAL) {
> > + /*
> > + * CAUTION
> > + *
> > + * When arbitration lost, device become _slave_ mode.
> > + */
> > + dev_dbg(dev, "Arbitration Lost\n");
> > + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
> > + goto out;
> > + }
> > +
> > + /*
> > + * Stop
> > + */
> > + if (msr & MST) {
> > + dev_dbg(dev, "Stop\n");
> > + rcar_i2c_flags_set(priv, ID_DONE);
> > + goto out;
> > + }
> > +
> > + /*
> > + * Nack
> > + */
> > + if (msr & MNR) {
> > + dev_dbg(dev, "Nack\n");
> > +
> > + /* go to stop phase */
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
> > + rcar_i2c_flags_set(priv, ID_NACK);
> > + goto out;
> > + }
> > +
> > + /*
> > + * recv/send
> > + */
> > + if (rcar_i2c_is_recv(priv))
> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
> > + else
> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
> > +
> > +out:
> > + if (rcar_i2c_flags_has(priv, ID_DONE)) {
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
> > + rcar_i2c_status_clear(priv);
> > + wake_up(&priv->wait);
> > + }
> > +
> > + spin_unlock(&priv->lock);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> > + struct i2c_msg *msgs,
> > + int num)
> > +{
> > + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
> > + unsigned long flags;
> > + int i, ret, timeout;
> > +
> > + /*================== enable i2c device ===================*/
> > + pm_runtime_get_sync(dev);
> > +
> > + /*-------------- spin lock -----------------*/
> > + spin_lock_irqsave(&priv->lock, flags);
> > +
> > + rcar_i2c_init(priv);
> > + rcar_i2c_clock_start(priv);
> > +
> > + spin_unlock_irqrestore(&priv->lock, flags);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + ret = -EINVAL;
> > + for (i = 0; i < num; i++) {
> > + /*-------------- spin lock -----------------*/
> > + spin_lock_irqsave(&priv->lock, flags);
> > +
> > + /* init each data */
> > + priv->msg = &msgs[i];
> > + priv->pos = 0;
> > + priv->flags = 0;
> > + if (priv->msg == &msgs[num - 1])
> > + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> > +
> > + /* start send/recv */
> > + if (rcar_i2c_is_recv(priv))
> > + ret = rcar_i2c_recv(priv);
> > + else
> > + ret = rcar_i2c_send(priv);
> > +
> > + spin_unlock_irqrestore(&priv->lock, flags);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + if (ret < 0)
> > + break;
> > +
> > + /*
> > + * wait result
> > + */
> > + timeout = wait_event_timeout(priv->wait,
> > + rcar_i2c_flags_has(priv, ID_DONE),
> > + 5 * HZ);
> > + if (!timeout) {
> > + ret = -ETIMEDOUT;
> > + break;
> > + }
> > +
> > + /*
> > + * error handling
> > + */
> > + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> > + ret = -EREMOTEIO;
> > + break;
> > + }
> > +
> > + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> > + ret = -EAGAIN;
> > + break;
> > + }
> > +
> > + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> > + ret = -EIO;
> > + break;
> > + }
> > +
> > + ret = i + 1; /* The number of transfer */
> > + }
> > +
> > + pm_runtime_put_sync(dev);
> > + /*================== i2c device disabled ==================*/
> > +
> > + if (ret < 0)
> > + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> > +
> > + return ret;
> > +}
> > +
> > +static u32 rcar_i2c_func(struct i2c_adapter *adap)
> > +{
> > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> > +}
> > +
> > +static const struct i2c_algorithm rcar_i2c_algo = {
> > + .master_xfer = rcar_i2c_master_xfer,
> > + .functionality = rcar_i2c_func,
> > +};
> > +
> > +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> > +{
> > + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> > + struct rcar_i2c_priv *priv;
> > + struct i2c_adapter *adap;
> > + struct resource *res;
> > + struct device *dev = &pdev->dev;
> > + u32 bus_speed;
> > + int ret;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + if (!res) {
> > + dev_err(dev, "no mmio resources\n");
> > + return -ENODEV;
> > + }
> > +
> > + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> > + if (!priv) {
> > + dev_err(dev, "no mem for private data\n");
> > + return -ENOMEM;
> > + }
> > +
> > + bus_speed = 100000; /* default 100 kHz */
> > + if (pdata && pdata->bus_speed)
> > + bus_speed = pdata->bus_speed;
> > + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> > + if (ret < 0)
> > + return ret;
> > +
> > + priv->io = devm_ioremap(dev, res->start, resource_size(res));
> > + if (!priv->io) {
> > + dev_err(dev, "cannot ioremap\n");
> > + return -ENODEV;
> > + }
> > +
> > + priv->irq = platform_get_irq(pdev, 0);
> > + init_waitqueue_head(&priv->wait);
> > + spin_lock_init(&priv->lock);
> > +
> > + adap = &priv->adap;
> > + adap->nr = pdev->id;
> > + adap->algo = &rcar_i2c_algo;
> > + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> > + adap->retries = 3;
> > + adap->dev.parent = dev;
> > + i2c_set_adapdata(adap, priv);
> > + strlcpy(adap->name, pdev->name, sizeof(adap->name));
> > +
> > + if (request_irq(priv->irq, rcar_i2c_irq, 0,
> > + dev_name(dev), priv)) {
> > + dev_err(dev, "cannot get irq %d\n", priv->irq);
> > + return -EBUSY;
> > + }
> > +
> > + ret = i2c_add_numbered_adapter(adap);
> > + if (ret < 0) {
> > + dev_err(dev, "reg adap failed: %d\n", ret);
> > + goto error_irq;
> > + }
> > +
> > + pm_runtime_enable(dev);
> > + platform_set_drvdata(pdev, priv);
> > +
> > + dev_info(dev, "probed\n");
> > +
> > + return 0;
> > +
> > +error_irq:
> > + free_irq(priv->irq, priv);
> > +
> > + return ret;
> > +}
> > +
> > +static int __devexit rcar_i2c_remove(struct platform_device *pdev)
> > +{
> > + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
> > + struct device *dev = &pdev->dev;
> > +
> > + i2c_del_adapter(&priv->adap);
> > + free_irq(priv->irq, priv);
> > + pm_runtime_disable(dev);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver rcar_i2c_drv = {
> > + .driver = {
> > + .name = "i2c-rcar",
> > + .owner = THIS_MODULE,
> > + },
> > + .probe = rcar_i2c_probe,
> > + .remove = __devexit_p(rcar_i2c_remove),
> > +};
> > +
> > +module_platform_driver(rcar_i2c_drv);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
> > +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
> > diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
> > new file mode 100644
> > index 0000000..496f5c2
> > --- /dev/null
> > +++ b/include/linux/i2c/i2c-rcar.h
> > @@ -0,0 +1,10 @@
> > +#ifndef __I2C_R_CAR_H__
> > +#define __I2C_R_CAR_H__
> > +
> > +#include <linux/platform_device.h>
> > +
> > +struct i2c_rcar_platform_data {
> > + u32 bus_speed;
> > +};
> > +
> > +#endif /* __I2C_R_CAR_H__ */
> > --
> > 1.7.5.4
> >
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2] i2c: add Renesas R-Car I2C driver
[not found] ` <874nnnct03.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-09-28 1:57 ` Kuninori Morimoto
@ 2012-09-28 5:21 ` Shubhrajyoti Datta
[not found] ` <CAM=Q2cuGiNnduVa+nnNV_02dYwfrSJoFGesiFBm6Q5U3SGQXpQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
1 sibling, 1 reply; 21+ messages in thread
From: Shubhrajyoti Datta @ 2012-09-28 5:21 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Wolfram Sang, Jean Delvare, Ben Dooks, Simon, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Hi A few suggestions,
On Tue, Aug 28, 2012 at 2:07 PM, Kuninori Morimoto
<kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
> R-Car I2C is similar with SH7760 I2C.
> But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> This patch creates new Renesas R-Car I2C driver.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> ---
> v1 -> v2
>
> - removed #if 0 function
> - add explanation on rcar_i2c_bus_barrier()
> - removed IGNORE_NAK support
> - rename rcar_i2c_soft_reset() -> rcar_i2c_init()
> - removed devm_kfree/devm_iounmap
> - __raw_writel/readl => writel/readl
> - removed un-needed return from rcar_i2c_bus_phase()
> - tidyup calculation method on rcar_i2c_clock_calculate()
> - tidyup English type
> - tidyup comment to "i2c device disabled"
>
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-rcar.c | 715 +++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c/i2c-rcar.h | 10 +
> 4 files changed, 736 insertions(+), 0 deletions(-)
> create mode 100644 drivers/i2c/busses/i2c-rcar.c
> create mode 100644 include/linux/i2c/i2c-rcar.h
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index b4aaa1b..51baa08 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -709,6 +709,16 @@ config I2C_XLR
> This driver can also be built as a module. If so, the module
> will be called i2c-xlr.
>
> +config I2C_RCAR
> + tristate "Renesas R-Car I2C Controller"
> + depends on ARCH_SHMOBILE && I2C
> + help
> + If you say yes to this option, support will be included for the
> + R-Car I2C controller.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-rcar.
> +
> comment "External I2C/SMBus adapter drivers"
>
> config I2C_DIOLAN_U2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index ce3c2be..e98ff51 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> new file mode 100644
> index 0000000..bd8fcf1
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-rcar.c
> @@ -0,0 +1,715 @@
> +/*
> + * drivers/i2c/busses/i2c-rcar.c
> + *
> + * Copyright (C) 2012 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
> + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
> + *
> + * This file used out-of-tree driver i2c-rcar.c
> + * Copyright (C) 2011-2012 Renesas Electronics Corporation
> + *
> + * 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
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c/i2c-rcar.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +/* register offsets */
> +#define ICSCR 0x00 /* slave ctrl */
> +#define ICMCR 0x04 /* master ctrl */
> +#define ICSSR 0x08 /* slave status */
> +#define ICMSR 0x0C /* master status */
> +#define ICSIER 0x10 /* slave irq enable */
> +#define ICMIER 0x14 /* master irq enable */
> +#define ICCCR 0x18 /* clock dividers */
> +#define ICSAR 0x1C /* slave address */
> +#define ICMAR 0x20 /* master address */
> +#define ICRXTX 0x24 /* data port */
> +
> +/* ICMCR */
> +#define MDBS (1 << 7) /* non-fifo mode switch */
> +#define FSCL (1 << 6) /* override SCL pin */
> +#define FSDA (1 << 5) /* override SDA pin */
> +#define OBPC (1 << 4) /* override pins */
> +#define MIE (1 << 3) /* master if enable */
> +#define TSBE (1 << 2)
> +#define FSB (1 << 1) /* force stop bit */
> +#define ESG (1 << 0) /* en startbit gen */
> +
> +/* ICMSR */
> +#define MNR (1 << 6) /* nack received */
> +#define MAL (1 << 5) /* arbitration lost */
> +#define MST (1 << 4) /* sent a stop */
> +#define MDE (1 << 3)
> +#define MDT (1 << 2)
> +#define MDR (1 << 1)
> +#define MAT (1 << 0) /* slave addr xfer done */
> +
> +/* ICMIE */
> +#define MNRE (1 << 6) /* nack irq en */
> +#define MALE (1 << 5) /* arblos irq en */
> +#define MSTE (1 << 4) /* stop irq en */
> +#define MDEE (1 << 3)
> +#define MDTE (1 << 2)
> +#define MDRE (1 << 1)
> +#define MATE (1 << 0) /* address sent irq en */
> +
> +
> +enum {
> + RCAR_BUS_PHASE_ADDR,
> + RCAR_BUS_PHASE_DATA,
> + RCAR_BUS_PHASE_STOP,
> +};
> +
> +enum {
> + RCAR_IRQ_CLOSE,
> + RCAR_IRQ_OPEN_FOR_SEND,
> + RCAR_IRQ_OPEN_FOR_RECV,
> + RCAR_IRQ_OPEN_FOR_STOP,
> +};
> +
> +/*
> + * flags
> + */
> +#define ID_LAST_MSG (1 << 0)
> +#define ID_IOERROR (1 << 1)
> +#define ID_DONE (1 << 2)
> +#define ID_ARBLOST (1 << 3)
> +#define ID_NACK (1 << 4)
> +
> +struct rcar_i2c_priv {
> + void __iomem *io;
> + struct i2c_adapter adap;
> + struct i2c_msg *msg;
> +
> + spinlock_t lock;
> + wait_queue_head_t wait;
> +
> + int pos;
> + int irq;
> + u32 icccr;
> + u32 flags;
> +};
> +
> +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
> +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
> +
> +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
> +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
> +
> +/*
> + * basic functions
> + */
> +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> +{
> + writel(val, priv->io + reg);
> +}
> +
> +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> +{
> + return readl(priv->io + reg);
> +}
> +
> +static void rcar_i2c_init(struct rcar_i2c_priv *priv)
> +{
> + /*
> + * reset slave mode.
> + * slave mode is not used on this driver
> + */
> + rcar_i2c_write(priv, ICSIER, 0);
> + rcar_i2c_write(priv, ICSAR, 0);
> + rcar_i2c_write(priv, ICSCR, 0);
> + rcar_i2c_write(priv, ICSSR, 0);
> +
> + /* reset master mode */
> + rcar_i2c_write(priv, ICMIER, 0);
> + rcar_i2c_write(priv, ICMCR, 0);
> + rcar_i2c_write(priv, ICMSR, 0);
> + rcar_i2c_write(priv, ICMAR, 0);
> +}
> +
> +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
> +{
> + u32 val = MNRE | MALE | MSTE | MATE; /* default */
> +
> + switch (open) {
> + case RCAR_IRQ_OPEN_FOR_SEND:
> + val |= MDEE; /* default + send */
> + break;
> + case RCAR_IRQ_OPEN_FOR_RECV:
> + val |= MDRE; /* default + read */
> + break;
> + case RCAR_IRQ_OPEN_FOR_STOP:
> + val = MSTE; /* stop irq only */
> + break;
> + case RCAR_IRQ_CLOSE:
> + default:
> + val = 0; /* all close */
> + break;
> + }
> + rcar_i2c_write(priv, ICMIER, val);
> +}
> +
> +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
> +{
> + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
> +}
> +
> +/*
> + * bus control functions
> + */
> +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
> +{
> + int i;
> +
> + for (i = 0; i < 1024; i++) {
So the bus wait is 1 ms may be consider a macro for that.
> + /* make sure that bus is not busy */
> + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
> + return 0;
> + udelay(1);
> + }
> +
> + return -EBUSY;
> +}
> +
> +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> +{
> + switch (phase) {
> + case RCAR_BUS_PHASE_ADDR:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> + break;
> + case RCAR_BUS_PHASE_DATA:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> + break;
> + case RCAR_BUS_PHASE_STOP:
> + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> + break;
> + }
> +}
> +
> +/*
> + * clock function
> + */
> +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> + u32 bus_speed,
> + struct device *dev)
> +{
> + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> + u32 scgd, cdf;
> + u32 round, ick;
> + u32 scl;
> +
> + if (!clkp) {
> + dev_err(dev, "there is no peripheral_clk\n");
> + return -EIO;
> + }
> +
> + /*
> + * calculate SCL clock
> + * see
> + * ICCCR
> + *
> + * ick = clkp / (1 + CDF)
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + *
> + * ick : I2C internal clock < 20 MHz
> + * ticf : I2C SCL falling time = 35 ns here
> + * tr : I2C SCL rising time = 200 ns here
> + * intd : LSI internal delay = 50 ns here
> + * clkp : peripheral_clk
> + * F[] : integer up-valuation
> + */
> + for (cdf = 0; cdf < 4; cdf++) {
> + ick = clk_get_rate(clkp) / (1 + cdf);
> + if (ick < 20000000)
> + goto ick_find;
> + }
> + dev_err(dev, "there is no best CDF\n");
> + return -EIO;
> +
> +ick_find:
> + /*
> + * it is impossible to calculate large scale
> + * number on u32. separate it
> + *
> + * F[(ticf + tr + intd) * ick]
> + * = F[(35 + 200 + 50)ns * ick]
> + * = F[285 * ick / 1000000000]
> + * = F[(ick / 1000000) * 285 / 1000]
> + */
> + round = (ick + 500000) / 1000000 * 285;
> + round = (round + 500) / 1000;
> +
> + /*
> + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> + *
> + * Calculation result (= SCL) should be less than
> + * bus_speed for hardware safety
> + */
> + for (scgd = 0; scgd < 0x40; scgd++) {
> + scl = ick / (20 + (scgd * 8) + round);
> + if (scl <= bus_speed)
> + goto scgd_find;
> + }
> + dev_err(dev, "it is impossible to calculate best SCL\n");
> + return -EIO;
> +
> +scgd_find:
> + dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
> + scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
> +
> + /*
> + * keep icccr value
> + */
> + priv->icccr = (scgd << 2 | cdf);
> +
> + return 0;
> +}
> +
> +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_write(priv, ICCCR, priv->icccr);
> +}
> +
> +/*
> + * status functions
> + */
> +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
> +{
> + return rcar_i2c_read(priv, ICMSR);
> +}
> +
> +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
> +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
> +{
> + rcar_i2c_write(priv, ICMSR, ~bit);
> +}
> +
> +/*
> + * recv/send functions
> + */
> +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
> +{
> + rcar_i2c_set_addr(priv, 1);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
> +{
> + int ret;
> +
> + /*
> + * It should check bus status when send case
> + */
> + ret = rcar_i2c_bus_barrier(priv);
> + if (ret < 0)
> + return ret;
> +
> + rcar_i2c_set_addr(priv, 0);
> + rcar_i2c_status_clear(priv);
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
> +
> + return 0;
> +}
> +
> +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
> +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
> +
> +/*
> + * interrupt functions
> + */
> +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happened.
> + * Do nothing
> + */
> + if (!(msr & MDE))
> + return 0;
> +
> + /*
> + * If address transfer phase finished,
> + * goto data phase.
> + */
> + if (msr & MAT)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + if (priv->pos < msg->len) {
> + /*
> + * Prepare next data to ICRXTX register.
> + * This data will go to _SHIFT_ register.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
> + priv->pos++;
> +
> + } else {
> + /*
> + * The last data was pushed to ICRXTX on _PREV_ empty irq.
> + * It is on _SHIFT_ register, and will sent to I2C bus.
> + *
> + * *
> + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> + */
> +
> + if (priv->flags & ID_LAST_MSG)
> + /*
> + * If current msg is the _LAST_ msg,
> + * prepare stop condition here.
> + * ID_DONE will be set on STOP irq.
> + */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + /*
> + * If current msg is _NOT_ last msg,
> + * it doesn't call stop phase.
> + * thus, there is no STOP irq.
> + * return ID_DONE here.
> + */
> + return ID_DONE;
> + }
> +
> + rcar_i2c_send_restart(priv);
> +
> + return 0;
> +}
> +
> +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happened.
> + * Do nothing
> + */
> + if (!(msr & MDR))
> + return 0;
> +
> + if (msr & MAT) {
> + /*
> + * Address transfer phase finished,
> + * but, there is no data at this point.
> + * Do nothing.
> + */
> + } else if (priv->pos < msg->len) {
> + /*
> + * get received data
> + */
> + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
> + priv->pos++;
> + }
> +
> + /*
> + * If next received data is the last data,
> + * I2C driver should send NACK (or STOP).
> + * But this chip can't send NACK, thus, go to STOP phase.
did not understand this.
> + * Otherwise, go to data phase.
> + */
> + if (priv->pos + 1 >= msg->len)
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + else
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> +
> + rcar_i2c_recv_restart(priv);
> +
> + return 0;
> +}
> +
> +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
> +{
> + struct rcar_i2c_priv *priv = ptr;
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + u32 msr;
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock(&priv->lock);
> +
> + msr = rcar_i2c_status_get(priv);
> +
> + /*
> + * Arbitration lost
> + */
> + if (msr & MAL) {
> + /*
> + * CAUTION
> + *
> + * When arbitration lost, device become _slave_ mode.
> + */
> + dev_dbg(dev, "Arbitration Lost\n");
> + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
> + goto out;
> + }
> +
> + /*
> + * Stop
> + */
> + if (msr & MST) {
> + dev_dbg(dev, "Stop\n");
> + rcar_i2c_flags_set(priv, ID_DONE);
> + goto out;
> + }
> +
> + /*
> + * Nack
> + */
> + if (msr & MNR) {
> + dev_dbg(dev, "Nack\n");
> +
> + /* go to stop phase */
> + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
> + rcar_i2c_flags_set(priv, ID_NACK);
> + goto out;
> + }
> +
> + /*
> + * recv/send
> + */
> + if (rcar_i2c_is_recv(priv))
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
> + else
> + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
> +
> +out:
> + if (rcar_i2c_flags_has(priv, ID_DONE)) {
> + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
> + rcar_i2c_status_clear(priv);
> + wake_up(&priv->wait);
> + }
> +
> + spin_unlock(&priv->lock);
> + /*-------------- spin unlock -----------------*/
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs,
> + int num)
> +{
> + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> + struct device *dev = rcar_i2c_priv_to_dev(priv);
> + unsigned long flags;
> + int i, ret, timeout;
> +
> + /*================== enable i2c device ===================*/
> + pm_runtime_get_sync(dev);
> +
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + rcar_i2c_init(priv);
> + rcar_i2c_clock_start(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + ret = -EINVAL;
> + for (i = 0; i < num; i++) {
> + /*-------------- spin lock -----------------*/
> + spin_lock_irqsave(&priv->lock, flags);
> +
> + /* init each data */
> + priv->msg = &msgs[i];
> + priv->pos = 0;
> + priv->flags = 0;
> + if (priv->msg == &msgs[num - 1])
> + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> +
> + /* start send/recv */
> + if (rcar_i2c_is_recv(priv))
> + ret = rcar_i2c_recv(priv);
> + else
> + ret = rcar_i2c_send(priv);
> +
> + spin_unlock_irqrestore(&priv->lock, flags);
> + /*-------------- spin unlock -----------------*/
> +
> + if (ret < 0)
> + break;
> +
> + /*
> + * wait result
> + */
> + timeout = wait_event_timeout(priv->wait,
> + rcar_i2c_flags_has(priv, ID_DONE),
> + 5 * HZ);
> + if (!timeout) {
> + ret = -ETIMEDOUT;
> + break;
> + }
> +
> + /*
> + * error handling
> + */
> + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> + ret = -EREMOTEIO;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> + ret = -EAGAIN;
> + break;
> + }
> +
> + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> + ret = -EIO;
> + break;
> + }
> +
> + ret = i + 1; /* The number of transfer */
> + }
> +
> + pm_runtime_put_sync(dev);
you could consider put here? The difference is that it is not synchronous.
> + /*================== i2c device disabled ==================*/
this comment could be removed.
> +
> + if (ret < 0)
> + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> +
> + return ret;
> +}
> +
> +static u32 rcar_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm rcar_i2c_algo = {
> + .master_xfer = rcar_i2c_master_xfer,
> + .functionality = rcar_i2c_func,
> +};
> +
> +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> +{
> + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> + struct rcar_i2c_priv *priv;
> + struct i2c_adapter *adap;
> + struct resource *res;
> + struct device *dev = &pdev->dev;
> + u32 bus_speed;
> + int ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "no mmio resources\n");
> + return -ENODEV;
> + }
> +
> + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(dev, "no mem for private data\n");
> + return -ENOMEM;
> + }
> +
> + bus_speed = 100000; /* default 100 kHz */
> + if (pdata && pdata->bus_speed)
> + bus_speed = pdata->bus_speed;
> + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> + if (ret < 0)
> + return ret;
> +
> + priv->io = devm_ioremap(dev, res->start, resource_size(res));
> + if (!priv->io) {
> + dev_err(dev, "cannot ioremap\n");
> + return -ENODEV;
> + }
> +
> + priv->irq = platform_get_irq(pdev, 0);
> + init_waitqueue_head(&priv->wait);
> + spin_lock_init(&priv->lock);
> +
> + adap = &priv->adap;
> + adap->nr = pdev->id;
> + adap->algo = &rcar_i2c_algo;
> + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> + adap->retries = 3;
> + adap->dev.parent = dev;
> + i2c_set_adapdata(adap, priv);
> + strlcpy(adap->name, pdev->name, sizeof(adap->name));
> +
> + if (request_irq(priv->irq, rcar_i2c_irq, 0,
> + dev_name(dev), priv)) {
Could consider devm_ the free will not be needed then.
> + dev_err(dev, "cannot get irq %d\n", priv->irq);
> + return -EBUSY;
> + }
> +
> + ret = i2c_add_numbered_adapter(adap);
> + if (ret < 0) {
> + dev_err(dev, "reg adap failed: %d\n", ret);
> + goto error_irq;
> + }
> +
> + pm_runtime_enable(dev);
> + platform_set_drvdata(pdev, priv);
> +
> + dev_info(dev, "probed\n");
> +
> + return 0;
> +
> +error_irq:
> + free_irq(priv->irq, priv);
> +
> + return ret;
> +}
> +
> +static int __devexit rcar_i2c_remove(struct platform_device *pdev)
> +{
> + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> +
> + i2c_del_adapter(&priv->adap);
> + free_irq(priv->irq, priv);
> + pm_runtime_disable(dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver rcar_i2c_drv = {
> + .driver = {
> + .name = "i2c-rcar",
> + .owner = THIS_MODULE,
> + },
> + .probe = rcar_i2c_probe,
> + .remove = __devexit_p(rcar_i2c_remove),
> +};
> +
> +module_platform_driver(rcar_i2c_drv);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
> +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
> diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
> new file mode 100644
> index 0000000..496f5c2
> --- /dev/null
> +++ b/include/linux/i2c/i2c-rcar.h
> @@ -0,0 +1,10 @@
> +#ifndef __I2C_R_CAR_H__
> +#define __I2C_R_CAR_H__
> +
> +#include <linux/platform_device.h>
> +
> +struct i2c_rcar_platform_data {
> + u32 bus_speed;
> +};
> +
> +#endif /* __I2C_R_CAR_H__ */
> --
> 1.7.5.4
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2] i2c: add Renesas R-Car I2C driver
[not found] ` <CAM=Q2cuGiNnduVa+nnNV_02dYwfrSJoFGesiFBm6Q5U3SGQXpQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2012-09-28 5:56 ` Kuninori Morimoto
[not found] ` <w3pmx0an11d.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-09-28 6:44 ` [PATCH v3] " Kuninori Morimoto
1 sibling, 1 reply; 21+ messages in thread
From: Kuninori Morimoto @ 2012-09-28 5:56 UTC (permalink / raw)
To: Shubhrajyoti Datta
Cc: Wolfram Sang, Jean Delvare, Ben Dooks, Simon, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
Dear Shubhrajyoti
Thank you for your comment.
> Hi A few suggestions,
>
> On Tue, Aug 28, 2012 at 2:07 PM, Kuninori Morimoto
> <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
> > R-Car I2C is similar with SH7760 I2C.
> > But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> > Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> > This patch creates new Renesas R-Car I2C driver.
> >
> > Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> > ---
> > v1 -> v2
> >
> > - removed #if 0 function
> > - add explanation on rcar_i2c_bus_barrier()
> > - removed IGNORE_NAK support
> > - rename rcar_i2c_soft_reset() -> rcar_i2c_init()
> > - removed devm_kfree/devm_iounmap
> > - __raw_writel/readl => writel/readl
> > - removed un-needed return from rcar_i2c_bus_phase()
> > - tidyup calculation method on rcar_i2c_clock_calculate()
> > - tidyup English type
> > - tidyup comment to "i2c device disabled"
> >
> > drivers/i2c/busses/Kconfig | 10 +
> > drivers/i2c/busses/Makefile | 1 +
> > drivers/i2c/busses/i2c-rcar.c | 715 +++++++++++++++++++++++++++++++++++++++++
> > include/linux/i2c/i2c-rcar.h | 10 +
> > 4 files changed, 736 insertions(+), 0 deletions(-)
> > create mode 100644 drivers/i2c/busses/i2c-rcar.c
> > create mode 100644 include/linux/i2c/i2c-rcar.h
> >
> > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> > index b4aaa1b..51baa08 100644
> > --- a/drivers/i2c/busses/Kconfig
> > +++ b/drivers/i2c/busses/Kconfig
> > @@ -709,6 +709,16 @@ config I2C_XLR
> > This driver can also be built as a module. If so, the module
> > will be called i2c-xlr.
> >
> > +config I2C_RCAR
> > + tristate "Renesas R-Car I2C Controller"
> > + depends on ARCH_SHMOBILE && I2C
> > + help
> > + If you say yes to this option, support will be included for the
> > + R-Car I2C controller.
> > +
> > + This driver can also be built as a module. If so, the module
> > + will be called i2c-rcar.
> > +
> > comment "External I2C/SMBus adapter drivers"
> >
> > config I2C_DIOLAN_U2C
> > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> > index ce3c2be..e98ff51 100644
> > --- a/drivers/i2c/busses/Makefile
> > +++ b/drivers/i2c/busses/Makefile
> > @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> > obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
> > obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> > obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> > +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
> >
> > # External I2C/SMBus adapter drivers
> > obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> > diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> > new file mode 100644
> > index 0000000..bd8fcf1
> > --- /dev/null
> > +++ b/drivers/i2c/busses/i2c-rcar.c
> > @@ -0,0 +1,715 @@
> > +/*
> > + * drivers/i2c/busses/i2c-rcar.c
> > + *
> > + * Copyright (C) 2012 Renesas Solutions Corp.
> > + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> > + *
> > + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
> > + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
> > + *
> > + * This file used out-of-tree driver i2c-rcar.c
> > + * Copyright (C) 2011-2012 Renesas Electronics Corporation
> > + *
> > + * 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
> > + *
> > + * 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.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> > + */
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/err.h>
> > +#include <linux/init.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/i2c.h>
> > +#include <linux/i2c/i2c-rcar.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +
> > +/* register offsets */
> > +#define ICSCR 0x00 /* slave ctrl */
> > +#define ICMCR 0x04 /* master ctrl */
> > +#define ICSSR 0x08 /* slave status */
> > +#define ICMSR 0x0C /* master status */
> > +#define ICSIER 0x10 /* slave irq enable */
> > +#define ICMIER 0x14 /* master irq enable */
> > +#define ICCCR 0x18 /* clock dividers */
> > +#define ICSAR 0x1C /* slave address */
> > +#define ICMAR 0x20 /* master address */
> > +#define ICRXTX 0x24 /* data port */
> > +
> > +/* ICMCR */
> > +#define MDBS (1 << 7) /* non-fifo mode switch */
> > +#define FSCL (1 << 6) /* override SCL pin */
> > +#define FSDA (1 << 5) /* override SDA pin */
> > +#define OBPC (1 << 4) /* override pins */
> > +#define MIE (1 << 3) /* master if enable */
> > +#define TSBE (1 << 2)
> > +#define FSB (1 << 1) /* force stop bit */
> > +#define ESG (1 << 0) /* en startbit gen */
> > +
> > +/* ICMSR */
> > +#define MNR (1 << 6) /* nack received */
> > +#define MAL (1 << 5) /* arbitration lost */
> > +#define MST (1 << 4) /* sent a stop */
> > +#define MDE (1 << 3)
> > +#define MDT (1 << 2)
> > +#define MDR (1 << 1)
> > +#define MAT (1 << 0) /* slave addr xfer done */
> > +
> > +/* ICMIE */
> > +#define MNRE (1 << 6) /* nack irq en */
> > +#define MALE (1 << 5) /* arblos irq en */
> > +#define MSTE (1 << 4) /* stop irq en */
> > +#define MDEE (1 << 3)
> > +#define MDTE (1 << 2)
> > +#define MDRE (1 << 1)
> > +#define MATE (1 << 0) /* address sent irq en */
> > +
> > +
> > +enum {
> > + RCAR_BUS_PHASE_ADDR,
> > + RCAR_BUS_PHASE_DATA,
> > + RCAR_BUS_PHASE_STOP,
> > +};
> > +
> > +enum {
> > + RCAR_IRQ_CLOSE,
> > + RCAR_IRQ_OPEN_FOR_SEND,
> > + RCAR_IRQ_OPEN_FOR_RECV,
> > + RCAR_IRQ_OPEN_FOR_STOP,
> > +};
> > +
> > +/*
> > + * flags
> > + */
> > +#define ID_LAST_MSG (1 << 0)
> > +#define ID_IOERROR (1 << 1)
> > +#define ID_DONE (1 << 2)
> > +#define ID_ARBLOST (1 << 3)
> > +#define ID_NACK (1 << 4)
> > +
> > +struct rcar_i2c_priv {
> > + void __iomem *io;
> > + struct i2c_adapter adap;
> > + struct i2c_msg *msg;
> > +
> > + spinlock_t lock;
> > + wait_queue_head_t wait;
> > +
> > + int pos;
> > + int irq;
> > + u32 icccr;
> > + u32 flags;
> > +};
> > +
> > +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
> > +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
> > +
> > +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
> > +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
> > +
> > +/*
> > + * basic functions
> > + */
> > +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> > +{
> > + writel(val, priv->io + reg);
> > +}
> > +
> > +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> > +{
> > + return readl(priv->io + reg);
> > +}
> > +
> > +static void rcar_i2c_init(struct rcar_i2c_priv *priv)
> > +{
> > + /*
> > + * reset slave mode.
> > + * slave mode is not used on this driver
> > + */
> > + rcar_i2c_write(priv, ICSIER, 0);
> > + rcar_i2c_write(priv, ICSAR, 0);
> > + rcar_i2c_write(priv, ICSCR, 0);
> > + rcar_i2c_write(priv, ICSSR, 0);
> > +
> > + /* reset master mode */
> > + rcar_i2c_write(priv, ICMIER, 0);
> > + rcar_i2c_write(priv, ICMCR, 0);
> > + rcar_i2c_write(priv, ICMSR, 0);
> > + rcar_i2c_write(priv, ICMAR, 0);
> > +}
> > +
> > +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
> > +{
> > + u32 val = MNRE | MALE | MSTE | MATE; /* default */
> > +
> > + switch (open) {
> > + case RCAR_IRQ_OPEN_FOR_SEND:
> > + val |= MDEE; /* default + send */
> > + break;
> > + case RCAR_IRQ_OPEN_FOR_RECV:
> > + val |= MDRE; /* default + read */
> > + break;
> > + case RCAR_IRQ_OPEN_FOR_STOP:
> > + val = MSTE; /* stop irq only */
> > + break;
> > + case RCAR_IRQ_CLOSE:
> > + default:
> > + val = 0; /* all close */
> > + break;
> > + }
> > + rcar_i2c_write(priv, ICMIER, val);
> > +}
> > +
> > +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
> > +{
> > + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
> > +}
> > +
> > +/*
> > + * bus control functions
> > + */
> > +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < 1024; i++) {
>
> So the bus wait is 1 ms may be consider a macro for that.
Do you mean udelay(1) x 1024 = mdelay(1) ?
But this 1024 is just timeout counter.
In normal case, this ends in 2 - 3 times loops.
I don't want to use mdelay() here
>
> > + /* make sure that bus is not busy */
> > + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
> > + return 0;
> > + udelay(1);
> > + }
> > +
> > + return -EBUSY;
> > +}
> > +
> > +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
> > +{
> > + switch (phase) {
> > + case RCAR_BUS_PHASE_ADDR:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
> > + break;
> > + case RCAR_BUS_PHASE_DATA:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
> > + break;
> > + case RCAR_BUS_PHASE_STOP:
> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
> > + break;
> > + }
> > +}
> > +
> > +/*
> > + * clock function
> > + */
> > +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
> > + u32 bus_speed,
> > + struct device *dev)
> > +{
> > + struct clk *clkp = clk_get(NULL, "peripheral_clk");
> > + u32 scgd, cdf;
> > + u32 round, ick;
> > + u32 scl;
> > +
> > + if (!clkp) {
> > + dev_err(dev, "there is no peripheral_clk\n");
> > + return -EIO;
> > + }
> > +
> > + /*
> > + * calculate SCL clock
> > + * see
> > + * ICCCR
> > + *
> > + * ick = clkp / (1 + CDF)
> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> > + *
> > + * ick : I2C internal clock < 20 MHz
> > + * ticf : I2C SCL falling time = 35 ns here
> > + * tr : I2C SCL rising time = 200 ns here
> > + * intd : LSI internal delay = 50 ns here
> > + * clkp : peripheral_clk
> > + * F[] : integer up-valuation
> > + */
> > + for (cdf = 0; cdf < 4; cdf++) {
> > + ick = clk_get_rate(clkp) / (1 + cdf);
> > + if (ick < 20000000)
> > + goto ick_find;
> > + }
> > + dev_err(dev, "there is no best CDF\n");
> > + return -EIO;
> > +
> > +ick_find:
> > + /*
> > + * it is impossible to calculate large scale
> > + * number on u32. separate it
> > + *
> > + * F[(ticf + tr + intd) * ick]
> > + * = F[(35 + 200 + 50)ns * ick]
> > + * = F[285 * ick / 1000000000]
> > + * = F[(ick / 1000000) * 285 / 1000]
> > + */
> > + round = (ick + 500000) / 1000000 * 285;
> > + round = (round + 500) / 1000;
> > +
> > + /*
> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
> > + *
> > + * Calculation result (= SCL) should be less than
> > + * bus_speed for hardware safety
> > + */
> > + for (scgd = 0; scgd < 0x40; scgd++) {
> > + scl = ick / (20 + (scgd * 8) + round);
> > + if (scl <= bus_speed)
> > + goto scgd_find;
> > + }
> > + dev_err(dev, "it is impossible to calculate best SCL\n");
> > + return -EIO;
> > +
> > +scgd_find:
> > + dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
> > + scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
> > +
> > + /*
> > + * keep icccr value
> > + */
> > + priv->icccr = (scgd << 2 | cdf);
> > +
> > + return 0;
> > +}
> > +
> > +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
> > +{
> > + rcar_i2c_write(priv, ICCCR, priv->icccr);
> > +}
> > +
> > +/*
> > + * status functions
> > + */
> > +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
> > +{
> > + return rcar_i2c_read(priv, ICMSR);
> > +}
> > +
> > +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
> > +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
> > +{
> > + rcar_i2c_write(priv, ICMSR, ~bit);
> > +}
> > +
> > +/*
> > + * recv/send functions
> > + */
> > +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
> > +{
> > + rcar_i2c_set_addr(priv, 1);
> > + rcar_i2c_status_clear(priv);
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
> > +{
> > + int ret;
> > +
> > + /*
> > + * It should check bus status when send case
> > + */
> > + ret = rcar_i2c_bus_barrier(priv);
> > + if (ret < 0)
> > + return ret;
> > +
> > + rcar_i2c_set_addr(priv, 0);
> > + rcar_i2c_status_clear(priv);
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
> > +
> > + return 0;
> > +}
> > +
> > +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
> > +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
> > +
> > +/*
> > + * interrupt functions
> > + */
> > +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> > +{
> > + struct i2c_msg *msg = priv->msg;
> > +
> > + /*
> > + * FIXME
> > + * sometimes, unknown interrupt happened.
> > + * Do nothing
> > + */
> > + if (!(msr & MDE))
> > + return 0;
> > +
> > + /*
> > + * If address transfer phase finished,
> > + * goto data phase.
> > + */
> > + if (msr & MAT)
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> > +
> > + if (priv->pos < msg->len) {
> > + /*
> > + * Prepare next data to ICRXTX register.
> > + * This data will go to _SHIFT_ register.
> > + *
> > + * *
> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> > + */
> > + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
> > + priv->pos++;
> > +
> > + } else {
> > + /*
> > + * The last data was pushed to ICRXTX on _PREV_ empty irq.
> > + * It is on _SHIFT_ register, and will sent to I2C bus.
> > + *
> > + * *
> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
> > + */
> > +
> > + if (priv->flags & ID_LAST_MSG)
> > + /*
> > + * If current msg is the _LAST_ msg,
> > + * prepare stop condition here.
> > + * ID_DONE will be set on STOP irq.
> > + */
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + else
> > + /*
> > + * If current msg is _NOT_ last msg,
> > + * it doesn't call stop phase.
> > + * thus, there is no STOP irq.
> > + * return ID_DONE here.
> > + */
> > + return ID_DONE;
> > + }
> > +
> > + rcar_i2c_send_restart(priv);
> > +
> > + return 0;
> > +}
> > +
> > +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
> > +{
> > + struct i2c_msg *msg = priv->msg;
> > +
> > + /*
> > + * FIXME
> > + * sometimes, unknown interrupt happened.
> > + * Do nothing
> > + */
> > + if (!(msr & MDR))
> > + return 0;
> > +
> > + if (msr & MAT) {
> > + /*
> > + * Address transfer phase finished,
> > + * but, there is no data at this point.
> > + * Do nothing.
> > + */
> > + } else if (priv->pos < msg->len) {
> > + /*
> > + * get received data
> > + */
> > + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
> > + priv->pos++;
> > + }
> > +
> > + /*
> > + * If next received data is the last data,
> > + * I2C driver should send NACK (or STOP).
> > + * But this chip can't send NACK, thus, go to STOP phase.
> did not understand this.
OK. fix
>
> > + * Otherwise, go to data phase.
> > + */
> > + if (priv->pos + 1 >= msg->len)
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + else
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
> > +
> > + rcar_i2c_recv_restart(priv);
> > +
> > + return 0;
> > +}
> > +
> > +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
> > +{
> > + struct rcar_i2c_priv *priv = ptr;
> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
> > + u32 msr;
> > +
> > + /*-------------- spin lock -----------------*/
> > + spin_lock(&priv->lock);
> > +
> > + msr = rcar_i2c_status_get(priv);
> > +
> > + /*
> > + * Arbitration lost
> > + */
> > + if (msr & MAL) {
> > + /*
> > + * CAUTION
> > + *
> > + * When arbitration lost, device become _slave_ mode.
> > + */
> > + dev_dbg(dev, "Arbitration Lost\n");
> > + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
> > + goto out;
> > + }
> > +
> > + /*
> > + * Stop
> > + */
> > + if (msr & MST) {
> > + dev_dbg(dev, "Stop\n");
> > + rcar_i2c_flags_set(priv, ID_DONE);
> > + goto out;
> > + }
> > +
> > + /*
> > + * Nack
> > + */
> > + if (msr & MNR) {
> > + dev_dbg(dev, "Nack\n");
> > +
> > + /* go to stop phase */
> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
> > + rcar_i2c_flags_set(priv, ID_NACK);
> > + goto out;
> > + }
> > +
> > + /*
> > + * recv/send
> > + */
> > + if (rcar_i2c_is_recv(priv))
> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
> > + else
> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
> > +
> > +out:
> > + if (rcar_i2c_flags_has(priv, ID_DONE)) {
> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
> > + rcar_i2c_status_clear(priv);
> > + wake_up(&priv->wait);
> > + }
> > +
> > + spin_unlock(&priv->lock);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> > + struct i2c_msg *msgs,
> > + int num)
> > +{
> > + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
> > + unsigned long flags;
> > + int i, ret, timeout;
> > +
> > + /*================== enable i2c device ===================*/
> > + pm_runtime_get_sync(dev);
> > +
> > + /*-------------- spin lock -----------------*/
> > + spin_lock_irqsave(&priv->lock, flags);
> > +
> > + rcar_i2c_init(priv);
> > + rcar_i2c_clock_start(priv);
> > +
> > + spin_unlock_irqrestore(&priv->lock, flags);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + ret = -EINVAL;
> > + for (i = 0; i < num; i++) {
> > + /*-------------- spin lock -----------------*/
> > + spin_lock_irqsave(&priv->lock, flags);
> > +
> > + /* init each data */
> > + priv->msg = &msgs[i];
> > + priv->pos = 0;
> > + priv->flags = 0;
> > + if (priv->msg == &msgs[num - 1])
> > + rcar_i2c_flags_set(priv, ID_LAST_MSG);
> > +
> > + /* start send/recv */
> > + if (rcar_i2c_is_recv(priv))
> > + ret = rcar_i2c_recv(priv);
> > + else
> > + ret = rcar_i2c_send(priv);
> > +
> > + spin_unlock_irqrestore(&priv->lock, flags);
> > + /*-------------- spin unlock -----------------*/
> > +
> > + if (ret < 0)
> > + break;
> > +
> > + /*
> > + * wait result
> > + */
> > + timeout = wait_event_timeout(priv->wait,
> > + rcar_i2c_flags_has(priv, ID_DONE),
> > + 5 * HZ);
> > + if (!timeout) {
> > + ret = -ETIMEDOUT;
> > + break;
> > + }
> > +
> > + /*
> > + * error handling
> > + */
> > + if (rcar_i2c_flags_has(priv, ID_NACK)) {
> > + ret = -EREMOTEIO;
> > + break;
> > + }
> > +
> > + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
> > + ret = -EAGAIN;
> > + break;
> > + }
> > +
> > + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
> > + ret = -EIO;
> > + break;
> > + }
> > +
> > + ret = i + 1; /* The number of transfer */
> > + }
> > +
> > + pm_runtime_put_sync(dev);
> you could consider put here? The difference is that it is not synchronous.
Do you mean "use pm_runtime_put()" ?
then should I use pm_runtime_get() too ?
> > + /*================== i2c device disabled ==================*/
> this comment could be removed.
> > +
> > + if (ret < 0)
> > + dev_err(dev, "error %d : %x\n", ret, priv->flags);
> > +
> > + return ret;
> > +}
> > +
> > +static u32 rcar_i2c_func(struct i2c_adapter *adap)
> > +{
> > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> > +}
> > +
> > +static const struct i2c_algorithm rcar_i2c_algo = {
> > + .master_xfer = rcar_i2c_master_xfer,
> > + .functionality = rcar_i2c_func,
> > +};
> > +
> > +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> > +{
> > + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> > + struct rcar_i2c_priv *priv;
> > + struct i2c_adapter *adap;
> > + struct resource *res;
> > + struct device *dev = &pdev->dev;
> > + u32 bus_speed;
> > + int ret;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + if (!res) {
> > + dev_err(dev, "no mmio resources\n");
> > + return -ENODEV;
> > + }
> > +
> > + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> > + if (!priv) {
> > + dev_err(dev, "no mem for private data\n");
> > + return -ENOMEM;
> > + }
> > +
> > + bus_speed = 100000; /* default 100 kHz */
> > + if (pdata && pdata->bus_speed)
> > + bus_speed = pdata->bus_speed;
> > + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> > + if (ret < 0)
> > + return ret;
> > +
> > + priv->io = devm_ioremap(dev, res->start, resource_size(res));
> > + if (!priv->io) {
> > + dev_err(dev, "cannot ioremap\n");
> > + return -ENODEV;
> > + }
> > +
> > + priv->irq = platform_get_irq(pdev, 0);
> > + init_waitqueue_head(&priv->wait);
> > + spin_lock_init(&priv->lock);
> > +
> > + adap = &priv->adap;
> > + adap->nr = pdev->id;
> > + adap->algo = &rcar_i2c_algo;
> > + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> > + adap->retries = 3;
> > + adap->dev.parent = dev;
> > + i2c_set_adapdata(adap, priv);
> > + strlcpy(adap->name, pdev->name, sizeof(adap->name));
> > +
> > + if (request_irq(priv->irq, rcar_i2c_irq, 0,
> > + dev_name(dev), priv)) {
>
> Could consider devm_ the free will not be needed then.
OK.
>
> > + dev_err(dev, "cannot get irq %d\n", priv->irq);
> > + return -EBUSY;
> > + }
> > +
> > + ret = i2c_add_numbered_adapter(adap);
> > + if (ret < 0) {
> > + dev_err(dev, "reg adap failed: %d\n", ret);
> > + goto error_irq;
> > + }
> > +
> > + pm_runtime_enable(dev);
> > + platform_set_drvdata(pdev, priv);
> > +
> > + dev_info(dev, "probed\n");
> > +
> > + return 0;
> > +
> > +error_irq:
> > + free_irq(priv->irq, priv);
> > +
> > + return ret;
> > +}
> > +
> > +static int __devexit rcar_i2c_remove(struct platform_device *pdev)
> > +{
> > + struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
> > + struct device *dev = &pdev->dev;
> > +
> > + i2c_del_adapter(&priv->adap);
> > + free_irq(priv->irq, priv);
> > + pm_runtime_disable(dev);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver rcar_i2c_drv = {
> > + .driver = {
> > + .name = "i2c-rcar",
> > + .owner = THIS_MODULE,
> > + },
> > + .probe = rcar_i2c_probe,
> > + .remove = __devexit_p(rcar_i2c_remove),
> > +};
> > +
> > +module_platform_driver(rcar_i2c_drv);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
> > +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
> > diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
> > new file mode 100644
> > index 0000000..496f5c2
> > --- /dev/null
> > +++ b/include/linux/i2c/i2c-rcar.h
> > @@ -0,0 +1,10 @@
> > +#ifndef __I2C_R_CAR_H__
> > +#define __I2C_R_CAR_H__
> > +
> > +#include <linux/platform_device.h>
> > +
> > +struct i2c_rcar_platform_data {
> > + u32 bus_speed;
> > +};
> > +
> > +#endif /* __I2C_R_CAR_H__ */
> > --
> > 1.7.5.4
> >
Best regards
--
Kuninori Morimoto
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2] i2c: add Renesas R-Car I2C driver
[not found] ` <w3pmx0an11d.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
@ 2012-09-28 6:25 ` Shubhrajyoti Datta
0 siblings, 0 replies; 21+ messages in thread
From: Shubhrajyoti Datta @ 2012-09-28 6:25 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Wolfram Sang, Jean Delvare, Ben Dooks, Simon, Magnus, Linux-I2C,
Kuninori Morimoto, phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
On Fri, Sep 28, 2012 at 11:26 AM, Kuninori Morimoto
<kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
>
> Dear Shubhrajyoti
>
> Thank you for your comment.
>
>> Hi A few suggestions,
>>
>> On Tue, Aug 28, 2012 at 2:07 PM, Kuninori Morimoto
>> <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org> wrote:
>> > R-Car I2C is similar with SH7760 I2C.
>> > But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
>> > Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
>> > This patch creates new Renesas R-Car I2C driver.
>> >
>> > Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
>> > ---
>> > v1 -> v2
>> >
>> > - removed #if 0 function
>> > - add explanation on rcar_i2c_bus_barrier()
>> > - removed IGNORE_NAK support
>> > - rename rcar_i2c_soft_reset() -> rcar_i2c_init()
>> > - removed devm_kfree/devm_iounmap
>> > - __raw_writel/readl => writel/readl
>> > - removed un-needed return from rcar_i2c_bus_phase()
>> > - tidyup calculation method on rcar_i2c_clock_calculate()
>> > - tidyup English type
>> > - tidyup comment to "i2c device disabled"
>> >
>> > drivers/i2c/busses/Kconfig | 10 +
>> > drivers/i2c/busses/Makefile | 1 +
>> > drivers/i2c/busses/i2c-rcar.c | 715 +++++++++++++++++++++++++++++++++++++++++
>> > include/linux/i2c/i2c-rcar.h | 10 +
>> > 4 files changed, 736 insertions(+), 0 deletions(-)
>> > create mode 100644 drivers/i2c/busses/i2c-rcar.c
>> > create mode 100644 include/linux/i2c/i2c-rcar.h
>> >
>> > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
>> > index b4aaa1b..51baa08 100644
>> > --- a/drivers/i2c/busses/Kconfig
>> > +++ b/drivers/i2c/busses/Kconfig
>> > @@ -709,6 +709,16 @@ config I2C_XLR
>> > This driver can also be built as a module. If so, the module
>> > will be called i2c-xlr.
>> >
>> > +config I2C_RCAR
>> > + tristate "Renesas R-Car I2C Controller"
>> > + depends on ARCH_SHMOBILE && I2C
>> > + help
>> > + If you say yes to this option, support will be included for the
>> > + R-Car I2C controller.
>> > +
>> > + This driver can also be built as a module. If so, the module
>> > + will be called i2c-rcar.
>> > +
>> > comment "External I2C/SMBus adapter drivers"
>> >
>> > config I2C_DIOLAN_U2C
>> > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
>> > index ce3c2be..e98ff51 100644
>> > --- a/drivers/i2c/busses/Makefile
>> > +++ b/drivers/i2c/busses/Makefile
>> > @@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
>> > obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
>> > obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
>> > obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
>> > +obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
>> >
>> > # External I2C/SMBus adapter drivers
>> > obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
>> > diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
>> > new file mode 100644
>> > index 0000000..bd8fcf1
>> > --- /dev/null
>> > +++ b/drivers/i2c/busses/i2c-rcar.c
>> > @@ -0,0 +1,715 @@
>> > +/*
>> > + * drivers/i2c/busses/i2c-rcar.c
>> > + *
>> > + * Copyright (C) 2012 Renesas Solutions Corp.
>> > + * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
>> > + *
>> > + * This file is based on the drivers/i2c/busses/i2c-sh7760.c
>> > + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
>> > + *
>> > + * This file used out-of-tree driver i2c-rcar.c
>> > + * Copyright (C) 2011-2012 Renesas Electronics Corporation
>> > + *
>> > + * 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
>> > + *
>> > + * 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.
>> > + *
>> > + * You should have received a copy of the GNU General Public License
>> > + * along with this program; if not, write to the Free Software
>> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>> > + */
>> > +#include <linux/clk.h>
>> > +#include <linux/delay.h>
>> > +#include <linux/err.h>
>> > +#include <linux/init.h>
>> > +#include <linux/interrupt.h>
>> > +#include <linux/io.h>
>> > +#include <linux/i2c.h>
>> > +#include <linux/i2c/i2c-rcar.h>
>> > +#include <linux/kernel.h>
>> > +#include <linux/module.h>
>> > +#include <linux/platform_device.h>
>> > +#include <linux/pm_runtime.h>
>> > +#include <linux/slab.h>
>> > +#include <linux/spinlock.h>
>> > +
>> > +/* register offsets */
>> > +#define ICSCR 0x00 /* slave ctrl */
>> > +#define ICMCR 0x04 /* master ctrl */
>> > +#define ICSSR 0x08 /* slave status */
>> > +#define ICMSR 0x0C /* master status */
>> > +#define ICSIER 0x10 /* slave irq enable */
>> > +#define ICMIER 0x14 /* master irq enable */
>> > +#define ICCCR 0x18 /* clock dividers */
>> > +#define ICSAR 0x1C /* slave address */
>> > +#define ICMAR 0x20 /* master address */
>> > +#define ICRXTX 0x24 /* data port */
>> > +
>> > +/* ICMCR */
>> > +#define MDBS (1 << 7) /* non-fifo mode switch */
>> > +#define FSCL (1 << 6) /* override SCL pin */
>> > +#define FSDA (1 << 5) /* override SDA pin */
>> > +#define OBPC (1 << 4) /* override pins */
>> > +#define MIE (1 << 3) /* master if enable */
>> > +#define TSBE (1 << 2)
>> > +#define FSB (1 << 1) /* force stop bit */
>> > +#define ESG (1 << 0) /* en startbit gen */
>> > +
>> > +/* ICMSR */
>> > +#define MNR (1 << 6) /* nack received */
>> > +#define MAL (1 << 5) /* arbitration lost */
>> > +#define MST (1 << 4) /* sent a stop */
>> > +#define MDE (1 << 3)
>> > +#define MDT (1 << 2)
>> > +#define MDR (1 << 1)
>> > +#define MAT (1 << 0) /* slave addr xfer done */
>> > +
>> > +/* ICMIE */
>> > +#define MNRE (1 << 6) /* nack irq en */
>> > +#define MALE (1 << 5) /* arblos irq en */
>> > +#define MSTE (1 << 4) /* stop irq en */
>> > +#define MDEE (1 << 3)
>> > +#define MDTE (1 << 2)
>> > +#define MDRE (1 << 1)
>> > +#define MATE (1 << 0) /* address sent irq en */
>> > +
>> > +
>> > +enum {
>> > + RCAR_BUS_PHASE_ADDR,
>> > + RCAR_BUS_PHASE_DATA,
>> > + RCAR_BUS_PHASE_STOP,
>> > +};
>> > +
>> > +enum {
>> > + RCAR_IRQ_CLOSE,
>> > + RCAR_IRQ_OPEN_FOR_SEND,
>> > + RCAR_IRQ_OPEN_FOR_RECV,
>> > + RCAR_IRQ_OPEN_FOR_STOP,
>> > +};
>> > +
>> > +/*
>> > + * flags
>> > + */
>> > +#define ID_LAST_MSG (1 << 0)
>> > +#define ID_IOERROR (1 << 1)
>> > +#define ID_DONE (1 << 2)
>> > +#define ID_ARBLOST (1 << 3)
>> > +#define ID_NACK (1 << 4)
>> > +
>> > +struct rcar_i2c_priv {
>> > + void __iomem *io;
>> > + struct i2c_adapter adap;
>> > + struct i2c_msg *msg;
>> > +
>> > + spinlock_t lock;
>> > + wait_queue_head_t wait;
>> > +
>> > + int pos;
>> > + int irq;
>> > + u32 icccr;
>> > + u32 flags;
>> > +};
>> > +
>> > +#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
>> > +#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
>> > +
>> > +#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
>> > +#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
>> > +
>> > +/*
>> > + * basic functions
>> > + */
>> > +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
>> > +{
>> > + writel(val, priv->io + reg);
>> > +}
>> > +
>> > +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
>> > +{
>> > + return readl(priv->io + reg);
>> > +}
>> > +
>> > +static void rcar_i2c_init(struct rcar_i2c_priv *priv)
>> > +{
>> > + /*
>> > + * reset slave mode.
>> > + * slave mode is not used on this driver
>> > + */
>> > + rcar_i2c_write(priv, ICSIER, 0);
>> > + rcar_i2c_write(priv, ICSAR, 0);
>> > + rcar_i2c_write(priv, ICSCR, 0);
>> > + rcar_i2c_write(priv, ICSSR, 0);
>> > +
>> > + /* reset master mode */
>> > + rcar_i2c_write(priv, ICMIER, 0);
>> > + rcar_i2c_write(priv, ICMCR, 0);
>> > + rcar_i2c_write(priv, ICMSR, 0);
>> > + rcar_i2c_write(priv, ICMAR, 0);
>> > +}
>> > +
>> > +static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
>> > +{
>> > + u32 val = MNRE | MALE | MSTE | MATE; /* default */
>> > +
>> > + switch (open) {
>> > + case RCAR_IRQ_OPEN_FOR_SEND:
>> > + val |= MDEE; /* default + send */
>> > + break;
>> > + case RCAR_IRQ_OPEN_FOR_RECV:
>> > + val |= MDRE; /* default + read */
>> > + break;
>> > + case RCAR_IRQ_OPEN_FOR_STOP:
>> > + val = MSTE; /* stop irq only */
>> > + break;
>> > + case RCAR_IRQ_CLOSE:
>> > + default:
>> > + val = 0; /* all close */
>> > + break;
>> > + }
>> > + rcar_i2c_write(priv, ICMIER, val);
>> > +}
>> > +
>> > +static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
>> > +{
>> > + rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
>> > +}
>> > +
>> > +/*
>> > + * bus control functions
>> > + */
>> > +static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
>> > +{
>> > + int i;
>> > +
>> > + for (i = 0; i < 1024; i++) {
>>
>> So the bus wait is 1 ms may be consider a macro for that.
>
> Do you mean udelay(1) x 1024 = mdelay(1) ?
> But this 1024 is just timeout counter.
> In normal case, this ends in 2 - 3 times loops.
> I don't want to use mdelay() here
Which I agree.
The suggestion was to have a macro
#define XXX_TIMEOUT 1024
and then use that.
>
>>
>> > + /* make sure that bus is not busy */
>> > + if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
>> > + return 0;
>> > + udelay(1);
>> > + }
>> > +
>> > + return -EBUSY;
>> > +}
>> > +
>> > +static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
>> > +{
>> > + switch (phase) {
>> > + case RCAR_BUS_PHASE_ADDR:
>> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
>> > + break;
>> > + case RCAR_BUS_PHASE_DATA:
>> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE);
>> > + break;
>> > + case RCAR_BUS_PHASE_STOP:
>> > + rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
>> > + break;
>> > + }
>> > +}
>> > +
>> > +/*
>> > + * clock function
>> > + */
>> > +static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
>> > + u32 bus_speed,
>> > + struct device *dev)
>> > +{
>> > + struct clk *clkp = clk_get(NULL, "peripheral_clk");
>> > + u32 scgd, cdf;
>> > + u32 round, ick;
>> > + u32 scl;
>> > +
>> > + if (!clkp) {
>> > + dev_err(dev, "there is no peripheral_clk\n");
>> > + return -EIO;
>> > + }
>> > +
>> > + /*
>> > + * calculate SCL clock
>> > + * see
>> > + * ICCCR
>> > + *
>> > + * ick = clkp / (1 + CDF)
>> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
>> > + *
>> > + * ick : I2C internal clock < 20 MHz
>> > + * ticf : I2C SCL falling time = 35 ns here
>> > + * tr : I2C SCL rising time = 200 ns here
>> > + * intd : LSI internal delay = 50 ns here
>> > + * clkp : peripheral_clk
>> > + * F[] : integer up-valuation
>> > + */
>> > + for (cdf = 0; cdf < 4; cdf++) {
>> > + ick = clk_get_rate(clkp) / (1 + cdf);
>> > + if (ick < 20000000)
>> > + goto ick_find;
>> > + }
>> > + dev_err(dev, "there is no best CDF\n");
>> > + return -EIO;
>> > +
>> > +ick_find:
>> > + /*
>> > + * it is impossible to calculate large scale
>> > + * number on u32. separate it
>> > + *
>> > + * F[(ticf + tr + intd) * ick]
>> > + * = F[(35 + 200 + 50)ns * ick]
>> > + * = F[285 * ick / 1000000000]
>> > + * = F[(ick / 1000000) * 285 / 1000]
>> > + */
>> > + round = (ick + 500000) / 1000000 * 285;
>> > + round = (round + 500) / 1000;
>> > +
>> > + /*
>> > + * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
>> > + *
>> > + * Calculation result (= SCL) should be less than
>> > + * bus_speed for hardware safety
>> > + */
>> > + for (scgd = 0; scgd < 0x40; scgd++) {
>> > + scl = ick / (20 + (scgd * 8) + round);
>> > + if (scl <= bus_speed)
>> > + goto scgd_find;
>> > + }
>> > + dev_err(dev, "it is impossible to calculate best SCL\n");
>> > + return -EIO;
>> > +
>> > +scgd_find:
>> > + dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
>> > + scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
>> > +
>> > + /*
>> > + * keep icccr value
>> > + */
>> > + priv->icccr = (scgd << 2 | cdf);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
>> > +{
>> > + rcar_i2c_write(priv, ICCCR, priv->icccr);
>> > +}
>> > +
>> > +/*
>> > + * status functions
>> > + */
>> > +static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
>> > +{
>> > + return rcar_i2c_read(priv, ICMSR);
>> > +}
>> > +
>> > +#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
>> > +static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
>> > +{
>> > + rcar_i2c_write(priv, ICMSR, ~bit);
>> > +}
>> > +
>> > +/*
>> > + * recv/send functions
>> > + */
>> > +static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
>> > +{
>> > + rcar_i2c_set_addr(priv, 1);
>> > + rcar_i2c_status_clear(priv);
>> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
>> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static int rcar_i2c_send(struct rcar_i2c_priv *priv)
>> > +{
>> > + int ret;
>> > +
>> > + /*
>> > + * It should check bus status when send case
>> > + */
>> > + ret = rcar_i2c_bus_barrier(priv);
>> > + if (ret < 0)
>> > + return ret;
>> > +
>> > + rcar_i2c_set_addr(priv, 0);
>> > + rcar_i2c_status_clear(priv);
>> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
>> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
>> > +#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
>> > +
>> > +/*
>> > + * interrupt functions
>> > + */
>> > +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
>> > +{
>> > + struct i2c_msg *msg = priv->msg;
>> > +
>> > + /*
>> > + * FIXME
>> > + * sometimes, unknown interrupt happened.
>> > + * Do nothing
>> > + */
>> > + if (!(msr & MDE))
>> > + return 0;
>> > +
>> > + /*
>> > + * If address transfer phase finished,
>> > + * goto data phase.
>> > + */
>> > + if (msr & MAT)
>> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
>> > +
>> > + if (priv->pos < msg->len) {
>> > + /*
>> > + * Prepare next data to ICRXTX register.
>> > + * This data will go to _SHIFT_ register.
>> > + *
>> > + * *
>> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
>> > + */
>> > + rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
>> > + priv->pos++;
>> > +
>> > + } else {
>> > + /*
>> > + * The last data was pushed to ICRXTX on _PREV_ empty irq.
>> > + * It is on _SHIFT_ register, and will sent to I2C bus.
>> > + *
>> > + * *
>> > + * [ICRXTX] -> [SHIFT] -> [I2C bus]
>> > + */
>> > +
>> > + if (priv->flags & ID_LAST_MSG)
>> > + /*
>> > + * If current msg is the _LAST_ msg,
>> > + * prepare stop condition here.
>> > + * ID_DONE will be set on STOP irq.
>> > + */
>> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
>> > + else
>> > + /*
>> > + * If current msg is _NOT_ last msg,
>> > + * it doesn't call stop phase.
>> > + * thus, there is no STOP irq.
>> > + * return ID_DONE here.
>> > + */
>> > + return ID_DONE;
>> > + }
>> > +
>> > + rcar_i2c_send_restart(priv);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
>> > +{
>> > + struct i2c_msg *msg = priv->msg;
>> > +
>> > + /*
>> > + * FIXME
>> > + * sometimes, unknown interrupt happened.
>> > + * Do nothing
>> > + */
>> > + if (!(msr & MDR))
>> > + return 0;
>> > +
>> > + if (msr & MAT) {
>> > + /*
>> > + * Address transfer phase finished,
>> > + * but, there is no data at this point.
>> > + * Do nothing.
>> > + */
>> > + } else if (priv->pos < msg->len) {
>> > + /*
>> > + * get received data
>> > + */
>> > + msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
>> > + priv->pos++;
>> > + }
>> > +
>> > + /*
>> > + * If next received data is the last data,
>> > + * I2C driver should send NACK (or STOP).
>> > + * But this chip can't send NACK, thus, go to STOP phase.
>> did not understand this.
>
> OK. fix
>
>>
>> > + * Otherwise, go to data phase.
>> > + */
>> > + if (priv->pos + 1 >= msg->len)
>> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
>> > + else
>> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
>> > +
>> > + rcar_i2c_recv_restart(priv);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
>> > +{
>> > + struct rcar_i2c_priv *priv = ptr;
>> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
>> > + u32 msr;
>> > +
>> > + /*-------------- spin lock -----------------*/
>> > + spin_lock(&priv->lock);
>> > +
>> > + msr = rcar_i2c_status_get(priv);
>> > +
>> > + /*
>> > + * Arbitration lost
>> > + */
>> > + if (msr & MAL) {
>> > + /*
>> > + * CAUTION
>> > + *
>> > + * When arbitration lost, device become _slave_ mode.
>> > + */
>> > + dev_dbg(dev, "Arbitration Lost\n");
>> > + rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
>> > + goto out;
>> > + }
>> > +
>> > + /*
>> > + * Stop
>> > + */
>> > + if (msr & MST) {
>> > + dev_dbg(dev, "Stop\n");
>> > + rcar_i2c_flags_set(priv, ID_DONE);
>> > + goto out;
>> > + }
>> > +
>> > + /*
>> > + * Nack
>> > + */
>> > + if (msr & MNR) {
>> > + dev_dbg(dev, "Nack\n");
>> > +
>> > + /* go to stop phase */
>> > + rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
>> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
>> > + rcar_i2c_flags_set(priv, ID_NACK);
>> > + goto out;
>> > + }
>> > +
>> > + /*
>> > + * recv/send
>> > + */
>> > + if (rcar_i2c_is_recv(priv))
>> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
>> > + else
>> > + rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
>> > +
>> > +out:
>> > + if (rcar_i2c_flags_has(priv, ID_DONE)) {
>> > + rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
>> > + rcar_i2c_status_clear(priv);
>> > + wake_up(&priv->wait);
>> > + }
>> > +
>> > + spin_unlock(&priv->lock);
>> > + /*-------------- spin unlock -----------------*/
>> > +
>> > + return IRQ_HANDLED;
>> > +}
>> > +
>> > +static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
>> > + struct i2c_msg *msgs,
>> > + int num)
>> > +{
>> > + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
>> > + struct device *dev = rcar_i2c_priv_to_dev(priv);
>> > + unsigned long flags;
>> > + int i, ret, timeout;
>> > +
>> > + /*================== enable i2c device ===================*/
>> > + pm_runtime_get_sync(dev);
>> > +
>> > + /*-------------- spin lock -----------------*/
>> > + spin_lock_irqsave(&priv->lock, flags);
>> > +
>> > + rcar_i2c_init(priv);
>> > + rcar_i2c_clock_start(priv);
>> > +
>> > + spin_unlock_irqrestore(&priv->lock, flags);
>> > + /*-------------- spin unlock -----------------*/
>> > +
>> > + ret = -EINVAL;
>> > + for (i = 0; i < num; i++) {
>> > + /*-------------- spin lock -----------------*/
>> > + spin_lock_irqsave(&priv->lock, flags);
>> > +
>> > + /* init each data */
>> > + priv->msg = &msgs[i];
>> > + priv->pos = 0;
>> > + priv->flags = 0;
>> > + if (priv->msg == &msgs[num - 1])
>> > + rcar_i2c_flags_set(priv, ID_LAST_MSG);
>> > +
>> > + /* start send/recv */
>> > + if (rcar_i2c_is_recv(priv))
>> > + ret = rcar_i2c_recv(priv);
>> > + else
>> > + ret = rcar_i2c_send(priv);
>> > +
>> > + spin_unlock_irqrestore(&priv->lock, flags);
>> > + /*-------------- spin unlock -----------------*/
>> > +
>> > + if (ret < 0)
>> > + break;
>> > +
>> > + /*
>> > + * wait result
>> > + */
>> > + timeout = wait_event_timeout(priv->wait,
>> > + rcar_i2c_flags_has(priv, ID_DONE),
>> > + 5 * HZ);
>> > + if (!timeout) {
>> > + ret = -ETIMEDOUT;
>> > + break;
>> > + }
>> > +
>> > + /*
>> > + * error handling
>> > + */
>> > + if (rcar_i2c_flags_has(priv, ID_NACK)) {
>> > + ret = -EREMOTEIO;
>> > + break;
>> > + }
>> > +
>> > + if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
>> > + ret = -EAGAIN;
>> > + break;
>> > + }
>> > +
>> > + if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
>> > + ret = -EIO;
>> > + break;
>> > + }
>> > +
>> > + ret = i + 1; /* The number of transfer */
>> > + }
>> > +
>> > + pm_runtime_put_sync(dev);
>> you could consider put here? The difference is that it is not synchronous.
>
> Do you mean "use pm_runtime_put()" ?
> then should I use pm_runtime_get() too ?
>
I do not think so , when get it needs to be synchronous as you may
access the registers.
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v3] i2c: add Renesas R-Car I2C driver
[not found] ` <CAM=Q2cuGiNnduVa+nnNV_02dYwfrSJoFGesiFBm6Q5U3SGQXpQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-09-28 5:56 ` Kuninori Morimoto
@ 2012-09-28 6:44 ` Kuninori Morimoto
[not found] ` <877gred4u1.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
1 sibling, 1 reply; 21+ messages in thread
From: Kuninori Morimoto @ 2012-09-28 6:44 UTC (permalink / raw)
To: Shubhrajyoti Datta, Wolfram Sang, Jean Delvare, Ben Dooks
Cc: Simon, Magnus, Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
R-Car I2C is similar with SH7760 I2C.
But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
This patch creates new Renesas R-Car I2C driver.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
v2 -> v3
- used simple comment on rcar_i2c_irq_recv()
- used LOOP_TIMEOUT
- used pm_runtime_put()
- used devm_request_irq()
drivers/i2c/busses/Kconfig | 10 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-rcar.c | 709 +++++++++++++++++++++++++++++++++++++++++
include/linux/i2c/i2c-rcar.h | 10 +
4 files changed, 730 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-rcar.c
create mode 100644 include/linux/i2c/i2c-rcar.h
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index b4aaa1b..51baa08 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -709,6 +709,16 @@ config I2C_XLR
This driver can also be built as a module. If so, the module
will be called i2c-xlr.
+config I2C_RCAR
+ tristate "Renesas R-Car I2C Controller"
+ depends on ARCH_SHMOBILE && I2C
+ help
+ If you say yes to this option, support will be included for the
+ R-Car I2C controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-rcar.
+
comment "External I2C/SMBus adapter drivers"
config I2C_DIOLAN_U2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ce3c2be..e98ff51 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
+obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
new file mode 100644
index 0000000..f9399d1
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -0,0 +1,709 @@
+/*
+ * drivers/i2c/busses/i2c-rcar.c
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
+ *
+ * This file is based on the drivers/i2c/busses/i2c-sh7760.c
+ * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau-XXGV8rIhxyHQT0dZR+AlfA@public.gmane.org>
+ *
+ * This file used out-of-tree driver i2c-rcar.c
+ * Copyright (C) 2011-2012 Renesas Electronics Corporation
+ *
+ * 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
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c/i2c-rcar.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* register offsets */
+#define ICSCR 0x00 /* slave ctrl */
+#define ICMCR 0x04 /* master ctrl */
+#define ICSSR 0x08 /* slave status */
+#define ICMSR 0x0C /* master status */
+#define ICSIER 0x10 /* slave irq enable */
+#define ICMIER 0x14 /* master irq enable */
+#define ICCCR 0x18 /* clock dividers */
+#define ICSAR 0x1C /* slave address */
+#define ICMAR 0x20 /* master address */
+#define ICRXTX 0x24 /* data port */
+
+/* ICMCR */
+#define MDBS (1 << 7) /* non-fifo mode switch */
+#define FSCL (1 << 6) /* override SCL pin */
+#define FSDA (1 << 5) /* override SDA pin */
+#define OBPC (1 << 4) /* override pins */
+#define MIE (1 << 3) /* master if enable */
+#define TSBE (1 << 2)
+#define FSB (1 << 1) /* force stop bit */
+#define ESG (1 << 0) /* en startbit gen */
+
+/* ICMSR */
+#define MNR (1 << 6) /* nack received */
+#define MAL (1 << 5) /* arbitration lost */
+#define MST (1 << 4) /* sent a stop */
+#define MDE (1 << 3)
+#define MDT (1 << 2)
+#define MDR (1 << 1)
+#define MAT (1 << 0) /* slave addr xfer done */
+
+/* ICMIE */
+#define MNRE (1 << 6) /* nack irq en */
+#define MALE (1 << 5) /* arblos irq en */
+#define MSTE (1 << 4) /* stop irq en */
+#define MDEE (1 << 3)
+#define MDTE (1 << 2)
+#define MDRE (1 << 1)
+#define MATE (1 << 0) /* address sent irq en */
+
+
+enum {
+ RCAR_BUS_PHASE_ADDR,
+ RCAR_BUS_PHASE_DATA,
+ RCAR_BUS_PHASE_STOP,
+};
+
+enum {
+ RCAR_IRQ_CLOSE,
+ RCAR_IRQ_OPEN_FOR_SEND,
+ RCAR_IRQ_OPEN_FOR_RECV,
+ RCAR_IRQ_OPEN_FOR_STOP,
+};
+
+/*
+ * flags
+ */
+#define ID_LAST_MSG (1 << 0)
+#define ID_IOERROR (1 << 1)
+#define ID_DONE (1 << 2)
+#define ID_ARBLOST (1 << 3)
+#define ID_NACK (1 << 4)
+
+struct rcar_i2c_priv {
+ void __iomem *io;
+ struct i2c_adapter adap;
+ struct i2c_msg *msg;
+
+ spinlock_t lock;
+ wait_queue_head_t wait;
+
+ int pos;
+ int irq;
+ u32 icccr;
+ u32 flags;
+};
+
+#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
+#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
+
+#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
+#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
+
+#define LOOP_TIMEOUT 1024
+
+/*
+ * basic functions
+ */
+static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
+{
+ writel(val, priv->io + reg);
+}
+
+static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
+{
+ return readl(priv->io + reg);
+}
+
+static void rcar_i2c_init(struct rcar_i2c_priv *priv)
+{
+ /*
+ * reset slave mode.
+ * slave mode is not used on this driver
+ */
+ rcar_i2c_write(priv, ICSIER, 0);
+ rcar_i2c_write(priv, ICSAR, 0);
+ rcar_i2c_write(priv, ICSCR, 0);
+ rcar_i2c_write(priv, ICSSR, 0);
+
+ /* reset master mode */
+ rcar_i2c_write(priv, ICMIER, 0);
+ rcar_i2c_write(priv, ICMCR, 0);
+ rcar_i2c_write(priv, ICMSR, 0);
+ rcar_i2c_write(priv, ICMAR, 0);
+}
+
+static void rcar_i2c_irq_mask(struct rcar_i2c_priv *priv, int open)
+{
+ u32 val = MNRE | MALE | MSTE | MATE; /* default */
+
+ switch (open) {
+ case RCAR_IRQ_OPEN_FOR_SEND:
+ val |= MDEE; /* default + send */
+ break;
+ case RCAR_IRQ_OPEN_FOR_RECV:
+ val |= MDRE; /* default + read */
+ break;
+ case RCAR_IRQ_OPEN_FOR_STOP:
+ val = MSTE; /* stop irq only */
+ break;
+ case RCAR_IRQ_CLOSE:
+ default:
+ val = 0; /* all close */
+ break;
+ }
+ rcar_i2c_write(priv, ICMIER, val);
+}
+
+static void rcar_i2c_set_addr(struct rcar_i2c_priv *priv, u32 recv)
+{
+ rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | recv);
+}
+
+/*
+ * bus control functions
+ */
+static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
+{
+ int i;
+
+ for (i = 0; i < LOOP_TIMEOUT; i++) {
+ /* make sure that bus is not busy */
+ if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
+ return 0;
+ udelay(1);
+ }
+
+ return -EBUSY;
+}
+
+static void rcar_i2c_bus_phase(struct rcar_i2c_priv *priv, int phase)
+{
+ switch (phase) {
+ case RCAR_BUS_PHASE_ADDR:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE | ESG);
+ break;
+ case RCAR_BUS_PHASE_DATA:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE);
+ break;
+ case RCAR_BUS_PHASE_STOP:
+ rcar_i2c_write(priv, ICMCR, MDBS | MIE | FSB);
+ break;
+ }
+}
+
+/*
+ * clock function
+ */
+static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
+ u32 bus_speed,
+ struct device *dev)
+{
+ struct clk *clkp = clk_get(NULL, "peripheral_clk");
+ u32 scgd, cdf;
+ u32 round, ick;
+ u32 scl;
+
+ if (!clkp) {
+ dev_err(dev, "there is no peripheral_clk\n");
+ return -EIO;
+ }
+
+ /*
+ * calculate SCL clock
+ * see
+ * ICCCR
+ *
+ * ick = clkp / (1 + CDF)
+ * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
+ *
+ * ick : I2C internal clock < 20 MHz
+ * ticf : I2C SCL falling time = 35 ns here
+ * tr : I2C SCL rising time = 200 ns here
+ * intd : LSI internal delay = 50 ns here
+ * clkp : peripheral_clk
+ * F[] : integer up-valuation
+ */
+ for (cdf = 0; cdf < 4; cdf++) {
+ ick = clk_get_rate(clkp) / (1 + cdf);
+ if (ick < 20000000)
+ goto ick_find;
+ }
+ dev_err(dev, "there is no best CDF\n");
+ return -EIO;
+
+ick_find:
+ /*
+ * it is impossible to calculate large scale
+ * number on u32. separate it
+ *
+ * F[(ticf + tr + intd) * ick]
+ * = F[(35 + 200 + 50)ns * ick]
+ * = F[285 * ick / 1000000000]
+ * = F[(ick / 1000000) * 285 / 1000]
+ */
+ round = (ick + 500000) / 1000000 * 285;
+ round = (round + 500) / 1000;
+
+ /*
+ * SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
+ *
+ * Calculation result (= SCL) should be less than
+ * bus_speed for hardware safety
+ */
+ for (scgd = 0; scgd < 0x40; scgd++) {
+ scl = ick / (20 + (scgd * 8) + round);
+ if (scl <= bus_speed)
+ goto scgd_find;
+ }
+ dev_err(dev, "it is impossible to calculate best SCL\n");
+ return -EIO;
+
+scgd_find:
+ dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
+ scl, bus_speed, clk_get_rate(clkp), round, cdf, scgd);
+
+ /*
+ * keep icccr value
+ */
+ priv->icccr = (scgd << 2 | cdf);
+
+ return 0;
+}
+
+static void rcar_i2c_clock_start(struct rcar_i2c_priv *priv)
+{
+ rcar_i2c_write(priv, ICCCR, priv->icccr);
+}
+
+/*
+ * status functions
+ */
+static u32 rcar_i2c_status_get(struct rcar_i2c_priv *priv)
+{
+ return rcar_i2c_read(priv, ICMSR);
+}
+
+#define rcar_i2c_status_clear(priv) rcar_i2c_status_bit_clear(priv, 0xffffffff)
+static void rcar_i2c_status_bit_clear(struct rcar_i2c_priv *priv, u32 bit)
+{
+ rcar_i2c_write(priv, ICMSR, ~bit);
+}
+
+/*
+ * recv/send functions
+ */
+static int rcar_i2c_recv(struct rcar_i2c_priv *priv)
+{
+ rcar_i2c_set_addr(priv, 1);
+ rcar_i2c_status_clear(priv);
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_RECV);
+
+ return 0;
+}
+
+static int rcar_i2c_send(struct rcar_i2c_priv *priv)
+{
+ int ret;
+
+ /*
+ * It should check bus status when send case
+ */
+ ret = rcar_i2c_bus_barrier(priv);
+ if (ret < 0)
+ return ret;
+
+ rcar_i2c_set_addr(priv, 0);
+ rcar_i2c_status_clear(priv);
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_ADDR);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_SEND);
+
+ return 0;
+}
+
+#define rcar_i2c_send_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDE))
+#define rcar_i2c_recv_restart(priv) rcar_i2c_status_bit_clear(priv, (MAT | MDR))
+
+/*
+ * interrupt functions
+ */
+static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ /*
+ * FIXME
+ * sometimes, unknown interrupt happened.
+ * Do nothing
+ */
+ if (!(msr & MDE))
+ return 0;
+
+ /*
+ * If address transfer phase finished,
+ * goto data phase.
+ */
+ if (msr & MAT)
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
+
+ if (priv->pos < msg->len) {
+ /*
+ * Prepare next data to ICRXTX register.
+ * This data will go to _SHIFT_ register.
+ *
+ * *
+ * [ICRXTX] -> [SHIFT] -> [I2C bus]
+ */
+ rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
+ priv->pos++;
+
+ } else {
+ /*
+ * The last data was pushed to ICRXTX on _PREV_ empty irq.
+ * It is on _SHIFT_ register, and will sent to I2C bus.
+ *
+ * *
+ * [ICRXTX] -> [SHIFT] -> [I2C bus]
+ */
+
+ if (priv->flags & ID_LAST_MSG)
+ /*
+ * If current msg is the _LAST_ msg,
+ * prepare stop condition here.
+ * ID_DONE will be set on STOP irq.
+ */
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ else
+ /*
+ * If current msg is _NOT_ last msg,
+ * it doesn't call stop phase.
+ * thus, there is no STOP irq.
+ * return ID_DONE here.
+ */
+ return ID_DONE;
+ }
+
+ rcar_i2c_send_restart(priv);
+
+ return 0;
+}
+
+static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ /*
+ * FIXME
+ * sometimes, unknown interrupt happened.
+ * Do nothing
+ */
+ if (!(msr & MDR))
+ return 0;
+
+ if (msr & MAT) {
+ /*
+ * Address transfer phase finished,
+ * but, there is no data at this point.
+ * Do nothing.
+ */
+ } else if (priv->pos < msg->len) {
+ /*
+ * get received data
+ */
+ msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
+ priv->pos++;
+ }
+
+ /*
+ * If next received data is the _LAST_,
+ * go to STOP phase,
+ * otherwise, go to DATA phase.
+ */
+ if (priv->pos + 1 >= msg->len)
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ else
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_DATA);
+
+ rcar_i2c_recv_restart(priv);
+
+ return 0;
+}
+
+static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
+{
+ struct rcar_i2c_priv *priv = ptr;
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ u32 msr;
+
+ /*-------------- spin lock -----------------*/
+ spin_lock(&priv->lock);
+
+ msr = rcar_i2c_status_get(priv);
+
+ /*
+ * Arbitration lost
+ */
+ if (msr & MAL) {
+ /*
+ * CAUTION
+ *
+ * When arbitration lost, device become _slave_ mode.
+ */
+ dev_dbg(dev, "Arbitration Lost\n");
+ rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
+ goto out;
+ }
+
+ /*
+ * Stop
+ */
+ if (msr & MST) {
+ dev_dbg(dev, "Stop\n");
+ rcar_i2c_flags_set(priv, ID_DONE);
+ goto out;
+ }
+
+ /*
+ * Nack
+ */
+ if (msr & MNR) {
+ dev_dbg(dev, "Nack\n");
+
+ /* go to stop phase */
+ rcar_i2c_bus_phase(priv, RCAR_BUS_PHASE_STOP);
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_OPEN_FOR_STOP);
+ rcar_i2c_flags_set(priv, ID_NACK);
+ goto out;
+ }
+
+ /*
+ * recv/send
+ */
+ if (rcar_i2c_is_recv(priv))
+ rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
+ else
+ rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
+
+out:
+ if (rcar_i2c_flags_has(priv, ID_DONE)) {
+ rcar_i2c_irq_mask(priv, RCAR_IRQ_CLOSE);
+ rcar_i2c_status_clear(priv);
+ wake_up(&priv->wait);
+ }
+
+ spin_unlock(&priv->lock);
+ /*-------------- spin unlock -----------------*/
+
+ return IRQ_HANDLED;
+}
+
+static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs,
+ int num)
+{
+ struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+ unsigned long flags;
+ int i, ret, timeout;
+
+ pm_runtime_get_sync(dev);
+
+ /*-------------- spin lock -----------------*/
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rcar_i2c_init(priv);
+ rcar_i2c_clock_start(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ /*-------------- spin unlock -----------------*/
+
+ ret = -EINVAL;
+ for (i = 0; i < num; i++) {
+ /*-------------- spin lock -----------------*/
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* init each data */
+ priv->msg = &msgs[i];
+ priv->pos = 0;
+ priv->flags = 0;
+ if (priv->msg == &msgs[num - 1])
+ rcar_i2c_flags_set(priv, ID_LAST_MSG);
+
+ /* start send/recv */
+ if (rcar_i2c_is_recv(priv))
+ ret = rcar_i2c_recv(priv);
+ else
+ ret = rcar_i2c_send(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ /*-------------- spin unlock -----------------*/
+
+ if (ret < 0)
+ break;
+
+ /*
+ * wait result
+ */
+ timeout = wait_event_timeout(priv->wait,
+ rcar_i2c_flags_has(priv, ID_DONE),
+ 5 * HZ);
+ if (!timeout) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ /*
+ * error handling
+ */
+ if (rcar_i2c_flags_has(priv, ID_NACK)) {
+ ret = -EREMOTEIO;
+ break;
+ }
+
+ if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
+ ret = -EIO;
+ break;
+ }
+
+ ret = i + 1; /* The number of transfer */
+ }
+
+ pm_runtime_put(dev);
+
+ if (ret < 0)
+ dev_err(dev, "error %d : %x\n", ret, priv->flags);
+
+ return ret;
+}
+
+static u32 rcar_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm rcar_i2c_algo = {
+ .master_xfer = rcar_i2c_master_xfer,
+ .functionality = rcar_i2c_func,
+};
+
+static int __devinit rcar_i2c_probe(struct platform_device *pdev)
+{
+ struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
+ struct rcar_i2c_priv *priv;
+ struct i2c_adapter *adap;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ u32 bus_speed;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no mmio resources\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(dev, "no mem for private data\n");
+ return -ENOMEM;
+ }
+
+ bus_speed = 100000; /* default 100 kHz */
+ if (pdata && pdata->bus_speed)
+ bus_speed = pdata->bus_speed;
+ ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
+ if (ret < 0)
+ return ret;
+
+ priv->io = devm_ioremap(dev, res->start, resource_size(res));
+ if (!priv->io) {
+ dev_err(dev, "cannot ioremap\n");
+ return -ENODEV;
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ init_waitqueue_head(&priv->wait);
+ spin_lock_init(&priv->lock);
+
+ adap = &priv->adap;
+ adap->nr = pdev->id;
+ adap->algo = &rcar_i2c_algo;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ adap->retries = 3;
+ adap->dev.parent = dev;
+ i2c_set_adapdata(adap, priv);
+ strlcpy(adap->name, pdev->name, sizeof(adap->name));
+
+ ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0,
+ dev_name(dev), priv);
+ if (ret < 0) {
+ dev_err(dev, "cannot get irq %d\n", priv->irq);
+ return ret;
+ }
+
+ ret = i2c_add_numbered_adapter(adap);
+ if (ret < 0) {
+ dev_err(dev, "reg adap failed: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_enable(dev);
+ platform_set_drvdata(pdev, priv);
+
+ dev_info(dev, "probed\n");
+
+ return 0;
+}
+
+static int __devexit rcar_i2c_remove(struct platform_device *pdev)
+{
+ struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ i2c_del_adapter(&priv->adap);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static struct platform_driver rcar_i2c_drv = {
+ .driver = {
+ .name = "i2c-rcar",
+ .owner = THIS_MODULE,
+ },
+ .probe = rcar_i2c_probe,
+ .remove = __devexit_p(rcar_i2c_remove),
+};
+
+module_platform_driver(rcar_i2c_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car I2C bus driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>");
diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h
new file mode 100644
index 0000000..496f5c2
--- /dev/null
+++ b/include/linux/i2c/i2c-rcar.h
@@ -0,0 +1,10 @@
+#ifndef __I2C_R_CAR_H__
+#define __I2C_R_CAR_H__
+
+#include <linux/platform_device.h>
+
+struct i2c_rcar_platform_data {
+ u32 bus_speed;
+};
+
+#endif /* __I2C_R_CAR_H__ */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v3] i2c: add Renesas R-Car I2C driver
[not found] ` <877gred4u1.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
@ 2012-10-08 9:47 ` Wolfram Sang
2012-10-31 6:21 ` Simon Horman
1 sibling, 0 replies; 21+ messages in thread
From: Wolfram Sang @ 2012-10-08 9:47 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Shubhrajyoti Datta, Jean Delvare, Ben Dooks, Simon, Magnus,
Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
[-- Attachment #1: Type: text/plain, Size: 2701 bytes --]
On Thu, Sep 27, 2012 at 11:44:25PM -0700, Kuninori Morimoto wrote:
> R-Car I2C is similar with SH7760 I2C.
> But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> This patch creates new Renesas R-Car I2C driver.
>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
Only minor issues which might be fixed later. Applied to -next.
> +static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
> +{
> + writel(val, priv->io + reg);
> +}
> +
> +static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
> +{
> + return readl(priv->io + reg);
> +}
Not sure if those needed encapsulation.
...
> +static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
> +{
> + struct i2c_msg *msg = priv->msg;
> +
> + /*
> + * FIXME
> + * sometimes, unknown interrupt happened.
> + * Do nothing
> + */
> + if (!(msr & MDE))
> + return 0;
Uh, is this a known hardware flaw?
...
> +static int __devinit rcar_i2c_probe(struct platform_device *pdev)
> +{
> + struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
> + struct rcar_i2c_priv *priv;
> + struct i2c_adapter *adap;
> + struct resource *res;
> + struct device *dev = &pdev->dev;
> + u32 bus_speed;
> + int ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "no mmio resources\n");
> + return -ENODEV;
> + }
> +
> + priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(dev, "no mem for private data\n");
> + return -ENOMEM;
> + }
> +
> + bus_speed = 100000; /* default 100 kHz */
> + if (pdata && pdata->bus_speed)
> + bus_speed = pdata->bus_speed;
> + ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
> + if (ret < 0)
> + return ret;
> +
> + priv->io = devm_ioremap(dev, res->start, resource_size(res));
devm_request_mem_region is missing. Or use the helper
devm_request_and_ioremap.
> + if (!priv->io) {
> + dev_err(dev, "cannot ioremap\n");
> + return -ENODEV;
> + }
> +
> + priv->irq = platform_get_irq(pdev, 0);
> + init_waitqueue_head(&priv->wait);
> + spin_lock_init(&priv->lock);
> +
> + adap = &priv->adap;
> + adap->nr = pdev->id;
> + adap->algo = &rcar_i2c_algo;
> + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> + adap->retries = 3;
Do you really need the class?
Rest looks good.
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v3] i2c: add Renesas R-Car I2C driver
[not found] ` <877gred4u1.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-10-08 9:47 ` Wolfram Sang
@ 2012-10-31 6:21 ` Simon Horman
[not found] ` <20121031062128.GA31555-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
1 sibling, 1 reply; 21+ messages in thread
From: Simon Horman @ 2012-10-31 6:21 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Shubhrajyoti Datta, Wolfram Sang, Jean Delvare, Ben Dooks, Magnus,
Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
On Thu, Sep 27, 2012 at 11:44:25PM -0700, Kuninori Morimoto wrote:
> R-Car I2C is similar with SH7760 I2C.
> But the SH7760 I2C driver had many workaround operations, since H/W had bugs.
> Thus, it was pointless to keep compatible between SH7760 and R-Car I2C drivers.
> This patch creates new Renesas R-Car I2C driver.
Hi,
I am seeing the current section miss-matches, which appear to relate to
this patch, in linux/master (v3.6-rc3+).
make CONFIG_DEBUG_SECTION_MISMATCH=y
...
WARNING: vmlinux.o(.data+0x11798): Section mismatch in reference from the variable rcar_i2c_drv to the function .devinit.text:rcar_i2c_probe()
The variable rcar_i2c_drv references
the function __devinit rcar_i2c_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: vmlinux.o(.data+0x1179c): Section mismatch in reference from the variable rcar_i2c_drv to the function .devexit.text:rcar_i2c_remove()
The variable rcar_i2c_drv references
the function __devexit rcar_i2c_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH] i2c: rcar: fixup section mismatch on rcar_i2c_probe/remove()
[not found] ` <20121031062128.GA31555-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
@ 2012-10-31 6:44 ` Kuninori Morimoto
[not found] ` <877gq7m97r.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
0 siblings, 1 reply; 21+ messages in thread
From: Kuninori Morimoto @ 2012-10-31 6:44 UTC (permalink / raw)
To: Simon Horman
Cc: Shubhrajyoti Datta, Wolfram Sang, Jean Delvare, Ben Dooks, Magnus,
Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
This patch fixup below WARNING
make CONFIG_DEBUG_SECTION_MISMATCH=y
...
WARNING: vmlinux.o(.data+0x11798): Section mismatch in reference from the variable rcar_i2c_drv to the function .devinit.text:rcar_i2c_probe()
The variable rcar_i2c_drv references
the function __devinit rcar_i2c_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: vmlinux.o(.data+0x1179c): Section mismatch in reference from the variable rcar_i2c_drv to the function .devexit.text:rcar_i2c_remove()
The variable rcar_i2c_drv references
the function __devexit rcar_i2c_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
Reported-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
---
Simon
Is this patch solve your issue ?
drivers/i2c/busses/i2c-rcar.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index f9399d1..e10e04b 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -613,7 +613,7 @@ static const struct i2c_algorithm rcar_i2c_algo = {
.functionality = rcar_i2c_func,
};
-static int __devinit rcar_i2c_probe(struct platform_device *pdev)
+static int rcar_i2c_probe(struct platform_device *pdev)
{
struct i2c_rcar_platform_data *pdata = pdev->dev.platform_data;
struct rcar_i2c_priv *priv;
@@ -682,7 +682,7 @@ static int __devinit rcar_i2c_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit rcar_i2c_remove(struct platform_device *pdev)
+static int rcar_i2c_remove(struct platform_device *pdev)
{
struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: rcar: fixup section mismatch on rcar_i2c_probe/remove()
[not found] ` <877gq7m97r.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
@ 2012-11-01 0:22 ` Simon Horman
2012-11-13 10:23 ` Wolfram Sang
1 sibling, 0 replies; 21+ messages in thread
From: Simon Horman @ 2012-11-01 0:22 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Shubhrajyoti Datta, Wolfram Sang, Jean Delvare, Ben Dooks, Magnus,
Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
On Tue, Oct 30, 2012 at 11:44:44PM -0700, Kuninori Morimoto wrote:
> This patch fixup below WARNING
>
> make CONFIG_DEBUG_SECTION_MISMATCH=y
> ...
> WARNING: vmlinux.o(.data+0x11798): Section mismatch in reference from the variable rcar_i2c_drv to the function .devinit.text:rcar_i2c_probe()
> The variable rcar_i2c_drv references
> the function __devinit rcar_i2c_probe()
> If the reference is valid then annotate the
> variable with __init* or __refdata (see linux/init.h) or name the variable:
> *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
>
> WARNING: vmlinux.o(.data+0x1179c): Section mismatch in reference from the variable rcar_i2c_drv to the function .devexit.text:rcar_i2c_remove()
> The variable rcar_i2c_drv references
> the function __devexit rcar_i2c_remove()
> If the reference is valid then annotate the
> variable with __exit* (see linux/init.h) or name the variable:
> *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
>
> Reported-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
> Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
> ---
> Simon
>
> Is this patch solve your issue ?
Yes, thanks.
Tested-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH] i2c: rcar: fixup section mismatch on rcar_i2c_probe/remove()
[not found] ` <877gq7m97r.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-11-01 0:22 ` Simon Horman
@ 2012-11-13 10:23 ` Wolfram Sang
1 sibling, 0 replies; 21+ messages in thread
From: Wolfram Sang @ 2012-11-13 10:23 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: Simon Horman, Shubhrajyoti Datta, Jean Delvare, Ben Dooks, Magnus,
Linux-I2C, Kuninori Morimoto,
phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
[-- Attachment #1: Type: text/plain, Size: 1422 bytes --]
Hi,
On Tue, Oct 30, 2012 at 11:44:44PM -0700, Kuninori Morimoto wrote:
> This patch fixup below WARNING
Yes, but you forgot to remove the __devexit_p reference from the driver
struct. Anyhow...
>
> make CONFIG_DEBUG_SECTION_MISMATCH=y
> ...
> WARNING: vmlinux.o(.data+0x11798): Section mismatch in reference from the variable rcar_i2c_drv to the function .devinit.text:rcar_i2c_probe()
> The variable rcar_i2c_drv references
> the function __devinit rcar_i2c_probe()
> If the reference is valid then annotate the
> variable with __init* or __refdata (see linux/init.h) or name the variable:
> *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
>
> WARNING: vmlinux.o(.data+0x1179c): Section mismatch in reference from the variable rcar_i2c_drv to the function .devexit.text:rcar_i2c_remove()
> The variable rcar_i2c_drv references
> the function __devexit rcar_i2c_remove()
> If the reference is valid then annotate the
> variable with __exit* (see linux/init.h) or name the variable:
> *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
^^^^^^^
... I like this solution better, renaming to driver. I did the patch
anyhow, so I'll post it in a minute.
Thanks,
Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2012-11-13 10:23 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-25 6:36 [PATCH] i2c: add Renesas R-Car I2C driver Kuninori Morimoto
[not found] ` <871uk04aa2.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-08-28 0:19 ` Kuninori Morimoto
2012-08-28 4:48 ` Shubhrajyoti Datta
[not found] ` <CAM=Q2ctETiVdyKBd_MxY2Ts=1XMPa_cdEJRkQpEySL6du9oLmw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-08-28 5:33 ` Kuninori Morimoto
[not found] ` <878vczd1hw.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-08-28 5:39 ` Shubhrajyoti Datta
[not found] ` <CAM=Q2cs5pFpVjqa_MFCLTFqdCo6SSm-1Ep=WtJ7fpABzWEGD9w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-08-28 7:56 ` Kuninori Morimoto
2012-08-28 8:12 ` Jean Delvare
[not found] ` <20120828101259.409b410b-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2012-08-28 8:37 ` [PATCH v2] " Kuninori Morimoto
[not found] ` <874nnnct03.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-09-28 1:57 ` Kuninori Morimoto
[not found] ` <878vbudi3v.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-09-28 3:49 ` Simon Horman
2012-09-28 5:21 ` Shubhrajyoti Datta
[not found] ` <CAM=Q2cuGiNnduVa+nnNV_02dYwfrSJoFGesiFBm6Q5U3SGQXpQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-09-28 5:56 ` Kuninori Morimoto
[not found] ` <w3pmx0an11d.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-09-28 6:25 ` Shubhrajyoti Datta
2012-09-28 6:44 ` [PATCH v3] " Kuninori Morimoto
[not found] ` <877gred4u1.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-10-08 9:47 ` Wolfram Sang
2012-10-31 6:21 ` Simon Horman
[not found] ` <20121031062128.GA31555-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
2012-10-31 6:44 ` [PATCH] i2c: rcar: fixup section mismatch on rcar_i2c_probe/remove() Kuninori Morimoto
[not found] ` <877gq7m97r.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
2012-11-01 0:22 ` Simon Horman
2012-11-13 10:23 ` Wolfram Sang
2012-08-28 6:26 ` [PATCH] i2c: add Renesas R-Car I2C driver phil.edworthy-zM6kxYcvzFBBDgjK7y7TUQ
[not found] ` <OF0AA2687D.3C2F6EFC-ON80257A56.003BB6CC-80257A68.0023665F-mWMTcI9IYFFWk0Htik3J/w@public.gmane.org>
2012-08-28 7:00 ` Kuninori Morimoto
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).