From a76e7c6821b5dddf69db9d76ec282819545f5b73 Mon Sep 17 00:00:00 2001 From: Thomas Kavanagh Date: Thu, 20 Sep 2012 20:20:46 -0700 Subject: i2c: algo: pca: Fix chip reset function for PCA9665 The parameter passed to pca9665_reset is adap->data (which is bus driver specific), not i2c_algp_pca_data *adap. pca9665_reset expects it to be i2c_algp_pca_data *adap. All other wrappers from the algo call back to the bus driver, which knows to handle its custom data. Only pca9665_reset resides inside the algorithm code and does not know how to handle a custom data structure. This can result in a kernel crash. Fix by re-factoring pca_reset() from a macro to a function handling chip specific code, and only call adap->reset_chip() if the chip is not PCA9665. Signed-off-by: Thomas Kavanagh Signed-off-by: Guenter Roeck Signed-off-by: Wolfram Sang --- include/linux/i2c-algo-pca.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/i2c-algo-pca.h b/include/linux/i2c-algo-pca.h index 1364d62e2fbe..a3c3ecd59f08 100644 --- a/include/linux/i2c-algo-pca.h +++ b/include/linux/i2c-algo-pca.h @@ -62,6 +62,7 @@ struct i2c_algo_pca_data { * 330000, 288000, 217000, 146000, 88000, 59000, 44000, 36000 * For PCA9665, use the frequency you want here. */ unsigned int i2c_clock; + unsigned int chip; }; int i2c_pca_add_bus(struct i2c_adapter *); -- cgit v1.2.3-59-g8ed1b From 43fea5813c56e4327371fd3c2209791ef7822de2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 6 Aug 2012 11:09:57 +0100 Subject: i2c: nomadik: Add Device Tree support to the Nomadik I2C driver Here we apply the bindings required for successful Device Tree probing of the i2c-nomadik driver. Signed-off-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/nomadik.txt | 23 +++++++++++++++ drivers/i2c/busses/i2c-nomadik.c | 35 +++++++++++++++++++++-- include/linux/platform_data/i2c-nomadik.h | 2 +- 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/i2c/nomadik.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/i2c/nomadik.txt b/Documentation/devicetree/bindings/i2c/nomadik.txt new file mode 100644 index 000000000000..72065b0ff680 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/nomadik.txt @@ -0,0 +1,23 @@ +I2C for Nomadik based systems + +Required (non-standard) properties: + - Nil + +Recommended (non-standard) properties: + - clock-frequency : Maximum bus clock frequency for the device + +Optional (non-standard) properties: + - Nil + +Example : + +i2c@80004000 { + compatible = "stericsson,db8500-i2c", "st,nomadik-i2c"; + reg = <0x80004000 0x1000>; + interrupts = <0 21 0x4>; + #address-cells = <1>; + #size-cells = <0>; + v-i2c-supply = <&db8500_vape_reg>; + + clock-frequency = <400000>; +}; diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 1b898b647ec2..698d7acb0f08 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #define DRIVER_NAME "nmk-i2c" @@ -913,18 +915,42 @@ static struct nmk_i2c_controller u8500_i2c = { .sm = I2C_FREQ_MODE_FAST, }; +static void nmk_i2c_of_probe(struct device_node *np, + struct nmk_i2c_controller *pdata) +{ + of_property_read_u32(np, "clock-frequency", &pdata->clk_freq); + + /* This driver only supports 'standard' and 'fast' modes of operation. */ + if (pdata->clk_freq <= 100000) + pdata->sm = I2C_FREQ_MODE_STANDARD; + else + pdata->sm = I2C_FREQ_MODE_FAST; +} + static atomic_t adapter_id = ATOMIC_INIT(0); static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; struct nmk_i2c_controller *pdata = adev->dev.platform_data; + struct device_node *np = adev->dev.of_node; struct nmk_i2c_dev *dev; struct i2c_adapter *adap; - if (!pdata) - /* No i2c configuration found, using the default. */ - pdata = &u8500_i2c; + if (!pdata) { + if (np) { + pdata = devm_kzalloc(&adev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err_no_mem; + } + /* Provide the default configuration as a base. */ + memcpy(pdata, &u8500_i2c, sizeof(struct nmk_i2c_controller)); + nmk_i2c_of_probe(np, pdata); + } else + /* No i2c configuration found, using the default. */ + pdata = &u8500_i2c; + } dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL); if (!dev) { @@ -960,6 +986,7 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) } adap = &dev->adap; + adap->dev.of_node = np; adap->dev.parent = &adev->dev; adap->owner = THIS_MODULE; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; @@ -989,6 +1016,8 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) goto err_add_adap; } + of_i2c_register_devices(adap); + pm_runtime_put(&adev->dev); return 0; diff --git a/include/linux/platform_data/i2c-nomadik.h b/include/linux/platform_data/i2c-nomadik.h index c2303c3e4803..3a8be9cdc95c 100644 --- a/include/linux/platform_data/i2c-nomadik.h +++ b/include/linux/platform_data/i2c-nomadik.h @@ -28,7 +28,7 @@ enum i2c_freq_mode { * @sm: speed mode */ struct nmk_i2c_controller { - unsigned long clk_freq; + u32 clk_freq; unsigned short slsu; unsigned char tft; unsigned char rft; -- cgit v1.2.3-59-g8ed1b From 3db11feffc1ad2ab9dea27789e6b5b3032827adc Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Thu, 20 Sep 2012 18:08:03 +0200 Subject: ARM: OMAP: convert I2C driver to PM QoS for MPU latency constraints Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat API to the new PM QoS API. Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY class of PM QoS. The resulting MPU constraints are used by cpuidle to decide the next power state of the MPU subsystem. The I2C device latency timing is derived from the FIFO size and the clock speed and so is applicable to all OMAP SoCs. Signed-off-by: Jean Pihet Acked-by: Shubhrajyoti D Acked-by: Tony Lindgren Acked-by: Kevin Hilman Signed-off-by: Wolfram Sang --- arch/arm/plat-omap/i2c.c | 21 --------------------- drivers/i2c/busses/i2c-omap.c | 32 ++++++++++++++++++-------------- include/linux/i2c-omap.h | 1 - 3 files changed, 18 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c index db071bc71c4d..dba8338f6cd7 100644 --- a/arch/arm/plat-omap/i2c.c +++ b/arch/arm/plat-omap/i2c.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -34,7 +33,6 @@ #include #include #include -#include #include #define OMAP_I2C_SIZE 0x3f @@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id) #ifdef CONFIG_ARCH_OMAP2PLUS -/* - * XXX This function is a temporary compatibility wrapper - only - * needed until the I2C driver can be converted to call - * omap_pm_set_max_dev_wakeup_lat() and handle a return code. - */ -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t) -{ - omap_pm_set_max_mpu_wakeup_lat(dev, t); -} - static inline int omap2_i2c_add_bus(int bus_id) { int l; @@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id) dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr; pdata->flags = dev_attr->flags; - /* - * When waiting for completion of a i2c transfer, we need to - * set a wake up latency constraint for the MPU. This is to - * ensure quick enough wakeup from idle, when transfer - * completes. - * Only omap3 has support for constraints - */ - if (cpu_is_omap34xx()) - pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat; pdev = omap_device_build(name, bus_id, oh, pdata, sizeof(struct omap_i2c_bus_platform_data), NULL, 0, 0); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index c78431a4970a..b6c6b95d4a07 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -43,6 +43,7 @@ #include #include #include +#include /* I2C controller revisions */ #define OMAP_I2C_OMAP1_REV_2 0x20 @@ -186,9 +187,8 @@ struct omap_i2c_dev { int reg_shift; /* bit shift for I2C register addresses */ struct completion cmd_complete; struct resource *ioarea; - u32 latency; /* maximum mpu wkup latency */ - void (*set_mpu_wkup_lat)(struct device *dev, - long latency); + u32 latency; /* maximum MPU wkup latency */ + struct pm_qos_request pm_qos_request; u32 speed; /* Speed of bus in kHz */ u32 dtrev; /* extra revision from DT */ u32 flags; @@ -494,9 +494,7 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx) dev->b_hw = 1; /* Enable hardware fixes */ /* calculate wakeup latency constraint for MPU */ - if (dev->set_mpu_wkup_lat != NULL) - dev->latency = (1000000 * dev->threshold) / - (1000 * dev->speed / 8); + dev->latency = (1000000 * dev->threshold) / (1000 * dev->speed / 8); } /* @@ -631,8 +629,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) if (r < 0) goto out; - if (dev->set_mpu_wkup_lat != NULL) - dev->set_mpu_wkup_lat(dev->dev, dev->latency); + /* + * When waiting for completion of a i2c transfer, we need to + * set a wake up latency constraint for the MPU. This is to + * ensure quick enough wakeup from idle, when transfer + * completes. + */ + if (dev->latency) + pm_qos_add_request(&dev->pm_qos_request, + PM_QOS_CPU_DMA_LATENCY, + dev->latency); for (i = 0; i < num; i++) { r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1))); @@ -640,8 +646,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) break; } - if (dev->set_mpu_wkup_lat != NULL) - dev->set_mpu_wkup_lat(dev->dev, -1); + if (dev->latency) + pm_qos_remove_request(&dev->pm_qos_request); if (r == 0) r = num; @@ -1097,7 +1103,6 @@ omap_i2c_probe(struct platform_device *pdev) } else if (pdata != NULL) { dev->speed = pdata->clkrate; dev->flags = pdata->flags; - dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat; dev->dtrev = pdata->rev; } @@ -1153,9 +1158,8 @@ omap_i2c_probe(struct platform_device *pdev) dev->b_hw = 1; /* Enable hardware fixes */ /* calculate wakeup latency constraint for MPU */ - if (dev->set_mpu_wkup_lat != NULL) - dev->latency = (1000000 * dev->fifo_size) / - (1000 * dev->speed / 8); + dev->latency = (1000000 * dev->fifo_size) / + (1000 * dev->speed / 8); } /* reset ASAP, clearing any IRQs */ diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h index 92a0dc75bc74..df804ba73e0b 100644 --- a/include/linux/i2c-omap.h +++ b/include/linux/i2c-omap.h @@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data { u32 clkrate; u32 rev; u32 flags; - void (*set_mpu_wkup_lat)(struct device *dev, long set); }; #endif -- cgit v1.2.3-59-g8ed1b From 6ccbe607132bd823abbad8d32b13af89161707da Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Sep 2012 23:44:25 -0700 Subject: i2c: add Renesas R-Car I2C driver 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 Signed-off-by: Wolfram Sang --- 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 (limited to 'include') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index da77c37caf1e..c8c11b21778b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -712,6 +712,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 ce3c2be7fb40..e98ff51e9d9c 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 000000000000..f9399d163af2 --- /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 + * + * This file is based on the drivers/i2c/busses/i2c-sh7760.c + * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); diff --git a/include/linux/i2c/i2c-rcar.h b/include/linux/i2c/i2c-rcar.h new file mode 100644 index 000000000000..496f5c2b23c9 --- /dev/null +++ b/include/linux/i2c/i2c-rcar.h @@ -0,0 +1,10 @@ +#ifndef __I2C_R_CAR_H__ +#define __I2C_R_CAR_H__ + +#include + +struct i2c_rcar_platform_data { + u32 bus_speed; +}; + +#endif /* __I2C_R_CAR_H__ */ -- cgit v1.2.3-59-g8ed1b