From 4489750fa56e5c2ed49b3bf3590c0bfdab1a8a08 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 11 May 2015 12:44:28 -0700 Subject: i2c: rk3x: Increase wait timeout to 1 second Although unlikely, it is remotely possible for an i2c command to need more than 200ms complete. Unlike smbus, i2c devices can clock stretch for an unspecified amount of time. The longest time I've seen specified for a device is 144ms (bq27541 battery gas), but one could imagine a device taking a bit slower. 1 second "ought to be enough for anyone." The above is not the only justifcation for going above 200ms for a timeout, though. It turns out that if you've got a large number of printks going out to a serial console, interrupts on a CPU can be disabled for hundreds of milliseconds. That's not a great situation to be in to start with (maybe we should put a cap in vprintk_emit()) but it's pretty annoying to start seeing unexplained i2c timeouts. Note that to understand why we can timeout when printk has interrupts disabled, you need to understand that on current Linux ARM kernels interrupts are routed to a single CPU in a multicore system. Thus, you can get: 1. CPU1 is running rk3x_i2c_xfer() 2. CPU0 calls vprintk_emit(), which disables all IRQs on CPU0. 3. I2C interrupt is ready but is set to only run on CPU0, where IRQs are disabled. 4. CPU1 timeout expires. I2C interrupt is still ready, but CPU0 is still sitting in the same vprintk_emit() 5. CPU1 sees that no interrupt happened in 200ms, so timeout. A normal system shouldn't see i2c timeouts anyway, so increasing the timeout should help people debugging without hurting other people excessively. Signed-off-by: Doug Anderson Tested-by: Caesar Wang Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 019d5426fe52..72e97e306bd9 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -72,7 +72,7 @@ enum { #define REG_INT_ALL 0x7f /* Constants */ -#define WAIT_TIMEOUT 200 /* ms */ +#define WAIT_TIMEOUT 1000 /* ms */ #define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ enum rk3x_i2c_state { -- cgit v1.2.3 From bea6ff020f3a7228bc6b625071674c5129ffc185 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Tue, 5 May 2015 18:06:05 +0530 Subject: i2c: xiic: Fix kerneldoc warnings info(drivers/i2c/busses/i2c-xiic.c:55): Scanning doc for struct xiic_i2c Warning(drivers/i2c/busses/i2c-xiic.c:79): No description found for parameter 'endianness' Signed-off-by: Shubhrajyoti Datta Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-xiic.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index e8400042b358..4dda23f22a67 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -63,6 +63,7 @@ enum xiic_endian { * @state: See STATE_ * @rx_msg: Current RX message * @rx_pos: Position within current RX message + * @endianness: big/little-endian byte order */ struct xiic_i2c { void __iomem *base; -- cgit v1.2.3 From 7e5cd69a9152c70985ebf820e357da59dfa8f152 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 5 May 2015 18:32:22 +0200 Subject: i2c: Allow compile test of GPIO consumers if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. Relax the dependency on GPIOLIB if COMPILE_TEST is enabled, where appropriate. Signed-off-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 4 ++-- drivers/i2c/muxes/Kconfig | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 2255af23b9c7..5c9750ac486c 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -419,7 +419,7 @@ config I2C_CADENCE config I2C_CBUS_GPIO tristate "CBUS I2C driver" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Support for CBUS access using I2C API. Mostly relevant for Nokia Internet Tablets (770, N800 and N810). @@ -525,7 +525,7 @@ config I2C_EXYNOS5 config I2C_GPIO tristate "GPIO-based bitbanging I2C" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select I2C_ALGOBIT help This is a very simple bitbanging I2C driver utilizing the diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index f6d313e528de..fdd0769c84a3 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -7,7 +7,8 @@ menu "Multiplexer I2C Chip support" config I2C_ARB_GPIO_CHALLENGE tristate "GPIO-based I2C arbitration" - depends on GPIOLIB && OF + depends on GPIOLIB || COMPILE_TEST + depends on OF help If you say yes to this option, support will be included for an I2C multimaster arbitration scheme using GPIOs and a challenge & @@ -40,7 +41,7 @@ config I2C_MUX_PCA9541 config I2C_MUX_PCA954x tristate "Philips PCA954x I2C Mux/switches" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help If you say yes here you get support for the Philips PCA954x I2C mux/switch devices. -- cgit v1.2.3 From 096ea30c91b521c77964ee8c214f7ce3b9d08e7b Mon Sep 17 00:00:00 2001 From: Pascal Huerst Date: Wed, 6 May 2015 15:07:04 +0200 Subject: i2c: omap: Add calls for pinctrl state select This adds calls to pinctrl subsystem in order to switch pin states on suspend/resume if you provide a "sleep" state in DT. If no "sleep" state is provided in the DT, these calls turn to NOPs, so we don't need error checking here. Signed-off-by: Pascal Huerst Reviewed-by: Sekhar Nori Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-omap.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 0e894193accf..826194182118 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -38,6 +38,7 @@ #include #include #include +#include /* I2C controller revisions */ #define OMAP_I2C_OMAP1_REV_2 0x20 @@ -1423,6 +1424,8 @@ static int omap_i2c_runtime_suspend(struct device *dev) omap_i2c_read_reg(_dev, OMAP_I2C_STAT_REG); } + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -1431,6 +1434,8 @@ static int omap_i2c_runtime_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct omap_i2c_dev *_dev = platform_get_drvdata(pdev); + pinctrl_pm_select_default_state(dev); + if (!_dev->regs) return 0; -- cgit v1.2.3 From e9a02a3d89b9d16eafc540d1dc78476a3bb5d4eb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:54:25 +0900 Subject: i2c: constify platform_device_id The platform_device_id is not modified by these drivers and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 2 +- drivers/i2c/busses/i2c-mxs.c | 2 +- drivers/i2c/busses/i2c-rcar.c | 2 +- drivers/i2c/busses/i2c-s3c2410.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index a53a7dd66945..785aa674a4da 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -241,7 +241,7 @@ static struct imx_i2c_hwdata vf610_i2c_hwdata = { }; -static struct platform_device_id imx_i2c_devtype[] = { +static const struct platform_device_id imx_i2c_devtype[] = { { .name = "imx1-i2c", .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata, diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 3e84f6c090a5..033846cdf266 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -784,7 +784,7 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c) return 0; } -static struct platform_device_id mxs_i2c_devtype[] = { +static const struct platform_device_id mxs_i2c_devtype[] = { { .name = "imx23-i2c", .driver_data = MXS_I2C_V1, diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 5a84bea5b845..06f34d1c5121 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -713,7 +713,7 @@ static int rcar_i2c_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id rcar_i2c_id_table[] = { +static const struct platform_device_id rcar_i2c_id_table[] = { { "i2c-rcar", I2C_RCAR_GEN1 }, { "i2c-rcar_gen1", I2C_RCAR_GEN1 }, { "i2c-rcar_gen2", I2C_RCAR_GEN2 }, diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 958c8db4ec30..c1f0e62d1bb1 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -132,7 +132,7 @@ struct s3c24xx_i2c { unsigned int sys_i2c_cfg; }; -static struct platform_device_id s3c24xx_driver_ids[] = { +static const struct platform_device_id s3c24xx_driver_ids[] = { { .name = "s3c2410-i2c", .driver_data = 0, -- cgit v1.2.3 From 9dcb0e7b999db6c420c70fd32497a979a044fcdf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 6 May 2015 11:50:27 -0500 Subject: i2c: omap: implement bus recovery implement bus recovery methods for i2c-omap so we can recover from situations where SCL/SDA are stuck low. Signed-off-by: Felipe Balbi Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-omap.c | 69 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 826194182118..d1c22e3fdd14 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -482,10 +482,8 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev) timeout = jiffies + OMAP_I2C_TIMEOUT; while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { - if (time_after(jiffies, timeout)) { - dev_warn(dev->dev, "timeout waiting for bus ready\n"); - return -ETIMEDOUT; - } + if (time_after(jiffies, timeout)) + return i2c_recover_bus(&dev->adapter); msleep(1); } @@ -1210,6 +1208,68 @@ MODULE_DEVICE_TABLE(of, omap_i2c_of_match); #define OMAP_I2C_SCHEME_0 0 #define OMAP_I2C_SCHEME_1 1 +static int omap_i2c_get_scl(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + + return reg & OMAP_I2C_SYSTEST_SCL_I_FUNC; +} + +static int omap_i2c_get_sda(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + + return reg & OMAP_I2C_SYSTEST_SDA_I_FUNC; +} + +static void omap_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + if (val) + reg |= OMAP_I2C_SYSTEST_SCL_O; + else + reg &= ~OMAP_I2C_SYSTEST_SCL_O; + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); +} + +static void omap_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + reg |= OMAP_I2C_SYSTEST_ST_EN; + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); +} + +static void omap_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + u32 reg; + + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); + reg &= ~OMAP_I2C_SYSTEST_ST_EN; + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); +} + +static struct i2c_bus_recovery_info omap_i2c_bus_recovery_info = { + .get_scl = omap_i2c_get_scl, + .get_sda = omap_i2c_get_sda, + .set_scl = omap_i2c_set_scl, + .prepare_recovery = omap_i2c_prepare_recovery, + .unprepare_recovery = omap_i2c_unprepare_recovery, + .recover_bus = i2c_generic_scl_recovery, +}; + static int omap_i2c_probe(struct platform_device *pdev) { @@ -1359,6 +1419,7 @@ omap_i2c_probe(struct platform_device *pdev) adap->algo = &omap_i2c_algo; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; + adap->bus_recovery_info = &omap_i2c_bus_recovery_info; /* i2c device drivers may be active on return from add_adapter() */ adap->nr = pdev->id; -- cgit v1.2.3 From f6505fbabc426b9e293da5bb702ace2eb1ccf87d Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Fri, 24 Apr 2015 15:17:50 -0700 Subject: i2c: add SLIMpro I2C device driver on APM X-Gene platform Add SLIMpro I2C device driver on APM X-Gene platform. This I2C device driver use the SLIMpro Mailbox driver to tunnel message to the SLIMpro coprocessor to do the work of accessing I2C components. Signed-off-by: Feng Kan Signed-off-by: Hieu Le Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-xgene-slimpro.txt | 15 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-xgene-slimpro.c | 469 +++++++++++++++++++++ 4 files changed, 494 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-xgene-slimpro.txt create mode 100644 drivers/i2c/busses/i2c-xgene-slimpro.c (limited to 'drivers/i2c') diff --git a/Documentation/devicetree/bindings/i2c/i2c-xgene-slimpro.txt b/Documentation/devicetree/bindings/i2c/i2c-xgene-slimpro.txt new file mode 100644 index 000000000000..f6b2c20cfbf6 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-xgene-slimpro.txt @@ -0,0 +1,15 @@ +APM X-Gene SLIMpro Mailbox I2C Driver + +An I2C controller accessed over the "SLIMpro" mailbox. + +Required properties : + + - compatible : should be "apm,xgene-slimpro-i2c" + - mboxes : use the label reference for the mailbox as the first parameter. + The second parameter is the channel number. + +Example : + i2cslimpro { + compatible = "apm,xgene-slimpro-i2c"; + mboxes = <&mailbox 0>; + }; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 5c9750ac486c..1d759c3767e7 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1110,6 +1110,15 @@ config I2C_CROS_EC_TUNNEL connected there. This will work whatever the interface used to talk to the EC (SPI, I2C or LPC). +config I2C_XGENE_SLIMPRO + tristate "APM X-Gene SoC I2C SLIMpro devices support" + depends on ARCH_XGENE && MAILBOX + help + Enable I2C bus access using the APM X-Gene SoC SLIMpro + co-processor. The I2C device access the I2C bus via the X-Gene + to SLIMpro (On chip coprocessor) mailbox mechanism. + If unsure, say N. + config SCx200_ACB tristate "Geode ACCESS.bus support" depends on X86_32 && PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index cdf941da91c6..d8093022ee4c 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_OPAL) += i2c-opal.o obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o +obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c new file mode 100644 index 000000000000..0b33aa2e6730 --- /dev/null +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -0,0 +1,469 @@ +/* + * X-Gene SLIMpro I2C Driver + * + * Copyright (c) 2014, Applied Micro Circuits Corporation + * Author: Feng Kan + * Author: Hieu Le + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + * This driver provides support for X-Gene SLIMpro I2C device access + * using the APM X-Gene SLIMpro mailbox driver. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_OP_TIMEOUT 1000 /* Operation time out in ms */ +#define MAILBOX_I2C_INDEX 0 +#define SLIMPRO_IIC_BUS 1 /* Use I2C bus 1 only */ + +#define SMBUS_CMD_LEN 1 +#define BYTE_DATA 1 +#define WORD_DATA 2 +#define BLOCK_DATA 3 + +#define SLIMPRO_IIC_I2C_PROTOCOL 0 +#define SLIMPRO_IIC_SMB_PROTOCOL 1 + +#define SLIMPRO_IIC_READ 0 +#define SLIMPRO_IIC_WRITE 1 + +#define IIC_SMB_WITHOUT_DATA_LEN 0 +#define IIC_SMB_WITH_DATA_LEN 1 + +#define SLIMPRO_DEBUG_MSG 0 +#define SLIMPRO_MSG_TYPE_SHIFT 28 +#define SLIMPRO_DBG_SUBTYPE_I2C1READ 4 +#define SLIMPRO_DBGMSG_TYPE_SHIFT 24 +#define SLIMPRO_DBGMSG_TYPE_MASK 0x0F000000U +#define SLIMPRO_IIC_DEV_SHIFT 23 +#define SLIMPRO_IIC_DEV_MASK 0x00800000U +#define SLIMPRO_IIC_DEVID_SHIFT 13 +#define SLIMPRO_IIC_DEVID_MASK 0x007FE000U +#define SLIMPRO_IIC_RW_SHIFT 12 +#define SLIMPRO_IIC_RW_MASK 0x00001000U +#define SLIMPRO_IIC_PROTO_SHIFT 11 +#define SLIMPRO_IIC_PROTO_MASK 0x00000800U +#define SLIMPRO_IIC_ADDRLEN_SHIFT 8 +#define SLIMPRO_IIC_ADDRLEN_MASK 0x00000700U +#define SLIMPRO_IIC_DATALEN_SHIFT 0 +#define SLIMPRO_IIC_DATALEN_MASK 0x000000FFU + +/* + * SLIMpro I2C message encode + * + * dev - Controller number (0-based) + * chip - I2C chip address + * op - SLIMPRO_IIC_READ or SLIMPRO_IIC_WRITE + * proto - SLIMPRO_IIC_SMB_PROTOCOL or SLIMPRO_IIC_I2C_PROTOCOL + * addrlen - Length of the address field + * datalen - Length of the data field + */ +#define SLIMPRO_IIC_ENCODE_MSG(dev, chip, op, proto, addrlen, datalen) \ + ((SLIMPRO_DEBUG_MSG << SLIMPRO_MSG_TYPE_SHIFT) | \ + ((SLIMPRO_DBG_SUBTYPE_I2C1READ << SLIMPRO_DBGMSG_TYPE_SHIFT) & \ + SLIMPRO_DBGMSG_TYPE_MASK) | \ + ((dev << SLIMPRO_IIC_DEV_SHIFT) & SLIMPRO_IIC_DEV_MASK) | \ + ((chip << SLIMPRO_IIC_DEVID_SHIFT) & SLIMPRO_IIC_DEVID_MASK) | \ + ((op << SLIMPRO_IIC_RW_SHIFT) & SLIMPRO_IIC_RW_MASK) | \ + ((proto << SLIMPRO_IIC_PROTO_SHIFT) & SLIMPRO_IIC_PROTO_MASK) | \ + ((addrlen << SLIMPRO_IIC_ADDRLEN_SHIFT) & SLIMPRO_IIC_ADDRLEN_MASK) | \ + ((datalen << SLIMPRO_IIC_DATALEN_SHIFT) & SLIMPRO_IIC_DATALEN_MASK)) + +/* + * Encode for upper address for block data + */ +#define SLIMPRO_IIC_ENCODE_FLAG_BUFADDR 0x80000000 +#define SLIMPRO_IIC_ENCODE_FLAG_WITH_DATA_LEN(a) ((u32) (((a) << 30) \ + & 0x40000000)) +#define SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(a) ((u32) (((a) >> 12) \ + & 0x3FF00000)) +#define SLIMPRO_IIC_ENCODE_ADDR(a) ((a) & 0x000FFFFF) + +struct slimpro_i2c_dev { + struct i2c_adapter adapter; + struct device *dev; + struct mbox_chan *mbox_chan; + struct mbox_client mbox_client; + struct completion rd_complete; + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX]; + u32 *resp_msg; +}; + +#define to_slimpro_i2c_dev(cl) \ + container_of(cl, struct slimpro_i2c_dev, mbox_client) + +static void slimpro_i2c_rx_cb(struct mbox_client *cl, void *mssg) +{ + struct slimpro_i2c_dev *ctx = to_slimpro_i2c_dev(cl); + + /* + * Response message format: + * mssg[0] is the return code of the operation + * mssg[1] is the first data word + * mssg[2] is NOT used + */ + if (ctx->resp_msg) + *ctx->resp_msg = ((u32 *)mssg)[1]; + + if (ctx->mbox_client.tx_block) + complete(&ctx->rd_complete); +} + +static int start_i2c_msg_xfer(struct slimpro_i2c_dev *ctx) +{ + if (ctx->mbox_client.tx_block) { + if (!wait_for_completion_timeout(&ctx->rd_complete, + msecs_to_jiffies(MAILBOX_OP_TIMEOUT))) + return -ETIMEDOUT; + } + + /* Check of invalid data or no device */ + if (*ctx->resp_msg == 0xffffffff) + return -ENODEV; + + return 0; +} + +static int slimpro_i2c_rd(struct slimpro_i2c_dev *ctx, u32 chip, + u32 addr, u32 addrlen, u32 protocol, + u32 readlen, u32 *data) +{ + u32 msg[3]; + int rc; + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, + SLIMPRO_IIC_READ, protocol, addrlen, readlen); + msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = 0; + ctx->resp_msg = data; + rc = mbox_send_message(ctx->mbox_chan, &msg); + if (rc < 0) + goto err; + + rc = start_i2c_msg_xfer(ctx); +err: + ctx->resp_msg = NULL; + return rc; +} + +static int slimpro_i2c_wr(struct slimpro_i2c_dev *ctx, u32 chip, + u32 addr, u32 addrlen, u32 protocol, u32 writelen, + u32 data) +{ + u32 msg[3]; + int rc; + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, + SLIMPRO_IIC_WRITE, protocol, addrlen, writelen); + msg[1] = SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = data; + ctx->resp_msg = msg; + + rc = mbox_send_message(ctx->mbox_chan, &msg); + if (rc < 0) + goto err; + + rc = start_i2c_msg_xfer(ctx); +err: + ctx->resp_msg = NULL; + return rc; +} + +static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr, + u32 addrlen, u32 protocol, u32 readlen, + u32 with_data_len, void *data) +{ + dma_addr_t paddr; + u32 msg[3]; + int rc; + + paddr = dma_map_single(ctx->dev, ctx->dma_buffer, readlen, DMA_FROM_DEVICE); + rc = dma_mapping_error(ctx->dev, paddr); + if (rc) { + dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", + ctx->dma_buffer); + goto err; + } + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_READ, + protocol, addrlen, readlen); + msg[1] = SLIMPRO_IIC_ENCODE_FLAG_BUFADDR | + SLIMPRO_IIC_ENCODE_FLAG_WITH_DATA_LEN(with_data_len) | + SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) | + SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = (u32)paddr; + ctx->resp_msg = msg; + + rc = mbox_send_message(ctx->mbox_chan, &msg); + if (rc < 0) + goto err_unmap; + + rc = start_i2c_msg_xfer(ctx); + + /* Copy to destination */ + memcpy(data, ctx->dma_buffer, readlen); + +err_unmap: + dma_unmap_single(ctx->dev, paddr, readlen, DMA_FROM_DEVICE); +err: + ctx->resp_msg = NULL; + return rc; +} + +static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip, + u32 addr, u32 addrlen, u32 protocol, u32 writelen, + void *data) +{ + dma_addr_t paddr; + u32 msg[3]; + int rc; + + memcpy(ctx->dma_buffer, data, writelen); + paddr = dma_map_single(ctx->dev, ctx->dma_buffer, writelen, + DMA_TO_DEVICE); + rc = dma_mapping_error(ctx->dev, paddr); + if (rc) { + dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", + ctx->dma_buffer); + goto err; + } + + msg[0] = SLIMPRO_IIC_ENCODE_MSG(SLIMPRO_IIC_BUS, chip, SLIMPRO_IIC_WRITE, + protocol, addrlen, writelen); + msg[1] = SLIMPRO_IIC_ENCODE_FLAG_BUFADDR | + SLIMPRO_IIC_ENCODE_UPPER_BUFADDR(paddr) | + SLIMPRO_IIC_ENCODE_ADDR(addr); + msg[2] = (u32)paddr; + ctx->resp_msg = msg; + + if (ctx->mbox_client.tx_block) + reinit_completion(&ctx->rd_complete); + + rc = mbox_send_message(ctx->mbox_chan, &msg); + if (rc < 0) + goto err_unmap; + + rc = start_i2c_msg_xfer(ctx); + +err_unmap: + dma_unmap_single(ctx->dev, paddr, writelen, DMA_TO_DEVICE); +err: + ctx->resp_msg = NULL; + return rc; +} + +static int xgene_slimpro_i2c_xfer(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + struct slimpro_i2c_dev *ctx = i2c_get_adapdata(adap); + int ret = -EOPNOTSUPP; + u32 val; + + switch (size) { + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_rd(ctx, addr, 0, 0, + SLIMPRO_IIC_SMB_PROTOCOL, + BYTE_DATA, &val); + data->byte = val; + } else { + ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + 0, 0); + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_rd(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + BYTE_DATA, &val); + data->byte = val; + } else { + val = data->byte; + ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + BYTE_DATA, val); + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_rd(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + WORD_DATA, &val); + data->word = val; + } else { + val = data->word; + ret = slimpro_i2c_wr(ctx, addr, command, SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + WORD_DATA, val); + } + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_blkrd(ctx, addr, command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + I2C_SMBUS_BLOCK_MAX + 1, + IIC_SMB_WITH_DATA_LEN, + &data->block[0]); + + } else { + ret = slimpro_i2c_blkwr(ctx, addr, command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_SMB_PROTOCOL, + data->block[0] + 1, + &data->block[0]); + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = slimpro_i2c_blkrd(ctx, addr, + command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_I2C_PROTOCOL, + I2C_SMBUS_BLOCK_MAX, + IIC_SMB_WITHOUT_DATA_LEN, + &data->block[1]); + } else { + ret = slimpro_i2c_blkwr(ctx, addr, command, + SMBUS_CMD_LEN, + SLIMPRO_IIC_I2C_PROTOCOL, + data->block[0], + &data->block[1]); + } + break; + default: + break; + } + return ret; +} + +/* +* Return list of supported functionality. +*/ +static u32 xgene_slimpro_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static struct i2c_algorithm xgene_slimpro_i2c_algorithm = { + .smbus_xfer = xgene_slimpro_i2c_xfer, + .functionality = xgene_slimpro_i2c_func, +}; + +static int __init xgene_slimpro_i2c_probe(struct platform_device *pdev) +{ + struct slimpro_i2c_dev *ctx; + struct i2c_adapter *adapter; + struct mbox_client *cl; + int rc; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = &pdev->dev; + platform_set_drvdata(pdev, ctx); + cl = &ctx->mbox_client; + + /* Request mailbox channel */ + cl->dev = &pdev->dev; + cl->rx_callback = slimpro_i2c_rx_cb; + cl->tx_block = true; + init_completion(&ctx->rd_complete); + cl->tx_tout = MAILBOX_OP_TIMEOUT; + cl->knows_txdone = false; + ctx->mbox_chan = mbox_request_channel(cl, MAILBOX_I2C_INDEX); + if (IS_ERR(ctx->mbox_chan)) { + dev_err(&pdev->dev, "i2c mailbox channel request failed\n"); + return PTR_ERR(ctx->mbox_chan); + } + + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (rc) + dev_warn(&pdev->dev, "Unable to set dma mask\n"); + + /* Setup I2C adapter */ + adapter = &ctx->adapter; + snprintf(adapter->name, sizeof(adapter->name), "MAILBOX I2C"); + adapter->algo = &xgene_slimpro_i2c_algorithm; + adapter->class = I2C_CLASS_HWMON; + adapter->dev.parent = &pdev->dev; + i2c_set_adapdata(adapter, ctx); + rc = i2c_add_adapter(adapter); + if (rc) { + dev_err(&pdev->dev, "Adapter registeration failed\n"); + return rc; + } + + dev_info(&pdev->dev, "Mailbox I2C Adapter registered\n"); + return 0; +} + +static int xgene_slimpro_i2c_remove(struct platform_device *pdev) +{ + struct slimpro_i2c_dev *ctx = platform_get_drvdata(pdev); + + i2c_del_adapter(&ctx->adapter); + + mbox_free_channel(ctx->mbox_chan); + + return 0; +} + +static const struct of_device_id xgene_slimpro_i2c_dt_ids[] = { + {.compatible = "apm,xgene-slimpro-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, xgene_slimpro_i2c_dt_ids); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_slimpro_i2c_acpi_ids[] = { + {"APMC0D40", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, xgene_slimpro_i2c_acpi_ids); +#endif + +static struct platform_driver xgene_slimpro_i2c_driver = { + .probe = xgene_slimpro_i2c_probe, + .remove = xgene_slimpro_i2c_remove, + .driver = { + .name = "xgene-slimpro-i2c", + .of_match_table = of_match_ptr(xgene_slimpro_i2c_dt_ids), + .acpi_match_table = ACPI_PTR(xgene_slimpro_i2c_acpi_ids) + }, +}; + +module_platform_driver(xgene_slimpro_i2c_driver); + +MODULE_DESCRIPTION("APM X-Gene SLIMpro I2C driver"); +MODULE_AUTHOR("Feng Kan "); +MODULE_AUTHOR("Hieu Le "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 1fb2ad9565be7149cf50d663f47f489a9fcda42d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 14 May 2015 14:40:03 +0200 Subject: i2c: rcar: report slave capabilities to users Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 06f34d1c5121..9eef46254369 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -604,7 +604,8 @@ static int rcar_unreg_slave(struct i2c_client *slave) static u32 rcar_i2c_func(struct i2c_adapter *adap) { /* This HW can't do SMBUS_QUICK and NOSTART */ - return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); + return I2C_FUNC_I2C | I2C_FUNC_SLAVE | + (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); } static const struct i2c_algorithm rcar_i2c_algo = { -- cgit v1.2.3 From 0c7cab96ec413a69af86965d94fb650f01c5887a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 14 May 2015 14:40:05 +0200 Subject: i2c: slave: add error messages to slave core Inform users what went wrong from the core, so drivers don't have to do it. Signed-off-by: Wolfram Sang Acked-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 987c124432c5..2a627eb0365b 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -2914,18 +2914,24 @@ int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) { int ret; - if (!client || !slave_cb) + if (!client || !slave_cb) { + WARN(1, "insufficent data\n"); return -EINVAL; + } if (!(client->flags & I2C_CLIENT_TEN)) { /* Enforce stricter address checking */ ret = i2c_check_addr_validity(client->addr); - if (ret) + if (ret) { + dev_err(&client->dev, "%s: invalid address\n", __func__); return ret; + } } - if (!client->adapter->algo->reg_slave) + if (!client->adapter->algo->reg_slave) { + dev_err(&client->dev, "%s: not supported by adapter\n", __func__); return -EOPNOTSUPP; + } client->slave_cb = slave_cb; @@ -2933,8 +2939,10 @@ int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) ret = client->adapter->algo->reg_slave(client); i2c_unlock_adapter(client->adapter); - if (ret) + if (ret) { client->slave_cb = NULL; + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); + } return ret; } @@ -2944,8 +2952,10 @@ int i2c_slave_unregister(struct i2c_client *client) { int ret; - if (!client->adapter->algo->unreg_slave) + if (!client->adapter->algo->unreg_slave) { + dev_err(&client->dev, "%s: not supported by adapter\n", __func__); return -EOPNOTSUPP; + } i2c_lock_adapter(client->adapter); ret = client->adapter->algo->unreg_slave(client); @@ -2953,6 +2963,8 @@ int i2c_slave_unregister(struct i2c_client *client) if (ret == 0) client->slave_cb = NULL; + else + dev_err(&client->dev, "%s: adapter returned error %d\n", __func__, ret); return ret; } -- cgit v1.2.3 From 4c1344f105b626c222ea64669dc6dcfe100ccf3b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 19 May 2015 18:29:48 +0200 Subject: i2c: check for proper length of the reg property int is vague, let's simply use the type of the variable in question. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 2a627eb0365b..b8f11b82485c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1272,7 +1272,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, } addr = of_get_property(node, "reg", &len); - if (!addr || (len < sizeof(int))) { + if (!addr || (len < sizeof(*addr))) { dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", node->full_name); return ERR_PTR(-EINVAL); -- cgit v1.2.3 From ce38815d39eac9c73d37dd4b0039c87f15dcbe18 Mon Sep 17 00:00:00 2001 From: Xudong Chen Date: Thu, 21 May 2015 16:53:28 +0800 Subject: I2C: mediatek: Add driver for MediaTek I2C controller The mediatek SoCs have I2C controller that handle I2C transfer. This patch include common I2C bus driver. This driver is compatible with I2C controller on mt65xx/mt81xx. Signed-off-by: Xudong Chen Signed-off-by: Liguo Zhang Signed-off-by: Eddie Huang Acked-by: Sascha Hauer Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-mt6577.txt | 41 ++ drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-mt65xx.c | 686 +++++++++++++++++++++ 4 files changed, 737 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mt6577.txt create mode 100644 drivers/i2c/busses/i2c-mt65xx.c (limited to 'drivers/i2c') diff --git a/Documentation/devicetree/bindings/i2c/i2c-mt6577.txt b/Documentation/devicetree/bindings/i2c/i2c-mt6577.txt new file mode 100644 index 000000000000..0ce6fa3242f0 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mt6577.txt @@ -0,0 +1,41 @@ +* Mediatek's I2C controller + +The Mediatek's I2C controller is used to interface with I2C devices. + +Required properties: + - compatible: value should be either of the following. + (a) "mediatek,mt6577-i2c", for i2c compatible with mt6577 i2c. + (b) "mediatek,mt6589-i2c", for i2c compatible with mt6589 i2c. + (c) "mediatek,mt8127-i2c", for i2c compatible with mt8127 i2c. + (d) "mediatek,mt8135-i2c", for i2c compatible with mt8135 i2c. + (e) "mediatek,mt8173-i2c", for i2c compatible with mt8173 i2c. + - reg: physical base address of the controller and dma base, length of memory + mapped region. + - interrupts: interrupt number to the cpu. + - clock-div: the fixed value for frequency divider of clock source in i2c + module. Each IC may be different. + - clocks: clock name from clock manager + - clock-names: Must include "main" and "dma", if enable have-pmic need include + "pmic" extra. + +Optional properties: + - clock-frequency: Frequency in Hz of the bus when transfer, the default value + is 100000. + - mediatek,have-pmic: platform can control i2c form special pmic side. + Only mt6589 and mt8135 support this feature. + - mediatek,use-push-pull: IO config use push-pull mode. + +Example: + + i2c0: i2c@1100d000 { + compatible = "mediatek,mt6577-i2c"; + reg = <0x1100d000 0x70>, + <0x11000300 0x80>; + interrupts = ; + clock-frequency = <400000>; + mediatek,have-pmic; + clock-div = <16>; + clocks = <&i2c0_ck>, <&ap_dma_ck>; + clock-names = "main", "dma"; + }; + diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 1d759c3767e7..8a639dce5173 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -620,6 +620,15 @@ config I2C_MPC This driver can also be built as a module. If so, the module will be called i2c-mpc. +config I2C_MT65XX + tristate "MediaTek I2C adapter" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This selects the MediaTek(R) Integrated Inter Circuit bus driver + for MT65xx and MT81xx. + If you want to use MediaTek(R) I2C interface, say Y or M here. + If unsure, say N. + config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" depends on MV64X60 || PLAT_ORION || ARCH_SUNXI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index d8093022ee4c..65995423c680 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_MESON) += i2c-meson.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o +obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MXS) += i2c-mxs.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c new file mode 100644 index 000000000000..b1d2d7cc6db0 --- /dev/null +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Xudong Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_HS_NACKERR (1 << 2) +#define I2C_ACKERR (1 << 1) +#define I2C_TRANSAC_COMP (1 << 0) +#define I2C_TRANSAC_START (1 << 0) +#define I2C_DCM_DISABLE 0x0000 +#define I2C_IO_CONFIG_OPEN_DRAIN 0x0003 +#define I2C_IO_CONFIG_PUSH_PULL 0x0000 +#define I2C_SOFT_RST 0x0001 +#define I2C_FIFO_ADDR_CLR 0x0001 +#define I2C_DELAY_LEN 0x0002 +#define I2C_ST_START_CON 0x8001 +#define I2C_FS_START_CON 0x1800 +#define I2C_TIME_CLR_VALUE 0x0000 +#define I2C_TIME_DEFAULT_VALUE 0x0003 +#define I2C_FS_TIME_INIT_VALUE 0x1303 +#define I2C_WRRD_TRANAC_VALUE 0x0002 +#define I2C_RD_TRANAC_VALUE 0x0001 + +#define I2C_DMA_CON_TX 0x0000 +#define I2C_DMA_CON_RX 0x0001 +#define I2C_DMA_START_EN 0x0001 +#define I2C_DMA_INT_FLAG_NONE 0x0000 +#define I2C_DMA_CLR_FLAG 0x0000 + +#define I2C_DEFAULT_SPEED 100000 /* hz */ +#define MAX_FS_MODE_SPEED 400000 +#define MAX_HS_MODE_SPEED 3400000 +#define MAX_SAMPLE_CNT_DIV 8 +#define MAX_STEP_CNT_DIV 64 +#define MAX_HS_STEP_CNT_DIV 8 + +#define I2C_CONTROL_RS (0x1 << 1) +#define I2C_CONTROL_DMA_EN (0x1 << 2) +#define I2C_CONTROL_CLK_EXT_EN (0x1 << 3) +#define I2C_CONTROL_DIR_CHANGE (0x1 << 4) +#define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5) +#define I2C_CONTROL_TRANSFER_LEN_CHANGE (0x1 << 6) +#define I2C_CONTROL_WRAPPER (0x1 << 0) + +#define I2C_DRV_NAME "i2c-mt65xx" + +enum DMA_REGS_OFFSET { + OFFSET_INT_FLAG = 0x0, + OFFSET_INT_EN = 0x04, + OFFSET_EN = 0x08, + OFFSET_CON = 0x18, + OFFSET_TX_MEM_ADDR = 0x1c, + OFFSET_RX_MEM_ADDR = 0x20, + OFFSET_TX_LEN = 0x24, + OFFSET_RX_LEN = 0x28, +}; + +enum i2c_trans_st_rs { + I2C_TRANS_STOP = 0, + I2C_TRANS_REPEATED_START, +}; + +enum mtk_trans_op { + I2C_MASTER_WR = 1, + I2C_MASTER_RD, + I2C_MASTER_WRRD, +}; + +enum I2C_REGS_OFFSET { + OFFSET_DATA_PORT = 0x0, + OFFSET_SLAVE_ADDR = 0x04, + OFFSET_INTR_MASK = 0x08, + OFFSET_INTR_STAT = 0x0c, + OFFSET_CONTROL = 0x10, + OFFSET_TRANSFER_LEN = 0x14, + OFFSET_TRANSAC_LEN = 0x18, + OFFSET_DELAY_LEN = 0x1c, + OFFSET_TIMING = 0x20, + OFFSET_START = 0x24, + OFFSET_EXT_CONF = 0x28, + OFFSET_FIFO_STAT = 0x30, + OFFSET_FIFO_THRESH = 0x34, + OFFSET_FIFO_ADDR_CLR = 0x38, + OFFSET_IO_CONFIG = 0x40, + OFFSET_RSV_DEBUG = 0x44, + OFFSET_HS = 0x48, + OFFSET_SOFTRESET = 0x50, + OFFSET_DCM_EN = 0x54, + OFFSET_PATH_DIR = 0x60, + OFFSET_DEBUGSTAT = 0x64, + OFFSET_DEBUGCTRL = 0x68, + OFFSET_TRANSFER_LEN_AUX = 0x6c, +}; + +struct mtk_i2c_compatible { + const struct i2c_adapter_quirks *quirks; + unsigned char pmic_i2c: 1; + unsigned char dcm: 1; +}; + +struct mtk_i2c { + struct i2c_adapter adap; /* i2c host adapter */ + struct device *dev; + struct completion msg_complete; + + /* set in i2c probe */ + void __iomem *base; /* i2c base addr */ + void __iomem *pdmabase; /* dma base address*/ + struct clk *clk_main; /* main clock for i2c bus */ + struct clk *clk_dma; /* DMA clock for i2c via DMA */ + struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */ + bool have_pmic; /* can use i2c pins from PMIC */ + bool use_push_pull; /* IO config push-pull mode */ + + u16 irq_stat; /* interrupt status */ + unsigned int speed_hz; /* The speed in transfer */ + enum mtk_trans_op op; + u16 timing_reg; + u16 high_speed_reg; + const struct mtk_i2c_compatible *dev_comp; +}; + +static const struct i2c_adapter_quirks mt6577_i2c_quirks = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_num_msgs = 1, + .max_write_len = 255, + .max_read_len = 255, + .max_comb_1st_msg_len = 255, + .max_comb_2nd_msg_len = 31, +}; + +static const struct mtk_i2c_compatible mt6577_compat = { + .quirks = &mt6577_i2c_quirks, + .pmic_i2c = 0, + .dcm = 1, +}; + +static const struct mtk_i2c_compatible mt6589_compat = { + .quirks = &mt6577_i2c_quirks, + .pmic_i2c = 1, + .dcm = 0, +}; + +static const struct of_device_id mtk_i2c_of_match[] = { + { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, + { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_i2c_of_match); + +static int mtk_i2c_clock_enable(struct mtk_i2c *i2c) +{ + int ret; + + ret = clk_prepare_enable(i2c->clk_dma); + if (ret) + return ret; + + ret = clk_prepare_enable(i2c->clk_main); + if (ret) + goto err_main; + + if (i2c->have_pmic) { + ret = clk_prepare_enable(i2c->clk_pmic); + if (ret) + goto err_pmic; + } + return 0; + +err_pmic: + clk_disable_unprepare(i2c->clk_main); +err_main: + clk_disable_unprepare(i2c->clk_dma); + + return ret; +} + +static void mtk_i2c_clock_disable(struct mtk_i2c *i2c) +{ + if (i2c->have_pmic) + clk_disable_unprepare(i2c->clk_pmic); + + clk_disable_unprepare(i2c->clk_main); + clk_disable_unprepare(i2c->clk_dma); +} + +static void mtk_i2c_init_hw(struct mtk_i2c *i2c) +{ + u16 control_reg; + + writew(I2C_SOFT_RST, i2c->base + OFFSET_SOFTRESET); + + /* Set ioconfig */ + if (i2c->use_push_pull) + writew(I2C_IO_CONFIG_PUSH_PULL, i2c->base + OFFSET_IO_CONFIG); + else + writew(I2C_IO_CONFIG_OPEN_DRAIN, i2c->base + OFFSET_IO_CONFIG); + + if (i2c->dev_comp->dcm) + writew(I2C_DCM_DISABLE, i2c->base + OFFSET_DCM_EN); + + writew(i2c->timing_reg, i2c->base + OFFSET_TIMING); + writew(i2c->high_speed_reg, i2c->base + OFFSET_HS); + + /* If use i2c pin from PMIC mt6397 side, need set PATH_DIR first */ + if (i2c->have_pmic) + writew(I2C_CONTROL_WRAPPER, i2c->base + OFFSET_PATH_DIR); + + control_reg = I2C_CONTROL_ACKERR_DET_EN | + I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN; + writew(control_reg, i2c->base + OFFSET_CONTROL); + writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN); +} + +/* + * Calculate i2c port speed + * + * Hardware design: + * i2c_bus_freq = parent_clk / (clock_div * 2 * sample_cnt * step_cnt) + * clock_div: fixed in hardware, but may be various in different SoCs + * + * The calculation want to pick the highest bus frequency that is still + * less than or equal to i2c->speed_hz. The calculation try to get + * sample_cnt and step_cn + */ +static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, + unsigned int clock_div) +{ + unsigned int clk_src; + unsigned int step_cnt; + unsigned int sample_cnt; + unsigned int max_step_cnt; + unsigned int target_speed; + unsigned int base_sample_cnt = MAX_SAMPLE_CNT_DIV; + unsigned int base_step_cnt; + unsigned int opt_div; + unsigned int best_mul; + unsigned int cnt_mul; + + clk_src = parent_clk / clock_div; + target_speed = i2c->speed_hz; + + if (target_speed > MAX_HS_MODE_SPEED) + target_speed = MAX_HS_MODE_SPEED; + + if (target_speed > MAX_FS_MODE_SPEED) + max_step_cnt = MAX_HS_STEP_CNT_DIV; + else + max_step_cnt = MAX_STEP_CNT_DIV; + + base_step_cnt = max_step_cnt; + /* Find the best combination */ + opt_div = DIV_ROUND_UP(clk_src >> 1, target_speed); + best_mul = MAX_SAMPLE_CNT_DIV * max_step_cnt; + + /* Search for the best pair (sample_cnt, step_cnt) with + * 0 < sample_cnt < MAX_SAMPLE_CNT_DIV + * 0 < step_cnt < max_step_cnt + * sample_cnt * step_cnt >= opt_div + * optimizing for sample_cnt * step_cnt being minimal + */ + for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) { + step_cnt = DIV_ROUND_UP(opt_div, sample_cnt); + cnt_mul = step_cnt * sample_cnt; + if (step_cnt > max_step_cnt) + continue; + + if (cnt_mul < best_mul) { + best_mul = cnt_mul; + base_sample_cnt = sample_cnt; + base_step_cnt = step_cnt; + if (best_mul == opt_div) + break; + } + } + + sample_cnt = base_sample_cnt; + step_cnt = base_step_cnt; + + if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) { + /* In this case, hardware can't support such + * low i2c_bus_freq + */ + dev_dbg(i2c->dev, "Unsupported speed (%uhz)\n", target_speed); + return -EINVAL; + } + + step_cnt--; + sample_cnt--; + + if (target_speed > MAX_FS_MODE_SPEED) { + /* Set the high speed mode register */ + i2c->timing_reg = I2C_FS_TIME_INIT_VALUE; + i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE | + (sample_cnt << 12) | (step_cnt << 8); + } else { + i2c->timing_reg = (sample_cnt << 8) | (step_cnt << 0); + /* Disable the high speed transaction */ + i2c->high_speed_reg = I2C_TIME_CLR_VALUE; + } + + return 0; +} + +static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs) +{ + u16 addr_reg; + u16 control_reg; + dma_addr_t rpaddr = 0; + dma_addr_t wpaddr = 0; + int ret; + + i2c->irq_stat = 0; + + reinit_completion(&i2c->msg_complete); + + control_reg = readw(i2c->base + OFFSET_CONTROL) & + ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); + if (i2c->speed_hz > 400000) + control_reg |= I2C_CONTROL_RS; + + if (i2c->op == I2C_MASTER_WRRD) + control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS; + + writew(control_reg, i2c->base + OFFSET_CONTROL); + + /* set start condition */ + if (i2c->speed_hz <= 100000) + writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF); + else + writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF); + + addr_reg = msgs->addr << 1; + if (i2c->op == I2C_MASTER_RD) + addr_reg |= 0x1; + + writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR); + + /* Clear interrupt status */ + writew(I2C_HS_NACKERR | I2C_ACKERR | I2C_TRANSAC_COMP, + i2c->base + OFFSET_INTR_STAT); + writew(I2C_FIFO_ADDR_CLR, i2c->base + OFFSET_FIFO_ADDR_CLR); + + /* Enable interrupt */ + writew(I2C_HS_NACKERR | I2C_ACKERR | I2C_TRANSAC_COMP, + i2c->base + OFFSET_INTR_MASK); + + /* Set transfer and transaction len */ + if (i2c->op == I2C_MASTER_WRRD) { + writew(msgs->len | ((msgs + 1)->len) << 8, + i2c->base + OFFSET_TRANSFER_LEN); + writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN); + } else { + writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN); + writew(I2C_RD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN); + } + + /* Prepare buffer data to start transfer */ + if (i2c->op == I2C_MASTER_RD) { + writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG); + writel(I2C_DMA_CON_RX, i2c->pdmabase + OFFSET_CON); + rpaddr = dma_map_single(i2c->dev, msgs->buf, + msgs->len, DMA_FROM_DEVICE); + if (dma_mapping_error(i2c->dev, rpaddr)) + return -ENOMEM; + writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR); + writel(msgs->len, i2c->pdmabase + OFFSET_RX_LEN); + } else if (i2c->op == I2C_MASTER_WR) { + writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG); + writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON); + wpaddr = dma_map_single(i2c->dev, msgs->buf, + msgs->len, DMA_TO_DEVICE); + if (dma_mapping_error(i2c->dev, wpaddr)) + return -ENOMEM; + writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR); + writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN); + } else { + writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG); + writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON); + wpaddr = dma_map_single(i2c->dev, msgs->buf, + msgs->len, DMA_TO_DEVICE); + if (dma_mapping_error(i2c->dev, wpaddr)) + return -ENOMEM; + rpaddr = dma_map_single(i2c->dev, (msgs + 1)->buf, + (msgs + 1)->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(i2c->dev, rpaddr)) { + dma_unmap_single(i2c->dev, wpaddr, + msgs->len, DMA_TO_DEVICE); + return -ENOMEM; + } + writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR); + writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR); + writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN); + writel((msgs + 1)->len, i2c->pdmabase + OFFSET_RX_LEN); + } + + writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN); + writew(I2C_TRANSAC_START, i2c->base + OFFSET_START); + + ret = wait_for_completion_timeout(&i2c->msg_complete, + i2c->adap.timeout); + + /* Clear interrupt mask */ + writew(~(I2C_HS_NACKERR | I2C_ACKERR | + I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK); + + if (i2c->op == I2C_MASTER_WR) { + dma_unmap_single(i2c->dev, wpaddr, + msgs->len, DMA_TO_DEVICE); + } else if (i2c->op == I2C_MASTER_RD) { + dma_unmap_single(i2c->dev, rpaddr, + msgs->len, DMA_FROM_DEVICE); + } else { + dma_unmap_single(i2c->dev, wpaddr, msgs->len, + DMA_TO_DEVICE); + dma_unmap_single(i2c->dev, rpaddr, (msgs + 1)->len, + DMA_FROM_DEVICE); + } + + if (ret == 0) { + dev_dbg(i2c->dev, "addr: %x, transfer timeout\n", msgs->addr); + mtk_i2c_init_hw(i2c); + return -ETIMEDOUT; + } + + completion_done(&i2c->msg_complete); + + if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) { + dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr); + mtk_i2c_init_hw(i2c); + return -ENXIO; + } + + return 0; +} + +static int mtk_i2c_transfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + int ret; + int left_num = num; + struct mtk_i2c *i2c = i2c_get_adapdata(adap); + + ret = mtk_i2c_clock_enable(i2c); + if (ret) + return ret; + + if (!msgs->buf) { + dev_dbg(i2c->dev, "data buffer is NULL.\n"); + ret = -EINVAL; + goto err_exit; + } + + if (msgs->flags & I2C_M_RD) + i2c->op = I2C_MASTER_RD; + else + i2c->op = I2C_MASTER_WR; + + if (num > 1) { + /* combined two messages into one transaction */ + i2c->op = I2C_MASTER_WRRD; + left_num--; + } + + /* always use DMA mode. */ + ret = mtk_i2c_do_transfer(i2c, msgs); + if (ret < 0) + goto err_exit; + + /* the return value is number of executed messages */ + ret = num; + +err_exit: + mtk_i2c_clock_disable(i2c); + return ret; +} + +static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id) +{ + struct mtk_i2c *i2c = dev_id; + + i2c->irq_stat = readw(i2c->base + OFFSET_INTR_STAT); + writew(I2C_HS_NACKERR | I2C_ACKERR + | I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT); + + complete(&i2c->msg_complete); + + return IRQ_HANDLED; +} + +static u32 mtk_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm mtk_i2c_algorithm = { + .master_xfer = mtk_i2c_transfer, + .functionality = mtk_i2c_functionality, +}; + +static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c, + unsigned int *clk_src_div) +{ + int ret; + + ret = of_property_read_u32(np, "clock-frequency", &i2c->speed_hz); + if (ret < 0) + i2c->speed_hz = I2C_DEFAULT_SPEED; + + ret = of_property_read_u32(np, "clock-div", clk_src_div); + if (ret < 0) + return ret; + + if (*clk_src_div == 0) + return -EINVAL; + + i2c->have_pmic = of_property_read_bool(np, "mediatek,have-pmic"); + i2c->use_push_pull = + of_property_read_bool(np, "mediatek,use-push-pull"); + + return 0; +} + +static int mtk_i2c_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + int ret = 0; + struct mtk_i2c *i2c; + struct clk *clk; + unsigned int clk_src_div; + struct resource *res; + int irq; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c, &clk_src_div); + if (ret) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2c->pdmabase)) + return PTR_ERR(i2c->pdmabase); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq; + + init_completion(&i2c->msg_complete); + + of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node); + if (!of_id) + return -EINVAL; + + i2c->dev_comp = of_id->data; + i2c->adap.dev.of_node = pdev->dev.of_node; + i2c->dev = &pdev->dev; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &mtk_i2c_algorithm; + i2c->adap.quirks = i2c->dev_comp->quirks; + i2c->adap.timeout = 2 * HZ; + i2c->adap.retries = 1; + + if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c) + return -EINVAL; + + i2c->clk_main = devm_clk_get(&pdev->dev, "main"); + if (IS_ERR(i2c->clk_main)) { + dev_err(&pdev->dev, "cannot get main clock\n"); + return PTR_ERR(i2c->clk_main); + } + + i2c->clk_dma = devm_clk_get(&pdev->dev, "dma"); + if (IS_ERR(i2c->clk_dma)) { + dev_err(&pdev->dev, "cannot get dma clock\n"); + return PTR_ERR(i2c->clk_dma); + } + + clk = i2c->clk_main; + if (i2c->have_pmic) { + i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic"); + if (IS_ERR(i2c->clk_pmic)) { + dev_err(&pdev->dev, "cannot get pmic clock\n"); + return PTR_ERR(i2c->clk_pmic); + } + clk = i2c->clk_pmic; + } + + strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); + + ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk), clk_src_div); + if (ret) { + dev_err(&pdev->dev, "Failed to set the speed.\n"); + return -EINVAL; + } + + ret = mtk_i2c_clock_enable(i2c); + if (ret) { + dev_err(&pdev->dev, "clock enable failed!\n"); + return ret; + } + mtk_i2c_init_hw(i2c); + mtk_i2c_clock_disable(i2c); + + ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq, + IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c); + if (ret < 0) { + dev_err(&pdev->dev, + "Request I2C IRQ %d fail\n", irq); + return ret; + } + + i2c_set_adapdata(&i2c->adap, i2c); + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n"); + return ret; + } + + platform_set_drvdata(pdev, i2c); + + return 0; +} + +static int mtk_i2c_remove(struct platform_device *pdev) +{ + struct mtk_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + + return 0; +} + +static struct platform_driver mtk_i2c_driver = { + .probe = mtk_i2c_probe, + .remove = mtk_i2c_remove, + .driver = { + .name = I2C_DRV_NAME, + .of_match_table = of_match_ptr(mtk_i2c_of_match), + }, +}; + +module_platform_driver(mtk_i2c_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MediaTek I2C Bus Driver"); +MODULE_AUTHOR("Xudong Chen "); -- cgit v1.2.3 From b2ed11e224a28b896b52c109657d183059f7a53f Mon Sep 17 00:00:00 2001 From: Eddie Huang Date: Thu, 21 May 2015 16:53:30 +0800 Subject: I2C: mediatek: Add driver for MediaTek MT8173 I2C controller Add mediatek MT8173 I2C controller driver. Compare to I2C controller of earlier mediatek SoC, MT8173 fix write-then-read limitation, and also increase message size to 64kb. Signed-off-by: Xudong Chen Signed-off-by: Liguo Zhang Signed-off-by: Eddie Huang Acked-by: Sascha Hauer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 103 +++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 29 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index b1d2d7cc6db0..9920eef74672 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -33,10 +33,13 @@ #include #include +#define I2C_RS_TRANSFER (1 << 4) #define I2C_HS_NACKERR (1 << 2) #define I2C_ACKERR (1 << 1) #define I2C_TRANSAC_COMP (1 << 0) #define I2C_TRANSAC_START (1 << 0) +#define I2C_RS_MUL_CNFG (1 << 15) +#define I2C_RS_MUL_TRIG (1 << 14) #define I2C_DCM_DISABLE 0x0000 #define I2C_IO_CONFIG_OPEN_DRAIN 0x0003 #define I2C_IO_CONFIG_PUSH_PULL 0x0000 @@ -126,6 +129,7 @@ struct mtk_i2c_compatible { const struct i2c_adapter_quirks *quirks; unsigned char pmic_i2c: 1; unsigned char dcm: 1; + unsigned char auto_restart: 1; }; struct mtk_i2c { @@ -159,21 +163,39 @@ static const struct i2c_adapter_quirks mt6577_i2c_quirks = { .max_comb_2nd_msg_len = 31, }; +static const struct i2c_adapter_quirks mt8173_i2c_quirks = { + .max_num_msgs = 65535, + .max_write_len = 65535, + .max_read_len = 65535, + .max_comb_1st_msg_len = 65535, + .max_comb_2nd_msg_len = 65535, +}; + static const struct mtk_i2c_compatible mt6577_compat = { .quirks = &mt6577_i2c_quirks, .pmic_i2c = 0, .dcm = 1, + .auto_restart = 0, }; static const struct mtk_i2c_compatible mt6589_compat = { .quirks = &mt6577_i2c_quirks, .pmic_i2c = 1, .dcm = 0, + .auto_restart = 0, +}; + +static const struct mtk_i2c_compatible mt8173_compat = { + .quirks = &mt8173_i2c_quirks, + .pmic_i2c = 0, + .dcm = 1, + .auto_restart = 1, }; static const struct of_device_id mtk_i2c_of_match[] = { { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, {} }; MODULE_DEVICE_TABLE(of, mtk_i2c_of_match); @@ -332,21 +354,27 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk, return 0; } -static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs) +static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, + int num, int left_num) { u16 addr_reg; + u16 start_reg; u16 control_reg; + u16 restart_flag = 0; dma_addr_t rpaddr = 0; dma_addr_t wpaddr = 0; int ret; i2c->irq_stat = 0; + if (i2c->dev_comp->auto_restart) + restart_flag = I2C_RS_TRANSFER; + reinit_completion(&i2c->msg_complete); control_reg = readw(i2c->base + OFFSET_CONTROL) & ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); - if (i2c->speed_hz > 400000) + if ((i2c->speed_hz > 400000) || (left_num >= 1)) control_reg |= I2C_CONTROL_RS; if (i2c->op == I2C_MASTER_WRRD) @@ -367,13 +395,13 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs) writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR); /* Clear interrupt status */ - writew(I2C_HS_NACKERR | I2C_ACKERR | I2C_TRANSAC_COMP, - i2c->base + OFFSET_INTR_STAT); + writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | + I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT); writew(I2C_FIFO_ADDR_CLR, i2c->base + OFFSET_FIFO_ADDR_CLR); /* Enable interrupt */ - writew(I2C_HS_NACKERR | I2C_ACKERR | I2C_TRANSAC_COMP, - i2c->base + OFFSET_INTR_MASK); + writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | + I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK); /* Set transfer and transaction len */ if (i2c->op == I2C_MASTER_WRRD) { @@ -382,7 +410,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs) writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN); } else { writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN); - writew(I2C_RD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN); + writew(num, i2c->base + OFFSET_TRANSAC_LEN); } /* Prepare buffer data to start transfer */ @@ -426,13 +454,21 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs) } writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN); - writew(I2C_TRANSAC_START, i2c->base + OFFSET_START); + + if (!i2c->dev_comp->auto_restart) { + start_reg = I2C_TRANSAC_START; + } else { + start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG; + if (left_num >= 1) + start_reg |= I2C_RS_MUL_CNFG; + } + writew(start_reg, i2c->base + OFFSET_START); ret = wait_for_completion_timeout(&i2c->msg_complete, i2c->adap.timeout); /* Clear interrupt mask */ - writew(~(I2C_HS_NACKERR | I2C_ACKERR | + writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK); if (i2c->op == I2C_MASTER_WR) { @@ -476,28 +512,33 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, if (ret) return ret; - if (!msgs->buf) { - dev_dbg(i2c->dev, "data buffer is NULL.\n"); - ret = -EINVAL; - goto err_exit; - } - - if (msgs->flags & I2C_M_RD) - i2c->op = I2C_MASTER_RD; - else - i2c->op = I2C_MASTER_WR; + while (left_num--) { + if (!msgs->buf) { + dev_dbg(i2c->dev, "data buffer is NULL.\n"); + ret = -EINVAL; + goto err_exit; + } - if (num > 1) { - /* combined two messages into one transaction */ - i2c->op = I2C_MASTER_WRRD; - left_num--; - } + if (msgs->flags & I2C_M_RD) + i2c->op = I2C_MASTER_RD; + else + i2c->op = I2C_MASTER_WR; + + if (!i2c->dev_comp->auto_restart) { + if (num > 1) { + /* combined two messages into one transaction */ + i2c->op = I2C_MASTER_WRRD; + left_num--; + } + } - /* always use DMA mode. */ - ret = mtk_i2c_do_transfer(i2c, msgs); - if (ret < 0) - goto err_exit; + /* always use DMA mode. */ + ret = mtk_i2c_do_transfer(i2c, msgs, num, left_num); + if (ret < 0) + goto err_exit; + msgs++; + } /* the return value is number of executed messages */ ret = num; @@ -509,9 +550,13 @@ err_exit: static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id) { struct mtk_i2c *i2c = dev_id; + u16 restart_flag = 0; + + if (i2c->dev_comp->auto_restart) + restart_flag = I2C_RS_TRANSFER; i2c->irq_stat = readw(i2c->base + OFFSET_INTR_STAT); - writew(I2C_HS_NACKERR | I2C_ACKERR + writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT); complete(&i2c->msg_complete); -- cgit v1.2.3 From 95cc1e3d76398c4e8003dafe652285e43be90ee7 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Mon, 18 May 2015 22:24:01 +0530 Subject: i2c: core: fix typo in comment Signed-off-by: Shailendra Verma Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index b8f11b82485c..c007d3929da5 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1673,7 +1673,7 @@ void i2c_del_adapter(struct i2c_adapter *adap) * FIXME: This is old code and should ideally be replaced by an * alternative which results in decoupling the lifetime of the struct * device from the i2c_adapter, like spi or netdev do. Any solution - * should be throughly tested with DEBUG_KOBJECT_RELEASE enabled! + * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled! */ init_completion(&adap->dev_released); device_unregister(&adap->dev); -- cgit v1.2.3 From 0e6dd6a232e1910b00776318ec8b82589769a64c Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Mon, 18 May 2015 22:37:54 +0530 Subject: i2c: algo-pca: fix typo in comment Signed-off-by: Shailendra Verma Signed-off-by: Wolfram Sang --- drivers/i2c/algos/i2c-algo-pca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c index 580dbf05c148..e370804ec8bc 100644 --- a/drivers/i2c/algos/i2c-algo-pca.c +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -521,7 +521,7 @@ static int pca_init(struct i2c_adapter *adap) pca_set_con(pca_data, I2C_PCA_CON_ENSIO); } - udelay(500); /* 500 us for oscilator to stabilise */ + udelay(500); /* 500 us for oscillator to stabilise */ return 0; } -- cgit v1.2.3 From edc9102a8e00450871ce523da3ad7b6ea2f11069 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Tue, 19 May 2015 20:09:41 +0530 Subject: i2c: smbus: fix typo in commet Signed-off-by: Shailendra Verma Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-smbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 9ebf9cb4ad7a..94765a81970d 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -89,7 +89,7 @@ static void smbus_alert(struct work_struct *work) * to high, because of slave transmit arbitration. After * responding, an SMBus device stops asserting SMBALERT#. * - * Note that SMBus 2.0 reserves 10-bit addresess for future + * Note that SMBus 2.0 reserves 10-bit addresses for future * use. We neither handle them, nor try to use PEC here. */ status = i2c_smbus_read_byte(ara); -- cgit v1.2.3 From 7ef85f5fdd081b982d43b95b06272f8646a98818 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 20 May 2015 16:36:52 +0300 Subject: i2c: core: Reduce stack size of acpi_i2c_space_handler() sizeof(struct i2c_client) is 1088 bytes on a CONFIG_X86_64=y build and produces following warning when CONFIG_FRAME_WARN is set to 1024: drivers/i2c/i2c-core.c: In function ‘acpi_i2c_space_handler’: drivers/i2c/i2c-core.c:367:1: warning: the frame size of 1152 bytes is larger than 1024 bytes [-Wframe-larger-than=] This is not critical given that kernel stack is 16 kB on x86_64 but lets reduce the stack usage by allocating the struct i2c_client from the heap. Signed-off-by: Jarkko Nikula Acked-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index c007d3929da5..96771eaa923e 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -258,7 +258,7 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command, struct acpi_connection_info *info = &data->info; struct acpi_resource_i2c_serialbus *sb; struct i2c_adapter *adapter = data->adapter; - struct i2c_client client; + struct i2c_client *client; struct acpi_resource *ares; u32 accessor_type = function >> 16; u8 action = function & ACPI_IO_MASK; @@ -269,6 +269,12 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command, if (ACPI_FAILURE(ret)) return ret; + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) { + ret = AE_NO_MEMORY; + goto err; + } + if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { ret = AE_BAD_PARAMETER; goto err; @@ -280,75 +286,73 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command, goto err; } - memset(&client, 0, sizeof(client)); - client.adapter = adapter; - client.addr = sb->slave_address; - client.flags = 0; + client->adapter = adapter; + client->addr = sb->slave_address; if (sb->access_mode == ACPI_I2C_10BIT_MODE) - client.flags |= I2C_CLIENT_TEN; + client->flags |= I2C_CLIENT_TEN; switch (accessor_type) { case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV: if (action == ACPI_READ) { - status = i2c_smbus_read_byte(&client); + status = i2c_smbus_read_byte(client); if (status >= 0) { gsb->bdata = status; status = 0; } } else { - status = i2c_smbus_write_byte(&client, gsb->bdata); + status = i2c_smbus_write_byte(client, gsb->bdata); } break; case ACPI_GSB_ACCESS_ATTRIB_BYTE: if (action == ACPI_READ) { - status = i2c_smbus_read_byte_data(&client, command); + status = i2c_smbus_read_byte_data(client, command); if (status >= 0) { gsb->bdata = status; status = 0; } } else { - status = i2c_smbus_write_byte_data(&client, command, + status = i2c_smbus_write_byte_data(client, command, gsb->bdata); } break; case ACPI_GSB_ACCESS_ATTRIB_WORD: if (action == ACPI_READ) { - status = i2c_smbus_read_word_data(&client, command); + status = i2c_smbus_read_word_data(client, command); if (status >= 0) { gsb->wdata = status; status = 0; } } else { - status = i2c_smbus_write_word_data(&client, command, + status = i2c_smbus_write_word_data(client, command, gsb->wdata); } break; case ACPI_GSB_ACCESS_ATTRIB_BLOCK: if (action == ACPI_READ) { - status = i2c_smbus_read_block_data(&client, command, + status = i2c_smbus_read_block_data(client, command, gsb->data); if (status >= 0) { gsb->len = status; status = 0; } } else { - status = i2c_smbus_write_block_data(&client, command, + status = i2c_smbus_write_block_data(client, command, gsb->len, gsb->data); } break; case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE: if (action == ACPI_READ) { - status = acpi_gsb_i2c_read_bytes(&client, command, + status = acpi_gsb_i2c_read_bytes(client, command, gsb->data, info->access_length); if (status > 0) status = 0; } else { - status = acpi_gsb_i2c_write_bytes(&client, command, + status = acpi_gsb_i2c_write_bytes(client, command, gsb->data, info->access_length); } break; @@ -362,6 +366,7 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command, gsb->status = status; err: + kfree(client); ACPI_FREE(ares); return ret; } -- cgit v1.2.3 From 7e2dc811b4ae70ecfa3c164e3e400a78a3244338 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Wed, 13 May 2015 11:03:42 +0200 Subject: i2c: axxia: Add bus recovery functionality Use recovery framework and implement bus recovery using "Bus Monitor" register. Tests show that shortening SDA to GND results in "completion" timeout with "BUSY" bit still set, so initiate recovery in this case. Signed-off-by: Alexander Sverdlin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-axxia.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index 32d883490863..c335cc7852f9 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -42,6 +42,10 @@ #define IBML_LOW_SEXT 0x18 #define TIMER_CLOCK_DIV 0x1c #define I2C_BUS_MONITOR 0x20 +#define BM_SDAC BIT(3) +#define BM_SCLC BIT(2) +#define BM_SDAS BIT(1) +#define BM_SCLS BIT(0) #define SOFT_RESET 0x24 #define MST_COMMAND 0x28 #define CMD_BUSY (1<<3) @@ -394,6 +398,9 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) if (time_left == 0) idev->msg_err = -ETIMEDOUT; + if (idev->msg_err == -ETIMEDOUT) + i2c_recover_bus(&idev->adapter); + if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO) axxia_i2c_init(idev); @@ -437,6 +444,39 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) return ret ? : i; } +static int axxia_i2c_get_scl(struct i2c_adapter *adap) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + + return !!(readl(idev->base + I2C_BUS_MONITOR) & BM_SCLS); +} + +static void axxia_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + u32 tmp; + + /* Preserve SDA Control */ + tmp = readl(idev->base + I2C_BUS_MONITOR) & BM_SDAC; + if (!val) + tmp |= BM_SCLC; + writel(tmp, idev->base + I2C_BUS_MONITOR); +} + +static int axxia_i2c_get_sda(struct i2c_adapter *adap) +{ + struct axxia_i2c_dev *idev = i2c_get_adapdata(adap); + + return !!(readl(idev->base + I2C_BUS_MONITOR) & BM_SDAS); +} + +static struct i2c_bus_recovery_info axxia_i2c_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = axxia_i2c_get_scl, + .set_scl = axxia_i2c_set_scl, + .get_sda = axxia_i2c_get_sda, +}; + static u32 axxia_i2c_func(struct i2c_adapter *adap) { u32 caps = (I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | @@ -511,6 +551,7 @@ static int axxia_i2c_probe(struct platform_device *pdev) strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); idev->adapter.owner = THIS_MODULE; idev->adapter.algo = &axxia_i2c_algo; + idev->adapter.bus_recovery_info = &axxia_i2c_recovery_info; idev->adapter.quirks = &axxia_i2c_quirks; idev->adapter.dev.parent = &pdev->dev; idev->adapter.dev.of_node = pdev->dev.of_node; -- cgit v1.2.3 From 8503ff166504272577e8f4c77d658395e744a2bb Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 20 May 2015 22:33:13 +0800 Subject: i2c: designware: Avoid unnecessary resuming during system suspend Commit 1fc2fe204cb9 ("i2c: designware: Add runtime PM hooks") adds runtime pm support using the same ops for system pm and runtime pm. When suspend to ram, the i2c host may have been runtime suspended, thus i2c_dw_disable() hangs. Previously, I fixed this issue by separating ops for system pm and runtime pm, then in the system suspend/resume path, runtime pm apis are used to ensure the device is at correct state. But as Mika Westerberg pointed out: it sounds a bit silly to resume the device just because you want to call i2c_dw_disable() for it before suspending again. He then suggested an elegant solution which keeps the device runtime suspended during system suspend with the help of 'dev->power.direct_complete'. This patch adopted this solution, and in fact Mika provided the main code. Signed-off-by: Jisheng Zhang Acked-by: Mika Westerberg Tested-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 32 +++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 0a80e4aabaed..4794911a6165 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -298,6 +298,22 @@ static const struct of_device_id dw_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, dw_i2c_of_match); #endif +#ifdef CONFIG_PM_SLEEP +static int dw_i2c_prepare(struct device *dev) +{ + return pm_runtime_suspended(dev); +} + +static void dw_i2c_complete(struct device *dev) +{ + if (dev->power.direct_complete) + pm_request_resume(dev); +} +#else +#define dw_i2c_prepare NULL +#define dw_i2c_complete NULL +#endif + #ifdef CONFIG_PM static int dw_i2c_suspend(struct device *dev) { @@ -322,10 +338,18 @@ static int dw_i2c_resume(struct device *dev) return 0; } -#endif -static UNIVERSAL_DEV_PM_OPS(dw_i2c_dev_pm_ops, dw_i2c_suspend, - dw_i2c_resume, NULL); +static const struct dev_pm_ops dw_i2c_dev_pm_ops = { + .prepare = dw_i2c_prepare, + .complete = dw_i2c_complete, + SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_suspend, dw_i2c_resume) + SET_RUNTIME_PM_OPS(dw_i2c_suspend, dw_i2c_resume, NULL) +}; + +#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops) +#else +#define DW_I2C_DEV_PMOPS NULL +#endif /* work with hotplug and coldplug */ MODULE_ALIAS("platform:i2c_designware"); @@ -337,7 +361,7 @@ static struct platform_driver dw_i2c_driver = { .name = "i2c_designware", .of_match_table = of_match_ptr(dw_i2c_of_match), .acpi_match_table = ACPI_PTR(dw_i2c_acpi_match), - .pm = &dw_i2c_dev_pm_ops, + .pm = DW_I2C_DEV_PMOPS, }, }; -- cgit v1.2.3 From 0ee04e91eacd15fa95cd60ccccb513c1a5734916 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Thu, 14 May 2015 15:36:04 -0700 Subject: i2c: iproc: Add suspend/resume support Add suspend/resume support to the Broadcom iProc I2C driver Signed-off-by: Ray Jui Reviewed-by: Scott Branden Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-bcm-iproc.c | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index f9f2c2082151..0419f5284609 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -91,6 +91,7 @@ struct bcm_iproc_i2c_dev { void __iomem *base; struct i2c_adapter adapter; + unsigned int bus_speed; struct completion done; int xfer_is_done; @@ -309,6 +310,7 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) bus_speed = 400000; } + iproc_i2c->bus_speed = bus_speed; val = readl(iproc_i2c->base + TIM_CFG_OFFSET); val &= ~(1 << TIM_CFG_MODE_400_SHIFT); val |= (bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT; @@ -439,6 +441,60 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP + +static int bcm_iproc_i2c_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + /* make sure there's no pending interrupt when we go into suspend */ + writel(0, iproc_i2c->base + IE_OFFSET); + readl(iproc_i2c->base + IE_OFFSET); + synchronize_irq(iproc_i2c->irq); + + /* now disable the controller */ + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + + return 0; +} + +static int bcm_iproc_i2c_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + int ret; + u32 val; + + /* + * Power domain could have been shut off completely in system deep + * sleep, so re-initialize the block here + */ + ret = bcm_iproc_i2c_init(iproc_i2c); + if (ret) + return ret; + + /* configure to the desired bus speed */ + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val |= (iproc_i2c->bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT; + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + + return 0; +} + +static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = { + .suspend_late = &bcm_iproc_i2c_suspend, + .resume_early = &bcm_iproc_i2c_resume +}; + +#define BCM_IPROC_I2C_PM_OPS (&bcm_iproc_i2c_pm_ops) +#else +#define BCM_IPROC_I2C_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + static const struct of_device_id bcm_iproc_i2c_of_match[] = { { .compatible = "brcm,iproc-i2c" }, { /* sentinel */ } @@ -449,6 +505,7 @@ static struct platform_driver bcm_iproc_i2c_driver = { .driver = { .name = "bcm-iproc-i2c", .of_match_table = bcm_iproc_i2c_of_match, + .pm = BCM_IPROC_I2C_PM_OPS, }, .probe = bcm_iproc_i2c_probe, .remove = bcm_iproc_i2c_remove, -- cgit v1.2.3 From 93563a6a71bb69dd324fc7354c60fb05f84aae6b Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Jun 2015 18:22:14 +0200 Subject: i2c: at91: fix a race condition when using the DMA controller For TX transactions, the TXCOMP bit in the Status Register is cleared when the first data is written into the Transmit Holding Register. In the lines from at91_do_twi_transfer(): at91_twi_write_data_dma(dev); at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); the TXCOMP interrupt may be enabled before the DMA controller has actually started to write into the THR. In such a case, the TXCOMP bit is still set into the Status Register so the interrupt is triggered immediately. The driver understands that a transaction completion has occurred but this transaction hasn't started yet. Hence the TXCOMP interrupt is no longer enabled by at91_do_twi_transfer() but instead by at91_twi_write_data_dma_callback(). Also, the TXCOMP bit in the Status Register in not a clear on read flag but a snapshot of the transmission state at the time the Status Register is read. When a NACK error is dectected by the I2C controller, the TXCOMP, NACK and TXRDY bits are set together to 1 in the SR. If enabled, the TXCOMP interrupt is triggered at the same time. Also setting the TXRDY to 1 triggers the DMA controller to write the next data into the THR. Such a write resets the TXCOMP bit to 0 in the SR. So depending on when the interrupt handler reads the SR, it may fail to detect the NACK error if it relies on the TXCOMP bit. The NACK bit and its interrupt should be used instead. For RX transactions, the TXCOMP bit in the Status Register is cleared when the START bit is set into the Control Register. However to unify the management of the TXCOMP bit when the DMA controller is used, the TXCOMP interrupt is now enabled by the DMA callbacks for both TX and RX transfers. Signed-off-by: Cyrille Pitchen Cc: stable@vger.kernel.org #3.10 and later Acked-by: Ludovic Desroches Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 70 ++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 17 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index ff23d1bdd230..9bd10a9b4b50 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -65,6 +65,9 @@ #define AT91_TWI_UNRE 0x0080 /* Underrun Error */ #define AT91_TWI_NACK 0x0100 /* Not Acknowledged */ +#define AT91_TWI_INT_MASK \ + (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) + #define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */ #define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */ #define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */ @@ -119,13 +122,12 @@ static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val) static void at91_disable_twi_interrupts(struct at91_twi_dev *dev) { - at91_twi_write(dev, AT91_TWI_IDR, - AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY); + at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK); } static void at91_twi_irq_save(struct at91_twi_dev *dev) { - dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & 0x7; + dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK; at91_disable_twi_interrupts(dev); } @@ -215,6 +217,14 @@ static void at91_twi_write_data_dma_callback(void *data) dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg), dev->buf_len, DMA_TO_DEVICE); + /* + * When this callback is called, THR/TX FIFO is likely not to be empty + * yet. So we have to wait for TXCOMP or NACK bits to be set into the + * Status Register to be sure that the STOP bit has been sent and the + * transfer is completed. The NACK interrupt has already been enabled, + * we just have to enable TXCOMP one. + */ + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); } @@ -309,7 +319,7 @@ static void at91_twi_read_data_dma_callback(void *data) /* The last two bytes have to be read without using dma */ dev->buf += dev->buf_len - 2; dev->buf_len = 2; - at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_RXRDY); + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_RXRDY | AT91_TWI_TXCOMP); } static void at91_twi_read_data_dma(struct at91_twi_dev *dev) @@ -370,7 +380,7 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id) /* catch error flags */ dev->transfer_status |= status; - if (irqstatus & AT91_TWI_TXCOMP) { + if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) { at91_disable_twi_interrupts(dev); complete(&dev->cmd_complete); } @@ -384,6 +394,34 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) unsigned long time_left; bool has_unre_flag = dev->pdata->has_unre_flag; + /* + * WARNING: the TXCOMP bit in the Status Register is NOT a clear on + * read flag but shows the state of the transmission at the time the + * Status Register is read. According to the programmer datasheet, + * TXCOMP is set when both holding register and internal shifter are + * empty and STOP condition has been sent. + * Consequently, we should enable NACK interrupt rather than TXCOMP to + * detect transmission failure. + * + * Besides, the TXCOMP bit is already set before the i2c transaction + * has been started. For read transactions, this bit is cleared when + * writing the START bit into the Control Register. So the + * corresponding interrupt can safely be enabled just after. + * However for write transactions managed by the CPU, we first write + * into THR, so TXCOMP is cleared. Then we can safely enable TXCOMP + * interrupt. If TXCOMP interrupt were enabled before writing into THR, + * the interrupt handler would be called immediately and the i2c command + * would be reported as completed. + * Also when a write transaction is managed by the DMA controller, + * enabling the TXCOMP interrupt in this function may lead to a race + * condition since we don't know whether the TXCOMP interrupt is enabled + * before or after the DMA has started to write into THR. So the TXCOMP + * interrupt is enabled later by at91_twi_write_data_dma_callback(). + * Immediately after in that DMA callback, we still need to send the + * STOP condition manually writing the corresponding bit into the + * Control Register. + */ + dev_dbg(dev->dev, "transfer: %s %d bytes.\n", (dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len); @@ -414,26 +452,24 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) * seems to be the best solution. */ if (dev->use_dma && (dev->buf_len > AT91_I2C_DMA_THRESHOLD)) { + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK); at91_twi_read_data_dma(dev); - /* - * It is important to enable TXCOMP irq here because - * doing it only when transferring the last two bytes - * will mask NACK errors since TXCOMP is set when a - * NACK occurs. - */ - at91_twi_write(dev, AT91_TWI_IER, - AT91_TWI_TXCOMP); - } else + } else { at91_twi_write(dev, AT91_TWI_IER, - AT91_TWI_TXCOMP | AT91_TWI_RXRDY); + AT91_TWI_TXCOMP | + AT91_TWI_NACK | + AT91_TWI_RXRDY); + } } else { if (dev->use_dma && (dev->buf_len > AT91_I2C_DMA_THRESHOLD)) { + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK); at91_twi_write_data_dma(dev); - at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); } else { at91_twi_write_next_byte(dev); at91_twi_write(dev, AT91_TWI_IER, - AT91_TWI_TXCOMP | AT91_TWI_TXRDY); + AT91_TWI_TXCOMP | + AT91_TWI_NACK | + AT91_TWI_TXRDY); } } -- cgit v1.2.3 From e84cf8f03f57c15c0260d88366d064a03b5e383a Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Jun 2015 18:22:15 +0200 Subject: i2c: at91: use BIT() macro to define register bits This patch just fixes typo before applying later patches which will use register bits with index above 16. Signed-off-by: Cyrille Pitchen Acked-by: Ludovic Desroches Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 9bd10a9b4b50..0e88b68bb519 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -41,29 +41,30 @@ /* AT91 TWI register definitions */ #define AT91_TWI_CR 0x0000 /* Control Register */ -#define AT91_TWI_START 0x0001 /* Send a Start Condition */ -#define AT91_TWI_STOP 0x0002 /* Send a Stop Condition */ -#define AT91_TWI_MSEN 0x0004 /* Master Transfer Enable */ -#define AT91_TWI_SVDIS 0x0020 /* Slave Transfer Disable */ -#define AT91_TWI_QUICK 0x0040 /* SMBus quick command */ -#define AT91_TWI_SWRST 0x0080 /* Software Reset */ +#define AT91_TWI_START BIT(0) /* Send a Start Condition */ +#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */ +#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */ +#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */ +#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */ +#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */ +#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */ +#define AT91_TWI_SWRST BIT(7) /* Software Reset */ #define AT91_TWI_MMR 0x0004 /* Master Mode Register */ #define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */ -#define AT91_TWI_MREAD 0x1000 /* Master Read Direction */ +#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */ #define AT91_TWI_IADR 0x000c /* Internal Address Register */ #define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */ #define AT91_TWI_SR 0x0020 /* Status Register */ -#define AT91_TWI_TXCOMP 0x0001 /* Transmission Complete */ -#define AT91_TWI_RXRDY 0x0002 /* Receive Holding Register Ready */ -#define AT91_TWI_TXRDY 0x0004 /* Transmit Holding Register Ready */ - -#define AT91_TWI_OVRE 0x0040 /* Overrun Error */ -#define AT91_TWI_UNRE 0x0080 /* Underrun Error */ -#define AT91_TWI_NACK 0x0100 /* Not Acknowledged */ +#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */ +#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */ +#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */ +#define AT91_TWI_OVRE BIT(6) /* Overrun Error */ +#define AT91_TWI_UNRE BIT(7) /* Underrun Error */ +#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */ #define AT91_TWI_INT_MASK \ (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) -- cgit v1.2.3 From 0ef6f3213dac48bbc71c98b5a2752db556f3e649 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Jun 2015 18:22:17 +0200 Subject: i2c: at91: add support for new alternative command mode The alternative command mode was introduced to simplify the transmission of STOP conditions and to solve timing and latency issues around them. This mode relies on a new register, the Alternative Command Register, which must be set at the same time as the Master Mode Register. This new register was designed to allow simple setup of basic combined transactions built from up to two unitary transactions. Indeed, the ACR is split into two areas, which describe one unitary transaction each. Each area is filled with Data Length 8bit counter, a Direction and a PEC Request bit. The PEC bit is only used in SMBus mode and is not supported by this driver yet. Also when using alternative command mode, the MREAD bit from the Master Mode Register is ignored. Instead the Direction bits from ACR are used to setup the direction, read or write, of each unitary transaction. Finally the 8bit counters must filled with the data length of their respective transaction. Then if only one transaction is to be used, the data length of the second one must be set to zero. At the moment, this driver uses only the first transaction. In addition to MMR and ACR, the Control Register also need to be written to enable the alternative command mode. That's the purpose of its ACMEN bit, which stands for Alternative Command Mode Enable. Note that the alternative command mode is compatible with the use of the Internal Address Register. So combined transactions for eeprom read are actually implemented with the Internal Address Register. This register is written with up to 3 bytes, which are the internal address sent to the slave through the first write transaction. Then the first area of the ACR describe the write transaction to follow, which carries the data to be read from the eeprom. The second area of the ACR is not used so its Data Length 8bit counter is cleared. For each byte sent or received by the device, the Data Length 8bit counter is decremented. When it reaches 0, a STOP condition is automatically sent. Signed-off-by: Cyrille Pitchen Acked-by: Ludovic Desroches Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 121 +++++++++++++++++++++++++++++++++++------- 1 file changed, 101 insertions(+), 20 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 0e88b68bb519..817ae69cd1fb 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -49,6 +49,11 @@ #define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */ #define AT91_TWI_QUICK BIT(6) /* SMBus quick command */ #define AT91_TWI_SWRST BIT(7) /* Software Reset */ +#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */ +#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */ +#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */ +#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */ +#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */ #define AT91_TWI_MMR 0x0004 /* Master Mode Register */ #define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */ @@ -65,6 +70,7 @@ #define AT91_TWI_OVRE BIT(6) /* Overrun Error */ #define AT91_TWI_UNRE BIT(7) /* Underrun Error */ #define AT91_TWI_NACK BIT(8) /* Not Acknowledged */ +#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */ #define AT91_TWI_INT_MASK \ (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) @@ -75,10 +81,15 @@ #define AT91_TWI_RHR 0x0030 /* Receive Holding Register */ #define AT91_TWI_THR 0x0034 /* Transmit Holding Register */ +#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */ +#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff) +#define AT91_TWI_ACR_DIR BIT(8) + struct at91_twi_pdata { unsigned clk_max_div; unsigned clk_offset; bool has_unre_flag; + bool has_alt_cmd; struct at_dma_slave dma_slave; }; @@ -204,7 +215,8 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev) /* send stop when last byte has been written */ if (--dev->buf_len == 0) - at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); + if (!dev->pdata->has_alt_cmd) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); dev_dbg(dev->dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len); @@ -226,7 +238,8 @@ static void at91_twi_write_data_dma_callback(void *data) * we just have to enable TXCOMP one. */ at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); - at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); + if (!dev->pdata->has_alt_cmd) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); } static void at91_twi_write_data_dma(struct at91_twi_dev *dev) @@ -302,7 +315,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev) } /* send stop if second but last byte has been read */ - if (dev->buf_len == 1) + if (!dev->pdata->has_alt_cmd && dev->buf_len == 1) at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP); dev_dbg(dev->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len); @@ -313,14 +326,18 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev) static void at91_twi_read_data_dma_callback(void *data) { struct at91_twi_dev *dev = (struct at91_twi_dev *)data; + unsigned ier = AT91_TWI_TXCOMP; dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg), dev->buf_len, DMA_FROM_DEVICE); - /* The last two bytes have to be read without using dma */ - dev->buf += dev->buf_len - 2; - dev->buf_len = 2; - at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_RXRDY | AT91_TWI_TXCOMP); + if (!dev->pdata->has_alt_cmd) { + /* The last two bytes have to be read without using dma */ + dev->buf += dev->buf_len - 2; + dev->buf_len = 2; + ier |= AT91_TWI_RXRDY; + } + at91_twi_write(dev, AT91_TWI_IER, ier); } static void at91_twi_read_data_dma(struct at91_twi_dev *dev) @@ -329,13 +346,14 @@ static void at91_twi_read_data_dma(struct at91_twi_dev *dev) struct dma_async_tx_descriptor *rxdesc; struct at91_twi_dma *dma = &dev->dma; struct dma_chan *chan_rx = dma->chan_rx; + size_t buf_len; + buf_len = (dev->pdata->has_alt_cmd) ? dev->buf_len : dev->buf_len - 2; dma->direction = DMA_FROM_DEVICE; /* Keep in mind that we won't use dma to read the last two bytes */ at91_twi_irq_save(dev); - dma_addr = dma_map_single(dev->dev, dev->buf, dev->buf_len - 2, - DMA_FROM_DEVICE); + dma_addr = dma_map_single(dev->dev, dev->buf, buf_len, DMA_FROM_DEVICE); if (dma_mapping_error(dev->dev, dma_addr)) { dev_err(dev->dev, "dma map failed\n"); return; @@ -343,7 +361,7 @@ static void at91_twi_read_data_dma(struct at91_twi_dev *dev) dma->buf_mapped = true; at91_twi_irq_restore(dev); dma->sg.dma_address = dma_addr; - sg_dma_len(&dma->sg) = dev->buf_len - 2; + sg_dma_len(&dma->sg) = buf_len; rxdesc = dmaengine_prep_slave_sg(chan_rx, &dma->sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -394,6 +412,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) int ret; unsigned long time_left; bool has_unre_flag = dev->pdata->has_unre_flag; + bool has_alt_cmd = dev->pdata->has_alt_cmd; /* * WARNING: the TXCOMP bit in the Status Register is NOT a clear on @@ -403,6 +422,21 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) * empty and STOP condition has been sent. * Consequently, we should enable NACK interrupt rather than TXCOMP to * detect transmission failure. + * Indeed let's take the case of an i2c write command using DMA. + * Whenever the slave doesn't acknowledge a byte, the LOCK, NACK and + * TXCOMP bits are set together into the Status Register. + * LOCK is a clear on write bit, which is set to prevent the DMA + * controller from sending new data on the i2c bus after a NACK + * condition has happened. Once locked, this i2c peripheral stops + * triggering the DMA controller for new data but it is more than + * likely that a new DMA transaction is already in progress, writing + * into the Transmit Holding Register. Since the peripheral is locked, + * these new data won't be sent to the i2c bus but they will remain + * into the Transmit Holding Register, so TXCOMP bit is cleared. + * Then when the interrupt handler is called, the Status Register is + * read: the TXCOMP bit is clear but NACK bit is still set. The driver + * manage the error properly, without waiting for timeout. + * This case can be reproduced easyly when writing into an at24 eeprom. * * Besides, the TXCOMP bit is already set before the i2c transaction * has been started. For read transactions, this bit is cleared when @@ -418,9 +452,9 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) * condition since we don't know whether the TXCOMP interrupt is enabled * before or after the DMA has started to write into THR. So the TXCOMP * interrupt is enabled later by at91_twi_write_data_dma_callback(). - * Immediately after in that DMA callback, we still need to send the - * STOP condition manually writing the corresponding bit into the - * Control Register. + * Immediately after in that DMA callback, if the alternative command + * mode is not used, we still need to send the STOP condition manually + * writing the corresponding bit into the Control Register. */ dev_dbg(dev->dev, "transfer: %s %d bytes.\n", @@ -441,14 +475,16 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) } /* if only one byte is to be read, immediately stop transfer */ - if (dev->buf_len <= 1 && !(dev->msg->flags & I2C_M_RECV_LEN)) + if (!has_alt_cmd && dev->buf_len <= 1 && + !(dev->msg->flags & I2C_M_RECV_LEN)) start_flags |= AT91_TWI_STOP; at91_twi_write(dev, AT91_TWI_CR, start_flags); /* - * When using dma, the last byte has to be read manually in - * order to not send the stop command too late and then - * to receive extra data. In practice, there are some issues - * if you use the dma to read n-1 bytes because of latency. + * When using dma without alternative command mode, the last + * byte has to be read manually in order to not send the stop + * command too late and then to receive extra data. + * In practice, there are some issues if you use the dma to + * read n-1 bytes because of latency. * Reading n-2 bytes with dma and the two last ones manually * seems to be the best solution. */ @@ -477,6 +513,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) time_left = wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout); if (time_left == 0) { + dev->transfer_status |= at91_twi_read(dev, AT91_TWI_SR); dev_err(dev->dev, "controller timed out\n"); at91_init_twi_bus(dev); ret = -ETIMEDOUT; @@ -497,6 +534,11 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) ret = -EIO; goto error; } + if (has_alt_cmd && (dev->transfer_status & AT91_TWI_LOCK)) { + dev_err(dev->dev, "tx locked\n"); + ret = -EIO; + goto error; + } if (dev->recv_len_abort) { dev_err(dev->dev, "invalid smbus block length recvd\n"); ret = -EPROTO; @@ -508,7 +550,14 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) return 0; error: + /* first stop DMA transfer if still in progress */ at91_twi_dma_cleanup(dev); + /* then flush THR/FIFO and unlock TX if locked */ + if (has_alt_cmd && (dev->transfer_status & AT91_TWI_LOCK)) { + dev_dbg(dev->dev, "unlock tx\n"); + at91_twi_write(dev, AT91_TWI_CR, + AT91_TWI_THRCLR | AT91_TWI_LOCKCLR); + } return ret; } @@ -518,6 +567,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) int ret; unsigned int_addr_flag = 0; struct i2c_msg *m_start = msg; + bool is_read, use_alt_cmd = false; dev_dbg(&adap->dev, "at91_xfer: processing %d messages:\n", num); @@ -540,8 +590,23 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) at91_twi_write(dev, AT91_TWI_IADR, internal_address); } - at91_twi_write(dev, AT91_TWI_MMR, (m_start->addr << 16) | int_addr_flag - | ((m_start->flags & I2C_M_RD) ? AT91_TWI_MREAD : 0)); + is_read = (m_start->flags & I2C_M_RD); + if (dev->pdata->has_alt_cmd) { + if (m_start->len > 0) { + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMEN); + at91_twi_write(dev, AT91_TWI_ACR, + AT91_TWI_ACR_DATAL(m_start->len) | + ((is_read) ? AT91_TWI_ACR_DIR : 0)); + use_alt_cmd = true; + } else { + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_ACMDIS); + } + } + + at91_twi_write(dev, AT91_TWI_MMR, + (m_start->addr << 16) | + int_addr_flag | + ((!use_alt_cmd && is_read) ? AT91_TWI_MREAD : 0)); dev->buf_len = m_start->len; dev->buf = m_start->buf; @@ -582,30 +647,35 @@ static struct at91_twi_pdata at91rm9200_config = { .clk_max_div = 5, .clk_offset = 3, .has_unre_flag = true, + .has_alt_cmd = false, }; static struct at91_twi_pdata at91sam9261_config = { .clk_max_div = 5, .clk_offset = 4, .has_unre_flag = false, + .has_alt_cmd = false, }; static struct at91_twi_pdata at91sam9260_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, + .has_alt_cmd = false, }; static struct at91_twi_pdata at91sam9g20_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, + .has_alt_cmd = false, }; static struct at91_twi_pdata at91sam9g10_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, + .has_alt_cmd = false, }; static const struct platform_device_id at91_twi_devtypes[] = { @@ -634,6 +704,14 @@ static struct at91_twi_pdata at91sam9x5_config = { .clk_max_div = 7, .clk_offset = 4, .has_unre_flag = false, + .has_alt_cmd = false, +}; + +static struct at91_twi_pdata sama5d2_config = { + .clk_max_div = 7, + .clk_offset = 4, + .has_unre_flag = true, + .has_alt_cmd = true, }; static const struct of_device_id atmel_twi_dt_ids[] = { @@ -655,6 +733,9 @@ static const struct of_device_id atmel_twi_dt_ids[] = { }, { .compatible = "atmel,at91sam9x5-i2c", .data = &at91sam9x5_config, + }, { + .compatible = "atmel,sama5d2-i2c", + .data = &sama5d2_config, }, { /* sentinel */ } -- cgit v1.2.3 From 6ce461ea504b0c5eb011b1acccd38fdc28e812ad Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Jun 2015 18:22:18 +0200 Subject: i2c: at91: print hardware version The probe() function now prints the hardware version of the I2C controller. Signed-off-by: Cyrille Pitchen [wsa: s/version/hw version/] for clarity] Acked-by: Ludovic Desroches Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 817ae69cd1fb..abd93f3a05a0 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -85,6 +85,8 @@ #define AT91_TWI_ACR_DATAL(len) ((len) & 0xff) #define AT91_TWI_ACR_DIR BIT(8) +#define AT91_TWI_VER 0x00fc /* Version Register */ + struct at91_twi_pdata { unsigned clk_max_div; unsigned clk_offset; @@ -908,7 +910,8 @@ static int at91_twi_probe(struct platform_device *pdev) return rc; } - dev_info(dev->dev, "AT91 i2c bus driver.\n"); + dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n", + at91_twi_read(dev, AT91_TWI_VER)); return 0; } -- cgit v1.2.3 From 5e3cfc6c4e37ec7040fa235f42586b3312c0ebf2 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Jun 2015 18:22:19 +0200 Subject: i2c: at91: add support to FIFOs When FIFOs are available and enabled, the driver now configures the Atmel eXtended DMA Controller to perform word accesses instead of byte accesses when possible. The actual access width depends on the size of the buffer to transmit. To enable FIFO support the "atmel,fifo-size" property must be set properly in the I2C controller node of the device tree. Signed-off-by: Cyrille Pitchen Acked-by: Ludovic Desroches Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 147 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 130 insertions(+), 17 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index abd93f3a05a0..9e54f97a9020 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -54,6 +54,8 @@ #define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */ #define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */ #define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */ +#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */ +#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */ #define AT91_TWI_MMR 0x0004 /* Master Mode Register */ #define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */ @@ -85,6 +87,22 @@ #define AT91_TWI_ACR_DATAL(len) ((len) & 0xff) #define AT91_TWI_ACR_DIR BIT(8) +#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */ +#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0) +#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0) +#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4) +#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4) +#define AT91_TWI_ONE_DATA 0x0 +#define AT91_TWI_TWO_DATA 0x1 +#define AT91_TWI_FOUR_DATA 0x2 + +#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */ + +#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */ +#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */ +#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */ +#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */ + #define AT91_TWI_VER 0x00fc /* Version Register */ struct at91_twi_pdata { @@ -98,7 +116,7 @@ struct at91_twi_pdata { struct at91_twi_dma { struct dma_chan *chan_rx; struct dma_chan *chan_tx; - struct scatterlist sg; + struct scatterlist sg[2]; struct dma_async_tx_descriptor *data_desc; enum dma_data_direction direction; bool buf_mapped; @@ -121,6 +139,7 @@ struct at91_twi_dev { struct at91_twi_pdata *pdata; bool use_dma; bool recv_len_abort; + u32 fifo_size; struct at91_twi_dma dma; }; @@ -154,6 +173,9 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev) { at91_disable_twi_interrupts(dev); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST); + /* FIFO should be enabled immediately after the software reset */ + if (dev->fifo_size) + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS); at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg); @@ -200,7 +222,7 @@ static void at91_twi_dma_cleanup(struct at91_twi_dev *dev) dma->xfer_in_progress = false; } if (dma->buf_mapped) { - dma_unmap_single(dev->dev, sg_dma_address(&dma->sg), + dma_unmap_single(dev->dev, sg_dma_address(&dma->sg[0]), dev->buf_len, dma->direction); dma->buf_mapped = false; } @@ -213,7 +235,8 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev) if (dev->buf_len <= 0) return; - at91_twi_write(dev, AT91_TWI_THR, *dev->buf); + /* 8bit write works with and without FIFO */ + writeb_relaxed(*dev->buf, dev->base + AT91_TWI_THR); /* send stop when last byte has been written */ if (--dev->buf_len == 0) @@ -229,7 +252,7 @@ static void at91_twi_write_data_dma_callback(void *data) { struct at91_twi_dev *dev = (struct at91_twi_dev *)data; - dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg), + dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]), dev->buf_len, DMA_TO_DEVICE); /* @@ -250,6 +273,7 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev) struct dma_async_tx_descriptor *txdesc; struct at91_twi_dma *dma = &dev->dma; struct dma_chan *chan_tx = dma->chan_tx; + unsigned int sg_len = 1; if (dev->buf_len <= 0) return; @@ -265,10 +289,43 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev) } dma->buf_mapped = true; at91_twi_irq_restore(dev); - sg_dma_len(&dma->sg) = dev->buf_len; - sg_dma_address(&dma->sg) = dma_addr; - txdesc = dmaengine_prep_slave_sg(chan_tx, &dma->sg, 1, DMA_MEM_TO_DEV, + if (dev->fifo_size) { + size_t part1_len, part2_len; + struct scatterlist *sg; + unsigned fifo_mr; + + sg_len = 0; + + part1_len = dev->buf_len & ~0x3; + if (part1_len) { + sg = &dma->sg[sg_len++]; + sg_dma_len(sg) = part1_len; + sg_dma_address(sg) = dma_addr; + } + + part2_len = dev->buf_len & 0x3; + if (part2_len) { + sg = &dma->sg[sg_len++]; + sg_dma_len(sg) = part2_len; + sg_dma_address(sg) = dma_addr + part1_len; + } + + /* + * DMA controller is triggered when at least 4 data can be + * written into the TX FIFO + */ + fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + fifo_mr &= ~AT91_TWI_FMR_TXRDYM_MASK; + fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_FOUR_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + } else { + sg_dma_len(&dma->sg[0]) = dev->buf_len; + sg_dma_address(&dma->sg[0]) = dma_addr; + } + + txdesc = dmaengine_prep_slave_sg(chan_tx, dma->sg, sg_len, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) { dev_err(dev->dev, "dma prep slave sg failed\n"); @@ -293,7 +350,8 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev) if (dev->buf_len <= 0) return; - *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff; + /* 8bit read works with and without FIFO */ + *dev->buf = readb_relaxed(dev->base + AT91_TWI_RHR); --dev->buf_len; /* return if aborting, we only needed to read RHR to clear RXRDY*/ @@ -330,7 +388,7 @@ static void at91_twi_read_data_dma_callback(void *data) struct at91_twi_dev *dev = (struct at91_twi_dev *)data; unsigned ier = AT91_TWI_TXCOMP; - dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg), + dma_unmap_single(dev->dev, sg_dma_address(&dev->dma.sg[0]), dev->buf_len, DMA_FROM_DEVICE); if (!dev->pdata->has_alt_cmd) { @@ -362,10 +420,24 @@ static void at91_twi_read_data_dma(struct at91_twi_dev *dev) } dma->buf_mapped = true; at91_twi_irq_restore(dev); - dma->sg.dma_address = dma_addr; - sg_dma_len(&dma->sg) = buf_len; - rxdesc = dmaengine_prep_slave_sg(chan_rx, &dma->sg, 1, DMA_DEV_TO_MEM, + if (dev->fifo_size && IS_ALIGNED(buf_len, 4)) { + unsigned fifo_mr; + + /* + * DMA controller is triggered when at least 4 data can be + * read from the RX FIFO + */ + fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + fifo_mr &= ~AT91_TWI_FMR_RXRDYM_MASK; + fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_FOUR_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + } + + sg_dma_len(&dma->sg[0]) = buf_len; + sg_dma_address(&dma->sg[0]) = dma_addr; + + rxdesc = dmaengine_prep_slave_sg(chan_rx, dma->sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) { dev_err(dev->dev, "dma prep slave sg failed\n"); @@ -465,6 +537,21 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) reinit_completion(&dev->cmd_complete); dev->transfer_status = 0; + if (dev->fifo_size) { + unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); + + /* Reset FIFO mode register */ + fifo_mr &= ~(AT91_TWI_FMR_TXRDYM_MASK | + AT91_TWI_FMR_RXRDYM_MASK); + fifo_mr |= AT91_TWI_FMR_TXRDYM(AT91_TWI_ONE_DATA); + fifo_mr |= AT91_TWI_FMR_RXRDYM(AT91_TWI_ONE_DATA); + at91_twi_write(dev, AT91_TWI_FMR, fifo_mr); + + /* Flush FIFOs */ + at91_twi_write(dev, AT91_TWI_CR, + AT91_TWI_THRCLR | AT91_TWI_RHRCLR); + } + if (!dev->buf_len) { at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_QUICK); at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); @@ -536,7 +623,8 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) ret = -EIO; goto error; } - if (has_alt_cmd && (dev->transfer_status & AT91_TWI_LOCK)) { + if ((has_alt_cmd || dev->fifo_size) && + (dev->transfer_status & AT91_TWI_LOCK)) { dev_err(dev->dev, "tx locked\n"); ret = -EIO; goto error; @@ -555,7 +643,8 @@ error: /* first stop DMA transfer if still in progress */ at91_twi_dma_cleanup(dev); /* then flush THR/FIFO and unlock TX if locked */ - if (has_alt_cmd && (dev->transfer_status & AT91_TWI_LOCK)) { + if ((has_alt_cmd || dev->fifo_size) && + (dev->transfer_status & AT91_TWI_LOCK)) { dev_dbg(dev->dev, "unlock tx\n"); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_THRCLR | AT91_TWI_LOCKCLR); @@ -750,13 +839,32 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) int ret = 0; struct dma_slave_config slave_config; struct at91_twi_dma *dma = &dev->dma; + enum dma_slave_buswidth addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + + /* + * The actual width of the access will be chosen in + * dmaengine_prep_slave_sg(): + * for each buffer in the scatter-gather list, if its size is aligned + * to addr_width then addr_width accesses will be performed to transfer + * the buffer. On the other hand, if the buffer size is not aligned to + * addr_width then the buffer is transferred using single byte accesses. + * Please refer to the Atmel eXtended DMA controller driver. + * When FIFOs are used, the TXRDYM threshold can always be set to + * trigger the XDMAC when at least 4 data can be written into the TX + * FIFO, even if single byte accesses are performed. + * However the RXRDYM threshold must be set to fit the access width, + * deduced from buffer length, so the XDMAC is triggered properly to + * read data from the RX FIFO. + */ + if (dev->fifo_size) + addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; memset(&slave_config, 0, sizeof(slave_config)); slave_config.src_addr = (dma_addr_t)phy_addr + AT91_TWI_RHR; - slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.src_addr_width = addr_width; slave_config.src_maxburst = 1; slave_config.dst_addr = (dma_addr_t)phy_addr + AT91_TWI_THR; - slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.dst_addr_width = addr_width; slave_config.dst_maxburst = 1; slave_config.device_fc = false; @@ -788,7 +896,7 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) goto error; } - sg_init_table(&dma->sg, 1); + sg_init_table(dma->sg, 2); dma->buf_mapped = false; dma->xfer_in_progress = false; dev->use_dma = true; @@ -874,6 +982,11 @@ static int at91_twi_probe(struct platform_device *pdev) return rc; } + if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", + &dev->fifo_size)) { + dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size); + } + rc = of_property_read_u32(dev->dev->of_node, "clock-frequency", &bus_clk_rate); if (rc) -- cgit v1.2.3 From dd1aa2524bc55345ede0b13853c54e1b0be22b9f Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Tue, 9 Jun 2015 15:36:20 -0400 Subject: i2c: brcmstb: Add Broadcom settop SoC i2c controller driver Adding support for i2c controller driver for Broadcom settop SoCs. Signed-off-by: Kamal Dasu [wsa: removed superfluous owner in platform_driver] Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-brcmstb.txt | 28 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-brcmstb.c | 694 +++++++++++++++++++++ 4 files changed, 733 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt create mode 100644 drivers/i2c/busses/i2c-brcmstb.c (limited to 'drivers/i2c') diff --git a/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt b/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt new file mode 100644 index 000000000000..d6f724efdcf2 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt @@ -0,0 +1,28 @@ +Broadcom stb bsc iic master controller + +Required properties: + +- compatible: should be "brcm,brcmstb-i2c" +- clock-frequency: 32-bit decimal value of iic master clock freqency in Hz + valid values are 375000, 390000, 187500, 200000 + 93750, 97500, 46875 and 50000 +- reg: specifies the base physical address and size of the registers + +Optional properties : + +- interrupt-parent: specifies the phandle to the parent interrupt controller + this one is cascaded from +- interrupts: specifies the interrupt number, the irq line to be used +- interrupt-names: Interrupt name string + +Example: + +bsca: i2c@f0406200 { + clock-frequency = <390000>; + compatible = "brcm,brcmstb-i2c"; + interrupt-parent = <&irq0_intc>; + reg = <0xf0406200 0x58>; + interrupts = <0x18>; + interrupt-names = "upg_bsca"; +}; + diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8a639dce5173..b8c3d6b53861 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -392,6 +392,16 @@ config I2C_BCM_KONA If you do not need KONA I2C interface, say N. +config I2C_BRCMSTB + tristate "BRCM Settop I2C controller" + depends on ARCH_BRCMSTB || COMPILE_TEST + default y + help + If you say yes to this option, support will be included for the + I2C interface on the Broadcom Settop SoCs. + + If you do not need I2C interface, say N. + config I2C_BLACKFIN_TWI tristate "Blackfin TWI I2C support" depends on BLACKFIN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 65995423c680..e5f537c80da0 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o # Other I2C/SMBus bus drivers obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o +obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_OPAL) += i2c-opal.o diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c new file mode 100644 index 000000000000..8e9637eea512 --- /dev/null +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2014 Broadcom 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define N_DATA_REGS 8 +#define N_DATA_BYTES (N_DATA_REGS * 4) + +/* BSC count register field definitions */ +#define BSC_CNT_REG1_MASK 0x0000003f +#define BSC_CNT_REG1_SHIFT 0 +#define BSC_CNT_REG2_MASK 0x00000fc0 +#define BSC_CNT_REG2_SHIFT 6 + +/* BSC CTL register field definitions */ +#define BSC_CTL_REG_DTF_MASK 0x00000003 +#define BSC_CTL_REG_SCL_SEL_MASK 0x00000030 +#define BSC_CTL_REG_SCL_SEL_SHIFT 4 +#define BSC_CTL_REG_INT_EN_MASK 0x00000040 +#define BSC_CTL_REG_INT_EN_SHIFT 6 +#define BSC_CTL_REG_DIV_CLK_MASK 0x00000080 + +/* BSC_IIC_ENABLE r/w enable and interrupt field defintions */ +#define BSC_IIC_EN_RESTART_MASK 0x00000040 +#define BSC_IIC_EN_NOSTART_MASK 0x00000020 +#define BSC_IIC_EN_NOSTOP_MASK 0x00000010 +#define BSC_IIC_EN_NOACK_MASK 0x00000004 +#define BSC_IIC_EN_INTRP_MASK 0x00000002 +#define BSC_IIC_EN_ENABLE_MASK 0x00000001 + +/* BSC_CTLHI control register field definitions */ +#define BSC_CTLHI_REG_INPUT_SWITCHING_LEVEL_MASK 0x00000080 +#define BSC_CTLHI_REG_DATAREG_SIZE_MASK 0x00000040 +#define BSC_CTLHI_REG_IGNORE_ACK_MASK 0x00000002 +#define BSC_CTLHI_REG_WAIT_DIS_MASK 0x00000001 + +#define I2C_TIMEOUT 100 /* msecs */ + +/* Condition mask used for non combined transfer */ +#define COND_RESTART BSC_IIC_EN_RESTART_MASK +#define COND_NOSTART BSC_IIC_EN_NOSTART_MASK +#define COND_NOSTOP BSC_IIC_EN_NOSTOP_MASK +#define COND_START_STOP (COND_RESTART | COND_NOSTART | COND_NOSTOP) + +/* BSC data transfer direction */ +#define DTF_WR_MASK 0x00000000 +#define DTF_RD_MASK 0x00000001 +/* BSC data transfer direction combined format */ +#define DTF_RD_WR_MASK 0x00000002 +#define DTF_WR_RD_MASK 0x00000003 + +#define INT_ENABLE true +#define INT_DISABLE false + +/* BSC block register map structure to cache fields to be written */ +struct bsc_regs { + u32 chip_address; /* slave address */ + u32 data_in[N_DATA_REGS]; /* tx data buffer*/ + u32 cnt_reg; /* rx/tx data length */ + u32 ctl_reg; /* control register */ + u32 iic_enable; /* xfer enable and status */ + u32 data_out[N_DATA_REGS]; /* rx data buffer */ + u32 ctlhi_reg; /* more control fields */ + u32 scl_param; /* reserved */ +}; + +struct bsc_clk_param { + u32 hz; + u32 scl_mask; + u32 div_mask; +}; + +enum bsc_xfer_cmd { + CMD_WR, + CMD_RD, + CMD_WR_NOACK, + CMD_RD_NOACK, +}; + +static char const *cmd_string[] = { + [CMD_WR] = "WR", + [CMD_RD] = "RD", + [CMD_WR_NOACK] = "WR NOACK", + [CMD_RD_NOACK] = "RD NOACK", +}; + +enum bus_speeds { + SPD_375K, + SPD_390K, + SPD_187K, + SPD_200K, + SPD_93K, + SPD_97K, + SPD_46K, + SPD_50K +}; + +static const struct bsc_clk_param bsc_clk[] = { + [SPD_375K] = { + .hz = 375000, + .scl_mask = SPD_375K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_390K] = { + .hz = 390000, + .scl_mask = SPD_390K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_187K] = { + .hz = 187500, + .scl_mask = SPD_187K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_200K] = { + .hz = 200000, + .scl_mask = SPD_200K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = 0 + }, + [SPD_93K] = { + .hz = 93750, + .scl_mask = SPD_375K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + }, + [SPD_97K] = { + .hz = 97500, + .scl_mask = SPD_390K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + }, + [SPD_46K] = { + .hz = 46875, + .scl_mask = SPD_187K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + }, + [SPD_50K] = { + .hz = 50000, + .scl_mask = SPD_200K << BSC_CTL_REG_SCL_SEL_SHIFT, + .div_mask = BSC_CTL_REG_DIV_CLK_MASK + } +}; + +struct brcmstb_i2c_dev { + struct device *device; + void __iomem *base; + void __iomem *irq_base; + int irq; + struct bsc_regs *bsc_regmap; + struct i2c_adapter adapter; + struct completion done; + bool is_suspended; + u32 clk_freq_hz; +}; + +/* register accessors for both be and le cpu arch */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define __bsc_readl(_reg) ioread32be(_reg) +#define __bsc_writel(_val, _reg) iowrite32be(_val, _reg) +#else +#define __bsc_readl(_reg) ioread32(_reg) +#define __bsc_writel(_val, _reg) iowrite32(_val, _reg) +#endif + +#define bsc_readl(_dev, _reg) \ + __bsc_readl(_dev->base + offsetof(struct bsc_regs, _reg)) + +#define bsc_writel(_dev, _val, _reg) \ + __bsc_writel(_val, _dev->base + offsetof(struct bsc_regs, _reg)) + +static void brcmstb_i2c_enable_disable_irq(struct brcmstb_i2c_dev *dev, + bool int_en) +{ + + if (int_en) + /* Enable BSC CTL interrupt line */ + dev->bsc_regmap->ctl_reg |= BSC_CTL_REG_INT_EN_MASK; + else + /* Disable BSC CTL interrupt line */ + dev->bsc_regmap->ctl_reg &= ~BSC_CTL_REG_INT_EN_MASK; + + barrier(); + bsc_writel(dev, dev->bsc_regmap->ctl_reg, ctl_reg); +} + +static irqreturn_t brcmstb_i2c_isr(int irq, void *devid) +{ + struct brcmstb_i2c_dev *dev = devid; + u32 status_bsc_ctl = bsc_readl(dev, ctl_reg); + u32 status_iic_intrp = bsc_readl(dev, iic_enable); + + dev_dbg(dev->device, "isr CTL_REG %x IIC_EN %x\n", + status_bsc_ctl, status_iic_intrp); + + if (!(status_bsc_ctl & BSC_CTL_REG_INT_EN_MASK)) + return IRQ_NONE; + + brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE); + complete_all(&dev->done); + + dev_dbg(dev->device, "isr handled"); + return IRQ_HANDLED; +} + +/* Wait for device to be ready */ +static int brcmstb_i2c_wait_if_busy(struct brcmstb_i2c_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT); + + while ((bsc_readl(dev, iic_enable) & BSC_IIC_EN_INTRP_MASK)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + return 0; +} + +/* i2c xfer completion function, handles both irq and polling mode */ +static int brcmstb_i2c_wait_for_completion(struct brcmstb_i2c_dev *dev) +{ + int ret = 0; + unsigned long timeout = msecs_to_jiffies(I2C_TIMEOUT); + + if (dev->irq >= 0) { + if (!wait_for_completion_timeout(&dev->done, timeout)) + ret = -ETIMEDOUT; + } else { + /* we are in polling mode */ + u32 bsc_intrp; + unsigned long time_left = jiffies + timeout; + + do { + bsc_intrp = bsc_readl(dev, iic_enable) & + BSC_IIC_EN_INTRP_MASK; + if (time_after(jiffies, time_left)) { + ret = -ETIMEDOUT; + break; + } + cpu_relax(); + } while (!bsc_intrp); + } + + if (dev->irq < 0 || ret == -ETIMEDOUT) + brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE); + + return ret; +} + +/* Set xfer START/STOP conditions for subsequent transfer */ +static void brcmstb_set_i2c_start_stop(struct brcmstb_i2c_dev *dev, + u32 cond_flag) +{ + u32 regval = dev->bsc_regmap->iic_enable; + + dev->bsc_regmap->iic_enable = (regval & ~COND_START_STOP) | cond_flag; +} + +/* Send I2C request check completion */ +static int brcmstb_send_i2c_cmd(struct brcmstb_i2c_dev *dev, + enum bsc_xfer_cmd cmd) +{ + int rc = 0; + struct bsc_regs *pi2creg = dev->bsc_regmap; + + /* Make sure the hardware is ready */ + rc = brcmstb_i2c_wait_if_busy(dev); + if (rc < 0) + return rc; + + /* only if we are in interrupt mode */ + if (dev->irq >= 0) + reinit_completion(&dev->done); + + /* enable BSC CTL interrupt line */ + brcmstb_i2c_enable_disable_irq(dev, INT_ENABLE); + + /* initiate transfer by setting iic_enable */ + pi2creg->iic_enable |= BSC_IIC_EN_ENABLE_MASK; + bsc_writel(dev, pi2creg->iic_enable, iic_enable); + + /* Wait for transaction to finish or timeout */ + rc = brcmstb_i2c_wait_for_completion(dev); + if (rc) { + dev_dbg(dev->device, "intr timeout for cmd %s\n", + cmd_string[cmd]); + goto cmd_out; + } + + if ((CMD_RD || CMD_WR) && + bsc_readl(dev, iic_enable) & BSC_IIC_EN_NOACK_MASK) { + rc = -EREMOTEIO; + dev_dbg(dev->device, "controller received NOACK intr for %s\n", + cmd_string[cmd]); + } + +cmd_out: + bsc_writel(dev, 0, cnt_reg); + bsc_writel(dev, 0, iic_enable); + + return rc; +} + +/* Actual data transfer through the BSC master */ +static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev, + u8 *buf, unsigned int len, + struct i2c_msg *pmsg) +{ + int cnt, byte, rc; + enum bsc_xfer_cmd cmd; + u32 ctl_reg; + struct bsc_regs *pi2creg = dev->bsc_regmap; + int no_ack = pmsg->flags & I2C_M_IGNORE_NAK; + + /* see if the transaction needs to check NACK conditions */ + if (no_ack || len <= N_DATA_BYTES) { + cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD_NOACK + : CMD_WR_NOACK; + pi2creg->ctlhi_reg |= BSC_CTLHI_REG_IGNORE_ACK_MASK; + } else { + cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD : CMD_WR; + pi2creg->ctlhi_reg &= ~BSC_CTLHI_REG_IGNORE_ACK_MASK; + } + bsc_writel(dev, pi2creg->ctlhi_reg, ctlhi_reg); + + /* set data transfer direction */ + ctl_reg = pi2creg->ctl_reg & ~BSC_CTL_REG_DTF_MASK; + if (cmd == CMD_WR || cmd == CMD_WR_NOACK) + pi2creg->ctl_reg = ctl_reg | DTF_WR_MASK; + else + pi2creg->ctl_reg = ctl_reg | DTF_RD_MASK; + + /* set the read/write length */ + bsc_writel(dev, BSC_CNT_REG1_MASK & (len << BSC_CNT_REG1_SHIFT), + cnt_reg); + + /* Write data into data_in register */ + if (cmd == CMD_WR || cmd == CMD_WR_NOACK) { + for (cnt = 0; cnt < len; cnt += 4) { + u32 word = 0; + + for (byte = 0; byte < 4; byte++) { + word >>= 8; + if ((cnt + byte) < len) + word |= buf[cnt + byte] << 24; + } + bsc_writel(dev, word, data_in[cnt >> 2]); + } + } + + /* Initiate xfer, the function will return on completion */ + rc = brcmstb_send_i2c_cmd(dev, cmd); + + if (rc != 0) { + dev_dbg(dev->device, "%s failure", cmd_string[cmd]); + return rc; + } + + if (cmd == CMD_RD || cmd == CMD_RD_NOACK) { + for (cnt = 0; cnt < len; cnt += 4) { + u32 data = bsc_readl(dev, data_out[cnt >> 2]); + + for (byte = 0; byte < 4 && + (byte + cnt) < len; byte++) { + buf[cnt + byte] = data & 0xff; + data >>= 8; + } + } + } + + return 0; +} + +/* Write a single byte of data to the i2c bus */ +static int brcmstb_i2c_write_data_byte(struct brcmstb_i2c_dev *dev, + u8 *buf, unsigned int nak_expected) +{ + enum bsc_xfer_cmd cmd = nak_expected ? CMD_WR : CMD_WR_NOACK; + + bsc_writel(dev, 1, cnt_reg); + bsc_writel(dev, *buf, data_in); + + return brcmstb_send_i2c_cmd(dev, cmd); +} + +/* Send i2c address */ +static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned char addr; + + if (msg->flags & I2C_M_TEN) { + /* First byte is 11110XX0 where XX is upper 2 bits */ + addr = 0xF0 | ((msg->addr & 0x300) >> 7); + bsc_writel(dev, addr, chip_address); + + /* Second byte is the remaining 8 bits */ + addr = msg->addr & 0xFF; + if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0) + return -EREMOTEIO; + + if (msg->flags & I2C_M_RD) { + /* For read, send restart without stop condition */ + brcmstb_set_i2c_start_stop(dev, COND_RESTART + | COND_NOSTOP); + /* Then re-send the first byte with the read bit set */ + addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01; + if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0) + return -EREMOTEIO; + + } + } else { + addr = msg->addr << 1; + if (msg->flags & I2C_M_RD) + addr |= 1; + + bsc_writel(dev, addr, chip_address); + } + + return 0; +} + +/* Master transfer function */ +static int brcmstb_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct brcmstb_i2c_dev *dev = i2c_get_adapdata(adapter); + struct i2c_msg *pmsg; + int rc = 0; + int i; + int bytes_to_xfer; + u8 *tmp_buf; + int len = 0; + + if (dev->is_suspended) + return -EBUSY; + + /* Loop through all messages */ + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + len = pmsg->len; + tmp_buf = pmsg->buf; + + dev_dbg(dev->device, + "msg# %d/%d flg %x buf %x len %d\n", i, + num - 1, pmsg->flags, + pmsg->buf ? pmsg->buf[0] : '0', pmsg->len); + + if (i < (num - 1) && (msgs[i + 1].flags & I2C_M_NOSTART)) + brcmstb_set_i2c_start_stop(dev, ~(COND_START_STOP)); + else + brcmstb_set_i2c_start_stop(dev, + COND_RESTART | COND_NOSTOP); + + /* Send slave address */ + if (!(pmsg->flags & I2C_M_NOSTART)) { + rc = brcmstb_i2c_do_addr(dev, pmsg); + if (rc < 0) { + dev_dbg(dev->device, + "NACK for addr %2.2x msg#%d rc = %d\n", + pmsg->addr, i, rc); + goto out; + } + } + + /* Perform data transfer */ + while (len) { + bytes_to_xfer = min(len, N_DATA_BYTES); + + if (len <= N_DATA_BYTES && i == (num - 1)) + brcmstb_set_i2c_start_stop(dev, + ~(COND_START_STOP)); + + rc = brcmstb_i2c_xfer_bsc_data(dev, tmp_buf, + bytes_to_xfer, pmsg); + if (rc < 0) + goto out; + + len -= bytes_to_xfer; + tmp_buf += bytes_to_xfer; + } + } + + rc = num; +out: + return rc; + +} + +static u32 brcmstb_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR + | I2C_FUNC_NOSTART | I2C_FUNC_PROTOCOL_MANGLING; +} + +static const struct i2c_algorithm brcmstb_i2c_algo = { + .master_xfer = brcmstb_i2c_xfer, + .functionality = brcmstb_i2c_functionality, +}; + +static void brcmstb_i2c_set_bus_speed(struct brcmstb_i2c_dev *dev) +{ + int i = 0, num_speeds = ARRAY_SIZE(bsc_clk); + u32 clk_freq_hz = dev->clk_freq_hz; + + for (i = 0; i < num_speeds; i++) { + if (bsc_clk[i].hz == clk_freq_hz) { + dev->bsc_regmap->ctl_reg &= ~(BSC_CTL_REG_SCL_SEL_MASK + | BSC_CTL_REG_DIV_CLK_MASK); + dev->bsc_regmap->ctl_reg |= (bsc_clk[i].scl_mask | + bsc_clk[i].div_mask); + bsc_writel(dev, dev->bsc_regmap->ctl_reg, ctl_reg); + break; + } + } + + /* in case we did not get find a valid speed */ + if (i == num_speeds) { + i = (bsc_readl(dev, ctl_reg) & BSC_CTL_REG_SCL_SEL_MASK) >> + BSC_CTL_REG_SCL_SEL_SHIFT; + dev_warn(dev->device, "leaving current clock-frequency @ %dHz\n", + bsc_clk[i].hz); + } +} + +static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev) +{ + /* 4 byte data register */ + dev->bsc_regmap->ctlhi_reg = BSC_CTLHI_REG_DATAREG_SIZE_MASK; + bsc_writel(dev, dev->bsc_regmap->ctlhi_reg, ctlhi_reg); + /* set bus speed */ + brcmstb_i2c_set_bus_speed(dev); +} + +static int brcmstb_i2c_probe(struct platform_device *pdev) +{ + int rc = 0; + struct brcmstb_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *iomem; + const char *int_name; + + /* Allocate memory for private data structure */ + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->bsc_regmap = devm_kzalloc(&pdev->dev, sizeof(struct bsc_regs *), + GFP_KERNEL); + if (!dev->bsc_regmap) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + dev->device = &pdev->dev; + init_completion(&dev->done); + + /* Map hardware registers */ + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->base = devm_ioremap_resource(dev->device, iomem); + if (IS_ERR(dev->base)) { + rc = -ENOMEM; + goto probe_errorout; + } + + rc = of_property_read_string(dev->device->of_node, "interrupt-names", + &int_name); + if (rc < 0) + int_name = NULL; + + /* Get the interrupt number */ + dev->irq = platform_get_irq(pdev, 0); + + /* disable the bsc interrupt line */ + brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE); + + /* register the ISR handler */ + rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr, + IRQF_SHARED, + int_name ? int_name : pdev->name, + dev); + + if (rc) { + dev_dbg(dev->device, "falling back to polling mode"); + dev->irq = -1; + } + + if (of_property_read_u32(dev->device->of_node, + "clock-frequency", &dev->clk_freq_hz)) { + dev_warn(dev->device, "setting clock-frequency@%dHz\n", + bsc_clk[0].hz); + dev->clk_freq_hz = bsc_clk[0].hz; + } + + brcmstb_i2c_set_bsc_reg_defaults(dev); + + /* Add the i2c adapter */ + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + strlcpy(adap->name, "Broadcom STB : ", sizeof(adap->name)); + if (int_name) + strlcat(adap->name, int_name, sizeof(adap->name)); + adap->algo = &brcmstb_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + rc = i2c_add_adapter(adap); + if (rc) { + dev_err(dev->device, "failed to add adapter\n"); + goto probe_errorout; + } + + dev_info(dev->device, "%s@%dhz registered in %s mode\n", + int_name ? int_name : " ", dev->clk_freq_hz, + (dev->irq >= 0) ? "interrupt" : "polling"); + + return 0; + +probe_errorout: + return rc; +} + +static int brcmstb_i2c_remove(struct platform_device *pdev) +{ + struct brcmstb_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int brcmstb_i2c_suspend(struct device *dev) +{ + struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + i2c_lock_adapter(&i2c_dev->adapter); + i2c_dev->is_suspended = true; + i2c_unlock_adapter(&i2c_dev->adapter); + + return 0; +} + +static int brcmstb_i2c_resume(struct device *dev) +{ + struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); + + i2c_lock_adapter(&i2c_dev->adapter); + brcmstb_i2c_set_bsc_reg_defaults(i2c_dev); + i2c_dev->is_suspended = false; + i2c_unlock_adapter(&i2c_dev->adapter); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend, + brcmstb_i2c_resume); + +static const struct of_device_id brcmstb_i2c_of_match[] = { + {.compatible = "brcm,brcmstb-i2c"}, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match); + +static struct platform_driver brcmstb_i2c_driver = { + .driver = { + .name = "brcmstb-i2c", + .of_match_table = brcmstb_i2c_of_match, + .pm = &brcmstb_i2c_pm, + }, + .probe = brcmstb_i2c_probe, + .remove = brcmstb_i2c_remove, +}; +module_platform_driver(brcmstb_i2c_driver); + +MODULE_AUTHOR("Kamal Dasu "); +MODULE_DESCRIPTION("Broadcom Settop I2C Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 72a956c556b68aa2a9c2c64f5a7b9e50f7bf67e1 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Tue, 9 Jun 2015 12:58:29 +0200 Subject: i2c: davinci: Refactor i2c_davinci_wait_bus_not_busy() There are several problems in the function: - "to_cnt" variable does nothing - schedule_timeout() call without setting current state does nothing - "allow_sleep" parameter is not really used Refactor the function so that it really tries to wait. In case of timeout try to recover the bus. Signed-off-by: Alexander Sverdlin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-davinci.c | 42 ++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 4788a32afb86..ab341aad9038 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -350,29 +350,25 @@ static struct i2c_bus_recovery_info davinci_i2c_scl_recovery_info = { /* * Waiting for bus not busy */ -static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev, - char allow_sleep) +static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev) { - unsigned long timeout; - static u16 to_cnt; - - timeout = jiffies + dev->adapter.timeout; - while (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) - & DAVINCI_I2C_STR_BB) { - if (to_cnt <= DAVINCI_I2C_MAX_TRIES) { - if (time_after(jiffies, timeout)) { - dev_warn(dev->dev, - "timeout waiting for bus ready\n"); - to_cnt++; - return -ETIMEDOUT; - } else { - to_cnt = 0; - i2c_recover_bus(&dev->adapter); - } - } - if (allow_sleep) - schedule_timeout(1); - } + unsigned long timeout = jiffies + dev->adapter.timeout; + + do { + if (!(davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) & DAVINCI_I2C_STR_BB)) + return 0; + schedule_timeout_uninterruptible(1); + } while (time_before_eq(jiffies, timeout)); + + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + i2c_recover_bus(&dev->adapter); + + /* + * if bus is still "busy" here, it's most probably a HW problem like + * short-circuit + */ + if (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) & DAVINCI_I2C_STR_BB) + return -EIO; return 0; } @@ -505,7 +501,7 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); - ret = i2c_davinci_wait_bus_not_busy(dev, 1); + ret = i2c_davinci_wait_bus_not_busy(dev); if (ret < 0) { dev_warn(dev->dev, "timeout waiting for bus ready\n"); return ret; -- cgit v1.2.3 From 41c8d4520461b5134260f3fb9bb56c8455bad610 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Tue, 9 Jun 2015 13:10:10 +0200 Subject: i2c: davinci: Avoid sending to own address Sending a message to own address locks the controller up in very bizarre state, it behaves as slave even if MDR register clearly states master. The controller remains in this state until reset. To avoid unnecessary timeouts simply avoid sending to own address. The controller cannot do this any way. Also, do not enable AAS IRQ, as the slave mode is not supported by the driver and the only possibility to trigger this IRQ is to send to own address. Signed-off-by: Alexander Sverdlin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-davinci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index ab341aad9038..3d3ae52b6916 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -41,8 +41,8 @@ #define DAVINCI_I2C_TIMEOUT (1*HZ) #define DAVINCI_I2C_MAX_TRIES 2 -#define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_AAS | \ - DAVINCI_I2C_IMR_SCD | \ +#define DAVINCI_I2C_OWN_ADDRESS 0x08 +#define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_SCD | \ DAVINCI_I2C_IMR_ARDY | \ DAVINCI_I2C_IMR_NACK | \ DAVINCI_I2C_IMR_AL) @@ -233,7 +233,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev) /* Respond at reserved "SMBus Host" slave address" (and zero); * we seem to have no option to not respond... */ - davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, 0x08); + davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, DAVINCI_I2C_OWN_ADDRESS); dev_dbg(dev->dev, "PSC = %d\n", davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG)); @@ -386,6 +386,11 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) u16 w; unsigned long time_left; + if (msg->addr == DAVINCI_I2C_OWN_ADDRESS) { + dev_warn(dev->dev, "transfer to own address aborted\n"); + return -EADDRNOTAVAIL; + } + /* Introduce a delay, required for some boards (e.g Davinci EVM) */ if (pdata->bus_delay) udelay(pdata->bus_delay); -- cgit v1.2.3 From d8453403948cbe2bcfce810d82e1afcc501c3c71 Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Tue, 2 Jun 2015 16:43:09 -0700 Subject: i2c: busses: xgene-slimpro: fix incorrect __init declation for probe Fix section mismatch error during kernel build for xgene_slimpro_i2c_probe function. It was incorrectly defined with __init declaration. Signed-off-by: Feng Kan Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-xgene-slimpro.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index 0b33aa2e6730..dcca7076231e 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -377,7 +377,7 @@ static struct i2c_algorithm xgene_slimpro_i2c_algorithm = { .functionality = xgene_slimpro_i2c_func, }; -static int __init xgene_slimpro_i2c_probe(struct platform_device *pdev) +static int xgene_slimpro_i2c_probe(struct platform_device *pdev) { struct slimpro_i2c_dev *ctx; struct i2c_adapter *adapter; -- cgit v1.2.3 From f30dc5208ca270ff783fa895b74bdd76dcb4ba3e Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Thu, 11 Jun 2015 11:16:32 +0200 Subject: i2c: at91: fix code checker warnings buf_len is a size_t, so unsigned, but was tested with '<= 0'. Reported-by: Wolfram Sang Signed-off-by: Cyrille Pitchen Acked-by: Ludovic Desroches Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 9e54f97a9020..1c758cd1e1ba 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -232,7 +232,7 @@ static void at91_twi_dma_cleanup(struct at91_twi_dev *dev) static void at91_twi_write_next_byte(struct at91_twi_dev *dev) { - if (dev->buf_len <= 0) + if (!dev->buf_len) return; /* 8bit write works with and without FIFO */ @@ -275,7 +275,7 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev) struct dma_chan *chan_tx = dma->chan_tx; unsigned int sg_len = 1; - if (dev->buf_len <= 0) + if (!dev->buf_len) return; dma->direction = DMA_TO_DEVICE; @@ -347,7 +347,7 @@ error: static void at91_twi_read_next_byte(struct at91_twi_dev *dev) { - if (dev->buf_len <= 0) + if (!dev->buf_len) return; /* 8bit read works with and without FIFO */ -- cgit v1.2.3 From 3ded3743a026e0762fa74467eafc66ecc1c484cc Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 9 Jun 2015 16:14:39 +0900 Subject: i2c: sh_mobile: add errata workaround This used to be in platform init code. We want it to do in the driver now. This is basically a code move and a new compatible added. Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Acked-by: Kuninori Morimoto Acked-by: Simon Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 007818b3e174..7193bcfdd940 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -150,6 +150,7 @@ struct sh_mobile_i2c_data { struct sh_mobile_dt_config { int clks_per_count; + void (*setup)(struct sh_mobile_i2c_data *pd); }; #define IIC_FLAG_HAS_ICIC67 (1 << 0) @@ -164,6 +165,7 @@ struct sh_mobile_dt_config { #define ICIC 0x0c #define ICCL 0x10 #define ICCH 0x14 +#define ICSTART 0x70 /* Register bits */ #define ICCR_ICE 0x80 @@ -190,6 +192,8 @@ struct sh_mobile_dt_config { #define ICIC_WAITE 0x02 #define ICIC_DTEE 0x01 +#define ICSTART_ICSTART 0x10 + static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data) { if (offs == ICIC) @@ -782,6 +786,33 @@ static struct i2c_algorithm sh_mobile_i2c_algorithm = { .master_xfer = sh_mobile_i2c_xfer, }; +/* + * r8a7740 chip has lasting errata on I2C I/O pad reset. + * this is work-around for it. + */ +static void sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd) +{ + iic_set_clr(pd, ICCR, ICCR_ICE, 0); + iic_rd(pd, ICCR); /* dummy read */ + + iic_set_clr(pd, ICSTART, ICSTART_ICSTART, 0); + iic_rd(pd, ICSTART); /* dummy read */ + + udelay(10); + + iic_wr(pd, ICCR, ICCR_SCP); + iic_wr(pd, ICSTART, 0); + + udelay(10); + + iic_wr(pd, ICCR, ICCR_TRS); + udelay(10); + iic_wr(pd, ICCR, 0); + udelay(10); + iic_wr(pd, ICCR, ICCR_TRS); + udelay(10); +} + static const struct sh_mobile_dt_config default_dt_config = { .clks_per_count = 1, }; @@ -790,9 +821,15 @@ static const struct sh_mobile_dt_config fast_clock_dt_config = { .clks_per_count = 2, }; +static const struct sh_mobile_dt_config r8a7740_dt_config = { + .clks_per_count = 1, + .setup = sh_mobile_i2c_r8a7740_workaround, +}; + static const struct of_device_id sh_mobile_i2c_dt_ids[] = { { .compatible = "renesas,rmobile-iic", .data = &default_dt_config }, { .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config }, + { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config }, { .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config }, { .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config }, @@ -885,6 +922,9 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) config = match->data; pd->clks_per_count = config->clks_per_count; + + if (config->setup) + config->setup(pd); } } else { if (pdata && pdata->bus_speed) -- cgit v1.2.3 From 7b618638a518ce7083fcca1bc7181a5c6337277a Mon Sep 17 00:00:00 2001 From: Silvan Wicki Date: Tue, 16 Jun 2015 17:40:59 +0200 Subject: i2c: bcm2835: clear reserved bits in S-Register The datasheet mentions on page 31 that the bits 10-31 must be read as don't care and written as 0. https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf We cannot guarantee that we read bits 10-31 as always 0 (because the datasheet says read as don't care). We clear the bits with a bitmask to prevent writing back unknown data at the reserved bits. Signed-off-by: Silvan Wicki Acked-by: Stephen Warren Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-bcm2835.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index c9336a3202d5..aa06da17e30e 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -50,6 +50,8 @@ #define BCM2835_I2C_S_CLKT BIT(9) #define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */ +#define BCM2835_I2C_BITMSK_S 0x03FF + #define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000)) struct bcm2835_i2c_dev { @@ -111,6 +113,7 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) u32 val, err; val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + val &= BCM2835_I2C_BITMSK_S; bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val); err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); -- cgit v1.2.3 From dc362d50ba94eaf2b1f11eecd81eb1d040d2d6e6 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 12 Jun 2015 14:41:16 +0200 Subject: i2c: use parent adapter quirks in mux Inherit parent adapter quirks in MUX in case the devices on the multiplexed buses are interested in the adapter limitations. Signed-off-by: Łukasz Gemborowski Signed-off-by: Alexander Sverdlin Signed-off-by: Wolfram Sang Cc: stable@kernel.org Fixes: b7f625840267b1 ("i2c: add quirk checks to core") --- drivers/i2c/i2c-mux.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 06cc1ff088f1..f190e75b9da8 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -144,6 +144,7 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, priv->adap.dev.parent = &parent->dev; priv->adap.retries = parent->retries; priv->adap.timeout = parent->timeout; + priv->adap.quirks = parent->quirks; /* Sanity check on class */ if (i2c_mux_parent_classes(parent) & class) -- cgit v1.2.3 From e766f338a74200b8104b1165776b19f56e252834 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 12 Jun 2015 14:40:37 +0200 Subject: i2c: mux: Use __i2c_transfer() instead of calling parent's master_xfer() Newly introduced quirks infrastructure doesn't work for the devices behind MUXes because MUX's master_xfer() calls parent's master_xfer() directly without checking the quirks. Instead of duplicating check code in MUX just call __i2c_transfer() instead. This has a side effect on tracing (messages will appear on both MUX bus and parent bus), but maybe that's not bad at the end. Signed-off-by: Alexander Sverdlin Tested-by: Łukasz Gemborowski Signed-off-by: Wolfram Sang Cc: stable@kernel.org Fixes: b7f625840267b1 ("i2c: add quirk checks to core") --- drivers/i2c/i2c-mux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index f190e75b9da8..2ba7c0fbc615 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -51,7 +51,7 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, ret = priv->select(parent, priv->mux_priv, priv->chan_id); if (ret >= 0) - ret = parent->algo->master_xfer(parent, msgs, num); + ret = __i2c_transfer(parent, msgs, num); if (priv->deselect) priv->deselect(parent, priv->mux_priv, priv->chan_id); -- cgit v1.2.3 From 0a8237ae319ab5988d40a7a9b33d68846aae34b4 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 12 Jun 2015 14:41:00 +0200 Subject: i2c: mux: pca954x: Use __i2c_transfer because of quirks pca9541 and pca954x are calling master_xfer() of the parent adapter directly thus bypassing the quirks checks of the adapter. Use __i2c_transfer() instead. Signed-off-by: Alexander Sverdlin Tested-by: Łukasz Gemborowski Acked-by: Laurent Pinchart Reviewed-by: Jisheng Zhang Signed-off-by: Wolfram Sang Cc: stable@kernel.org Fixes: b7f625840267b1 ("i2c: add quirk checks to core") --- drivers/i2c/muxes/i2c-mux-pca9541.c | 4 ++-- drivers/i2c/muxes/i2c-mux-pca954x.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index cb772775da43..0c8d4d2cbdaf 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -104,7 +104,7 @@ static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val) buf[0] = command; buf[1] = val; msg.buf = buf; - ret = adap->algo->master_xfer(adap, &msg, 1); + ret = __i2c_transfer(adap, &msg, 1); } else { union i2c_smbus_data data; @@ -144,7 +144,7 @@ static int pca9541_reg_read(struct i2c_client *client, u8 command) .buf = &val } }; - ret = adap->algo->master_xfer(adap, msg, 2); + ret = __i2c_transfer(adap, msg, 2); if (ret == 2) ret = val; else if (ret >= 0) diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index bea0d2de2993..ea4aa9dfcea9 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -134,7 +134,7 @@ static int pca954x_reg_write(struct i2c_adapter *adap, msg.len = 1; buf[0] = val; msg.buf = buf; - ret = adap->algo->master_xfer(adap, &msg, 1); + ret = __i2c_transfer(adap, &msg, 1); } else { union i2c_smbus_data data; ret = adap->algo->smbus_xfer(adap, client->addr, -- cgit v1.2.3 From 955fc950795d1c9f11f220f449ecb29b92985ee2 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Thu, 11 Jun 2015 11:35:26 +0200 Subject: i2c: davinci: Optimize SCL generation There are several cases where current clock configuration algorithm produces not optimal results: - truncation in "clk" calculation leads to the fact that actual BUS frequency will be always higher than spec except two exact module frequences 8MHz and 12MHz in the whole 7-12MHz range of permitted frequences - driver configures SCL HIGH to LOW ratio always 1 to 1 and this doesn't work well in 400kHz case, namely minimum time of LOW state (according to I2C Spec 2.1) 1.3us will not be fulfilled. HIGH to LOW ratio 1 to 2 would be more approriate here. Signed-off-by: Michael Lawnick Signed-off-by: Alexander Sverdlin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-davinci.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 3d3ae52b6916..3fbb9a035532 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -204,9 +204,30 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev) psc++; /* better to run under spec than over */ d = (psc >= 2) ? 5 : 7 - psc; - clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000)) - (d << 1); - clkh = clk >> 1; - clkl = clk - clkh; + clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000)); + /* Avoid driving the bus too fast because of rounding errors above */ + if (input_clock / (psc + 1) / clk > pdata->bus_freq * 1000) + clk++; + /* + * According to I2C-BUS Spec 2.1, in FAST-MODE LOW period should be at + * least 1.3uS, which is not the case with 50% duty cycle. Driving HIGH + * to LOW ratio as 1 to 2 is more safe. + */ + if (pdata->bus_freq > 100) + clkl = (clk << 1) / 3; + else + clkl = (clk >> 1); + /* + * It's not always possible to have 1 to 2 ratio when d=7, so fall back + * to minimal possible clkh in this case. + */ + if (clk >= clkl + d) { + clkh = clk - clkl - d; + clkl -= d; + } else { + clkh = 0; + clkl = clk - (d << 1); + } davinci_i2c_write_reg(dev, DAVINCI_I2C_PSC_REG, psc); davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh); -- cgit v1.2.3 From e8e523a42a236d0e80a5651741cfc916aa298e3f Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Thu, 11 Jun 2015 16:12:03 +0200 Subject: i2c: octeon: remove unused signal handling 'commit 2637e5fd232d ("i2c: octeon: Fix i2c fail problem when a process is terminated by a signal")' removed the wait_event_interruptible_timeout to prevent half/mixed i2c messages from being sent/recievd but forgot to drop the signal handling case in the return handling. This just removes this dead code. While at it the return variable is adjusted to the type expected. Signed-off-by: Nicholas Mc Guire Reviewed-by: Alexander Sverdlin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 6e75e016bffc..32914ab42a19 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -200,7 +200,7 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) */ static int octeon_i2c_wait(struct octeon_i2c *i2c) { - int result; + long result; octeon_i2c_int_enable(i2c); @@ -210,10 +210,7 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) octeon_i2c_int_disable(i2c); - if (result < 0) { - dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); - return result; - } else if (result == 0) { + if (result == 0) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; } -- cgit v1.2.3 From 4bb28e37765f4b5b18cb3b9242aa8542e8c4b8b4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 16 Jun 2015 19:47:21 +0200 Subject: i2c: tegra: don't advertise SMBUS_QUICK This HW cannot send 0-byte-length messages and the driver discards them. So, we should not advertise SMBUS_QUICK. Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 1bcd75ea0b4c..0a1b6eacef0f 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -656,8 +656,8 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], static u32 tegra_i2c_func(struct i2c_adapter *adap) { struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); - u32 ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | - I2C_FUNC_PROTOCOL_MANGLING; + u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; if (i2c_dev->hw->has_continue_xfer_support) ret |= I2C_FUNC_NOSTART; -- cgit v1.2.3 From 3aaa34b9923b994c13267e05ce8246faa1e8c8eb Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 16 Jun 2015 19:57:29 +0200 Subject: i2c: tegra: apply size limit quirk payload_size is a 12 bit field in the HW register, so add a limit for this size. That way we gracefully reject the message beforehand instead of generating an OOPS while transferring. Verified using some older Tegra2 documentation and a more recent Jetson TK1 board. Signed-off-by: Wolfram Sang Reviewed-by: Alexander Sverdlin --- drivers/i2c/busses/i2c-tegra.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 0a1b6eacef0f..78a366814696 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -669,6 +669,12 @@ static const struct i2c_algorithm tegra_i2c_algo = { .functionality = tegra_i2c_func, }; +/* payload size is only 12 bit */ +static struct i2c_adapter_quirks tegra_i2c_quirks = { + .max_read_len = 4096, + .max_write_len = 4096, +}; + static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_continue_xfer_support = false, .has_per_pkt_xfer_complete_irq = false, @@ -739,6 +745,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->base = base; i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->adapter.quirks = &tegra_i2c_quirks; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; -- cgit v1.2.3 From edfc39012364a6ea8e4c7067c2655c92c2d02df4 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 17 Jun 2015 12:08:38 +0300 Subject: i2c: designware: Make sure the device is suspended before disabling runtime PM The driver calls pm_runtime_put() right before pm_runtime_disable() in its ->remove() hook to make sure clock is gated etc. However, it turns out that pm_runtime_put() only calls ->idle() hook without actually suspending anything. The following pm_runtime_disable() will prevent the driver from suspending thus leaving it "active". It is better to suspend the device synchronously to make sure it is actually suspended before disabling runtime PM from it. While there, undo call to pm_runtime_use_autosuspend(). Signed-off-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 4794911a6165..3dd2de31a2f8 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -281,7 +281,8 @@ static int dw_i2c_remove(struct platform_device *pdev) i2c_dw_disable(dev); - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); if (has_acpi_companion(&pdev->dev)) -- cgit v1.2.3 From efd2c6118e49152bea891706432ac3c9579836fe Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 20 Jun 2015 21:03:19 +0200 Subject: i2c: rcar: use adapter default for timeout 5 seconds is a very large timeout, and it is hardcoded. Use the default timeout from 'struct adapter' which is 1 second. It can also be modified from userspace for specific workloads via i2c-dev. Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 9eef46254369..e57e520b4756 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -532,7 +532,7 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, timeout = wait_event_timeout(priv->wait, rcar_i2c_flags_has(priv, ID_DONE), - 5 * HZ); + adap->timeout); if (!timeout) { ret = -ETIMEDOUT; break; -- cgit v1.2.3 From b6763d0da5c16ea8770252c36ba2afca76aaedbd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 20 Jun 2015 21:03:20 +0200 Subject: i2c: rcar: use proper type for timeout wait_event_timeout returns long, not int. Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index e57e520b4756..d8361dada584 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -490,7 +490,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, 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; + int i, ret; + long timeout; pm_runtime_get_sync(dev); -- cgit v1.2.3 From 41bdbd2887591a3fa586e71cd2b462c3525845a3 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 20 Jun 2015 21:03:21 +0200 Subject: i2c: sh_mobile: use adapter default for timeout 5 seconds is a very large timeout, and it is hardcoded. Use the default timeout from 'struct adapter' which is 1 second. It can also be modified from userspace for specific workloads via i2c-dev. Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 7193bcfdd940..5490bfb58f3e 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -751,7 +751,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, /* The interrupt handler takes care of the rest... */ k = wait_event_timeout(pd->wait, pd->sr & (ICSR_TACK | SW_DONE), - 5 * HZ); + adapter->timeout); if (!k) { dev_err(pd->dev, "Transfer request timed out\n"); if (pd->dma_direction != DMA_NONE) -- cgit v1.2.3 From 365322fb32e82b20758e8a18e3aa94692ce2a3d1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 20 Jun 2015 21:03:22 +0200 Subject: i2c: sh_mobile: use proper type for timeout wait_event_timeout returns long, not int. Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 5490bfb58f3e..47659a925e09 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -730,7 +730,8 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter); struct i2c_msg *msg; int err = 0; - int i, k; + int i; + long timeout; activate_ch(pd); @@ -749,10 +750,10 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, i2c_op(pd, OP_START, 0); /* The interrupt handler takes care of the rest... */ - k = wait_event_timeout(pd->wait, + timeout = wait_event_timeout(pd->wait, pd->sr & (ICSR_TACK | SW_DONE), adapter->timeout); - if (!k) { + if (!timeout) { dev_err(pd->dev, "Transfer request timed out\n"); if (pd->dma_direction != DMA_NONE) sh_mobile_i2c_cleanup_dma(pd); -- cgit v1.2.3 From a294aba164389a3d2c40dfcf5f3989a3bbfe38a2 Mon Sep 17 00:00:00 2001 From: Silvan Wicki Date: Thu, 18 Jun 2015 11:10:11 +0200 Subject: i2c: busses: i2c-bcm2835: limits cdiv to allowed values Checks if the cdiv value is in between min (0x2) and max (0xFFFE) supported values by the bcm2835. If not, it returns -ENODEV. See page 33/34 of BCM2835-ARM-Peripherals.pdf for the DIV register. https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf Signed-off-by: Silvan Wicki [wsa: resolved a merge conflict] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-bcm2835.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index aa06da17e30e..3032b89ac60b 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -52,6 +52,9 @@ #define BCM2835_I2C_BITMSK_S 0x03FF +#define BCM2835_I2C_CDIV_MIN 0x0002 +#define BCM2835_I2C_CDIV_MAX 0xFFFE + #define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000)) struct bcm2835_i2c_dev { @@ -261,6 +264,11 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) */ if (divider & 1) divider++; + if ((divider < BCM2835_I2C_CDIV_MIN) || + (divider > BCM2835_I2C_CDIV_MAX)) { + dev_err(&pdev->dev, "Invalid clock-frequency\n"); + return -ENODEV; + } bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider); irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -- cgit v1.2.3