diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-04-11 15:19:26 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-04-11 15:19:26 -0700 |
commit | 9e528fe13c95cee1b4ee23690aebf8b02eebac1f (patch) | |
tree | f66470b8f1fed6353824caadbbe8fa02744dc583 | |
parent | 6f9fd9359daf4a4353bc78325a77e47dd78f60e3 (diff) | |
download | ltsi-kernel-9e528fe13c95cee1b4ee23690aebf8b02eebac1f.tar.gz |
marzen i2c patches added
-rw-r--r-- | patches.marzen/arm-shmobile-r8a7779-add-i2c-driver-support.patch | 127 | ||||
-rw-r--r-- | patches.marzen/i2c-add-renesas-r-car-i2c-driver.patch | 784 | ||||
-rw-r--r-- | patches.marzen/i2c-rcar-fix-section-mismatch.patch | 47 | ||||
-rw-r--r-- | patches.marzen/i2c-rcar-used-devm_request_and_ioremap-instead-of-devm_ioremap.patch | 31 | ||||
-rw-r--r-- | series | 4 |
5 files changed, 993 insertions, 0 deletions
diff --git a/patches.marzen/arm-shmobile-r8a7779-add-i2c-driver-support.patch b/patches.marzen/arm-shmobile-r8a7779-add-i2c-driver-support.patch new file mode 100644 index 00000000000000..cb0fa454ce8984 --- /dev/null +++ b/patches.marzen/arm-shmobile-r8a7779-add-i2c-driver-support.patch @@ -0,0 +1,127 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Wed Feb 27 20:03:20 2013 +From: Simon Horman <horms+renesas@verge.net.au> +Date: Thu, 28 Feb 2013 13:03:04 +0900 +Subject: [PATCH 4/4] ARM: shmobile: r8a7779: add I2C driver support +To: ltsi-dev@lists.linuxfoundation.org +Cc: Magnus Damm <magnus.damm@gmail.com>, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +Message-ID: <1362024184-27864-5-git-send-email-horms+renesas@verge.net.au> + + +From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + +This patch enable R-Car I2C driver + +Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +Signed-off-by: Simon Horman <horms@verge.net.au> +(cherry picked from commit ccc2a27b15c978334927cea9f1156f019a01b833) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + arch/arm/configs/marzen_defconfig | 2 + arch/arm/mach-shmobile/setup-r8a7779.c | 77 +++++++++++++++++++++++++++++++++ + 2 files changed, 79 insertions(+) + +--- a/arch/arm/configs/marzen_defconfig ++++ b/arch/arm/configs/marzen_defconfig +@@ -69,6 +69,8 @@ CONFIG_SERIAL_SH_SCI=y + CONFIG_SERIAL_SH_SCI_NR_UARTS=6 + CONFIG_SERIAL_SH_SCI_CONSOLE=y + # CONFIG_HW_RANDOM is not set ++CONFIG_I2C=y ++CONFIG_I2C_RCAR=y + CONFIG_GPIO_SYSFS=y + # CONFIG_HWMON is not set + CONFIG_SSB=y +--- a/arch/arm/mach-shmobile/setup-r8a7779.c ++++ b/arch/arm/mach-shmobile/setup-r8a7779.c +@@ -229,6 +229,79 @@ static struct platform_device tmu01_devi + .num_resources = ARRAY_SIZE(tmu01_resources), + }; + ++/* I2C */ ++static struct resource rcar_i2c0_res[] = { ++ { ++ .start = 0xffc70000, ++ .end = 0xffc70fff, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = gic_spi(79), ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device i2c0_device = { ++ .name = "i2c-rcar", ++ .id = 0, ++ .resource = rcar_i2c0_res, ++ .num_resources = ARRAY_SIZE(rcar_i2c0_res), ++}; ++ ++static struct resource rcar_i2c1_res[] = { ++ { ++ .start = 0xffc71000, ++ .end = 0xffc71fff, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = gic_spi(82), ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device i2c1_device = { ++ .name = "i2c-rcar", ++ .id = 1, ++ .resource = rcar_i2c1_res, ++ .num_resources = ARRAY_SIZE(rcar_i2c1_res), ++}; ++ ++static struct resource rcar_i2c2_res[] = { ++ { ++ .start = 0xffc72000, ++ .end = 0xffc72fff, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = gic_spi(80), ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device i2c2_device = { ++ .name = "i2c-rcar", ++ .id = 2, ++ .resource = rcar_i2c2_res, ++ .num_resources = ARRAY_SIZE(rcar_i2c2_res), ++}; ++ ++static struct resource rcar_i2c3_res[] = { ++ { ++ .start = 0xffc73000, ++ .end = 0xffc73fff, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = gic_spi(81), ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device i2c3_device = { ++ .name = "i2c-rcar", ++ .id = 3, ++ .resource = rcar_i2c3_res, ++ .num_resources = ARRAY_SIZE(rcar_i2c3_res), ++}; ++ + static struct platform_device *r8a7779_early_devices[] __initdata = { + &scif0_device, + &scif1_device, +@@ -238,6 +311,10 @@ static struct platform_device *r8a7779_e + &scif5_device, + &tmu00_device, + &tmu01_device, ++ &i2c0_device, ++ &i2c1_device, ++ &i2c2_device, ++ &i2c3_device, + }; + + static struct platform_device *r8a7779_late_devices[] __initdata = { diff --git a/patches.marzen/i2c-add-renesas-r-car-i2c-driver.patch b/patches.marzen/i2c-add-renesas-r-car-i2c-driver.patch new file mode 100644 index 00000000000000..96736e4a97f8cc --- /dev/null +++ b/patches.marzen/i2c-add-renesas-r-car-i2c-driver.patch @@ -0,0 +1,784 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Wed Feb 27 20:03:27 2013 +From: Simon Horman <horms+renesas@verge.net.au> +Date: Thu, 28 Feb 2013 13:03:01 +0900 +Subject: [PATCH 1/4] i2c: add Renesas R-Car I2C driver +To: ltsi-dev@lists.linuxfoundation.org +Cc: Magnus Damm <magnus.damm@gmail.com>, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +Message-ID: <1362024184-27864-2-git-send-email-horms+renesas@verge.net.au> + + +From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + +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@renesas.com> +Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> +(cherry picked from commit 6ccbe607132bd823abbad8d32b13af89161707da) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + 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 + +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -723,6 +723,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 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -72,6 +72,7 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versa + 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 +--- /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@renesas.com> ++ * ++ * This file is based on the drivers/i2c/busses/i2c-sh7760.c ++ * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com> ++ * ++ * 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@renesas.com>"); +--- /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__ */ diff --git a/patches.marzen/i2c-rcar-fix-section-mismatch.patch b/patches.marzen/i2c-rcar-fix-section-mismatch.patch new file mode 100644 index 00000000000000..7a68c8f200a221 --- /dev/null +++ b/patches.marzen/i2c-rcar-fix-section-mismatch.patch @@ -0,0 +1,47 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Wed Feb 27 20:03:23 2013 +From: Simon Horman <horms+renesas@verge.net.au> +Date: Thu, 28 Feb 2013 13:03:03 +0900 +Subject: [PATCH 3/4] i2c: rcar: fix section mismatch +To: ltsi-dev@lists.linuxfoundation.org +Cc: Magnus Damm <magnus.damm@gmail.com>, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +Message-ID: <1362024184-27864-4-git-send-email-horms+renesas@verge.net.au> + + +From: Wolfram Sang <w.sang@pengutronix.de> + +Give the driver struct a name according to the 'standard' to fix: + +WARNING: vmlinux.o(.data+0x11798): Section mismatch in reference from the variable rcar_i2c_drv to the function .devinit.text:rcar_i2c_probe() +... +WARNING: vmlinux.o(.data+0x1179c): Section mismatch in reference from the variable rcar_i2c_drv to the function .devexit.text:rcar_i2c_remove() + +Reported-by: Simon Horman <horms@verge.net.au> +Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> +Cc: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +(cherry picked from commit 45fd5e4ad2052101b4ceda5fdf4b003c428ebdfc) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/i2c/busses/i2c-rcar.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/i2c/busses/i2c-rcar.c ++++ b/drivers/i2c/busses/i2c-rcar.c +@@ -693,7 +693,7 @@ static int __devexit rcar_i2c_remove(str + return 0; + } + +-static struct platform_driver rcar_i2c_drv = { ++static struct platform_driver rcar_i2c_driver = { + .driver = { + .name = "i2c-rcar", + .owner = THIS_MODULE, +@@ -702,7 +702,7 @@ static struct platform_driver rcar_i2c_d + .remove = __devexit_p(rcar_i2c_remove), + }; + +-module_platform_driver(rcar_i2c_drv); ++module_platform_driver(rcar_i2c_driver); + + MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("Renesas R-Car I2C bus driver"); diff --git a/patches.marzen/i2c-rcar-used-devm_request_and_ioremap-instead-of-devm_ioremap.patch b/patches.marzen/i2c-rcar-used-devm_request_and_ioremap-instead-of-devm_ioremap.patch new file mode 100644 index 00000000000000..b4d97de24b4455 --- /dev/null +++ b/patches.marzen/i2c-rcar-used-devm_request_and_ioremap-instead-of-devm_ioremap.patch @@ -0,0 +1,31 @@ +From ltsi-dev-bounces@lists.linuxfoundation.org Wed Feb 27 20:03:30 2013 +From: Simon Horman <horms+renesas@verge.net.au> +Date: Thu, 28 Feb 2013 13:03:02 +0900 +Subject: [PATCH 2/4] i2c: rcar: used devm_request_and_ioremap() instead of devm_ioremap() +To: ltsi-dev@lists.linuxfoundation.org +Cc: Magnus Damm <magnus.damm@gmail.com>, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +Message-ID: <1362024184-27864-3-git-send-email-horms+renesas@verge.net.au> + + +From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + +Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> +(cherry picked from commit b53f4baf8b26303fc75ef3b00cf5e7398b58efd4) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/i2c/busses/i2c-rcar.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/i2c/busses/i2c-rcar.c ++++ b/drivers/i2c/busses/i2c-rcar.c +@@ -642,7 +642,7 @@ static int __devinit rcar_i2c_probe(stru + if (ret < 0) + return ret; + +- priv->io = devm_ioremap(dev, res->start, resource_size(res)); ++ priv->io = devm_request_and_ioremap(dev, res); + if (!priv->io) { + dev_err(dev, "cannot ioremap\n"); + return -ENODEV; @@ -307,6 +307,10 @@ patches.marzen/0099-mmc-sh_mmcif-support-generic-card-detection.patch patches.marzen/arm-mach-shmobile-marzen-defconfig-update.patch patches.marzen/arm-shmobile-r8a7779-correct-tmu-clock-support.patch patches.marzen/libata-add-r-car-sata-driver.patch +patches.marzen/i2c-add-renesas-r-car-i2c-driver.patch +patches.marzen/i2c-rcar-used-devm_request_and_ioremap-instead-of-devm_ioremap.patch +patches.marzen/i2c-rcar-fix-section-mismatch.patch +patches.marzen/arm-shmobile-r8a7779-add-i2c-driver-support.patch ############################################################################# # Armadillo 800 board support |