]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge branch 'i2c-mux/for-next' of https://github.com/peda-r/i2c-mux into i2c/for-5.2
authorWolfram Sang <wsa@the-dreams.de>
Fri, 3 May 2019 13:20:04 +0000 (15:20 +0200)
committerWolfram Sang <wsa@the-dreams.de>
Fri, 3 May 2019 13:20:58 +0000 (15:20 +0200)
Mainly some pca954x work, i.e. removal of unused platform data support
and added support for sysfs interface for manipulating/examining the
idle state. And then a mechanical cocci-style patch.

41 files changed:
Documentation/devicetree/bindings/eeprom/at24.txt
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
Documentation/devicetree/bindings/i2c/i2c-designware.txt
Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt
Documentation/devicetree/bindings/i2c/i2c-stm32.txt
Documentation/i2c/busses/i2c-amd-mp2 [new file with mode: 0644]
MAINTAINERS
drivers/i2c/algos/i2c-algo-bit.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-amd-mp2-pci.c [new file with mode: 0644]
drivers/i2c/busses/i2c-amd-mp2-plat.c [new file with mode: 0644]
drivers/i2c/busses/i2c-amd-mp2.h [new file with mode: 0644]
drivers/i2c/busses/i2c-at91-core.c [new file with mode: 0644]
drivers/i2c/busses/i2c-at91-master.c [moved from drivers/i2c/busses/i2c-at91.c with 65% similarity]
drivers/i2c/busses/i2c-at91-slave.c [new file with mode: 0644]
drivers/i2c/busses/i2c-at91.h [new file with mode: 0644]
drivers/i2c/busses/i2c-axxia.c
drivers/i2c/busses/i2c-bcm-iproc.c
drivers/i2c/busses/i2c-brcmstb.c
drivers/i2c/busses/i2c-designware-common.c
drivers/i2c/busses/i2c-designware-core.h
drivers/i2c/busses/i2c-designware-platdrv.c
drivers/i2c/busses/i2c-gpio.c
drivers/i2c/busses/i2c-isch.c
drivers/i2c/busses/i2c-mt65xx.c
drivers/i2c/busses/i2c-nomadik.c
drivers/i2c/busses/i2c-ocores.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-rcar.c
drivers/i2c/busses/i2c-riic.c
drivers/i2c/busses/i2c-stm32f7.c
drivers/i2c/busses/i2c-stu300.c
drivers/i2c/busses/i2c-tegra-bpmp.c
drivers/i2c/i2c-core-base.c
drivers/i2c/i2c-core-smbus.c
drivers/i2c/i2c-core.h
drivers/i2c/i2c-mux.c
drivers/i2c/muxes/i2c-demux-pinctrl.c
include/linux/i2c-algo-bit.h
include/linux/i2c.h

index 0e456bbc121399b73a22c4b4745a61bf7a38794d..22aead844d0f58d626a3d03cbaaa202475a9f438 100644 (file)
@@ -50,6 +50,7 @@ Required properties:
 
                 "nxp,se97b" - the fallback is "atmel,24c02",
                 "renesas,r1ex24002" - the fallback is "atmel,24c02"
+                "renesas,r1ex24016" - the fallback is "atmel,24c16"
                 "renesas,r1ex24128" - the fallback is "atmel,24c128"
                 "rohm,br24t01" - the fallback is "atmel,24c01"
 
index 81f982ccca315537e7b0db1c81ac4b6a0abec873..d12cc33cca6ce0585d2b55d07b260ad07974952e 100644 (file)
@@ -3,15 +3,12 @@ Broadcom iProc I2C controller
 Required properties:
 
 - compatible:
-    Must be "brcm,iproc-i2c"
+    Must be "brcm,iproc-i2c" or "brcm,iproc-nic-i2c"
 
 - reg:
     Define the base and range of the I/O address space that contain the iProc
     I2C controller registers
 
-- interrupts:
-    Should contain the I2C interrupt
-
 - clock-frequency:
     This is the I2C bus clock. Need to be either 100000 or 400000
 
@@ -21,6 +18,18 @@ Required properties:
 - #size-cells:
     Always 0
 
+Optional properties:
+
+- interrupts:
+    Should contain the I2C interrupt. For certain revisions of the I2C
+    controller, I2C interrupt is unwired to the interrupt controller. In such
+    case, this property should be left unspecified, and driver will fall back
+    to polling mode
+
+- brcm,ape-hsls-addr-mask:
+    Required for "brcm,iproc-nic-i2c". Host view of address mask into the
+    'APE' co-processor. Value must be unsigned, 32-bit
+
 Example:
        i2c0: i2c@18008000 {
                compatible = "brcm,iproc-i2c";
index 3e4bcc2fb6f71f7d81646ebe01a0f7a44396a46f..08be4d3846e57ce072be05f6cd2803ea12a9f919 100644 (file)
@@ -6,12 +6,21 @@ Required properties :
                 or "mscc,ocelot-i2c" with "snps,designware-i2c" for fallback
  - reg : Offset and length of the register set for the device
  - interrupts : <IRQ> where IRQ is the interrupt number.
+ - clocks : phandles for the clocks, see the description of clock-names below.
+   The phandle for the "ic_clk" clock is required. The phandle for the "pclk"
+   clock is optional. If a single clock is specified but no clock-name, it is
+   the "ic_clk" clock. If both clocks are listed, the "ic_clk" must be first.
 
 Recommended properties :
 
  - clock-frequency : desired I2C bus clock frequency in Hz.
 
 Optional properties :
+
+ - clock-names : Contains the names of the clocks:
+    "ic_clk", for the core clock used to generate the external I2C clock.
+    "pclk", the interface clock, required for register access.
+
  - reg : for "mscc,ocelot-i2c", a second register set to configure the SDA hold
    time, named ICPU_CFG:TWI_DELAY in the datasheet.
 
index ee4c324541987ca7242b52763f5b4a702398114f..68f6d73a8b73c2dac2dd333701e81922cc69dcdb 100644 (file)
@@ -12,13 +12,16 @@ Required properties:
       "mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
       "mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
       "mediatek,mt8173-i2c": for MediaTek MT8173
+      "mediatek,mt8183-i2c": for MediaTek MT8183
+      "mediatek,mt8516-i2c", "mediatek,mt2712-i2c": for MediaTek MT8516
   - 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
+  - clock-names: Must include "main" and "dma", "arb" is for multi-master that
+    one bus has more than two i2c controllers, if enable have-pmic need include
     "pmic" extra.
 
 Optional properties:
index 69240e189b01e61d44e2d588aab0e8fe3db579fa..f334738f7a356af06993d4b10f5abcca40096282 100644 (file)
@@ -1,11 +1,11 @@
 * I2C controller embedded in STMicroelectronics STM32 I2C platform
 
-Required properties :
-- compatible : Must be one of the following
+Required properties:
+- compatible: Must be one of the following
   - "st,stm32f4-i2c"
   - "st,stm32f7-i2c"
-- reg : Offset and length of the register set for the device
-- interrupts : Must contain the interrupt id for I2C event and then the
+- reg: Offset and length of the register set for the device
+- interrupts: Must contain the interrupt id for I2C event and then the
   interrupt id for I2C error.
 - resets: Must contain the phandle to the reset controller.
 - clocks: Must contain the input clock of the I2C instance.
@@ -14,25 +14,26 @@ Required properties :
 - #address-cells = <1>;
 - #size-cells = <0>;
 
-Optional properties :
-- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
+Optional properties:
+- clock-frequency: Desired I2C bus clock frequency in Hz. If not specified,
   the default 100 kHz frequency will be used.
   For STM32F4 SoC Standard-mode and Fast-mode are supported, possible values are
   100000 and 400000.
-  For STM32F7 SoC, Standard-mode, Fast-mode and Fast-mode Plus are supported,
-  possible values are 100000, 400000 and 1000000.
-- i2c-scl-rising-time-ns : Only for STM32F7, I2C SCL Rising time for the board
-  (default: 25)
-- i2c-scl-falling-time-ns : Only for STM32F7, I2C SCL Falling time for the board
-  (default: 10)
+  For STM32F7, STM32H7 and STM32MP1 SoCs, Standard-mode, Fast-mode and Fast-mode
+  Plus are supported, possible values are 100000, 400000 and 1000000.
+- i2c-scl-rising-time-ns: I2C SCL Rising time for the board (default: 25)
+  For STM32F7, STM32H7 and STM32MP1 only.
+- i2c-scl-falling-time-ns: I2C SCL Falling time for the board (default: 10)
+  For STM32F7, STM32H7 and STM32MP1 only.
   I2C Timings are derived from these 2 values
-- st,syscfg-fmp:  Only for STM32F7, use to set Fast Mode Plus bit within SYSCFG
-  whether Fast Mode Plus speed is selected by slave.
-       1st cell : phandle to syscfg
-       2nd cell : register offset within SYSCFG
-       3rd cell : register bitmask for FMP bit
+- st,syscfg-fmp: Use to set Fast Mode Plus bit within SYSCFG when Fast Mode
+  Plus speed is selected by slave.
+       1st cell: phandle to syscfg
+       2nd cell: register offset within SYSCFG
+       3rd cell: register bitmask for FMP bit
+  For STM32F7, STM32H7 and STM32MP1 only.
 
-Example :
+Example:
 
        i2c@40005400 {
                compatible = "st,stm32f4-i2c";
diff --git a/Documentation/i2c/busses/i2c-amd-mp2 b/Documentation/i2c/busses/i2c-amd-mp2
new file mode 100644 (file)
index 0000000..6571487
--- /dev/null
@@ -0,0 +1,23 @@
+Kernel driver i2c-amd-mp2
+
+Supported adapters:
+  * AMD MP2 PCIe interface
+
+Datasheet: not publicly available.
+
+Authors:
+       Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+       Nehal Shah <nehal-bakulchandra.shah@amd.com>
+       Elie Morisse <syniurge@gmail.com>
+
+Description
+-----------
+
+The MP2 is an ARM processor programmed as an I2C controller and communicating
+with the x86 host through PCI.
+
+If you see something like this:
+
+03:00.7 MP2 I2C controller: Advanced Micro Devices, Inc. [AMD] Device 15e6
+
+in your 'lspci -v', then this driver is for your device.
index 43b36dbed48e77bffcc94ea79b8a2e6d79ad0aae..baf7623b01b6b647227ce4a3df2a446d64a9e039 100644 (file)
@@ -816,6 +816,14 @@ F: drivers/gpu/drm/amd/include/vi_structs.h
 F:     drivers/gpu/drm/amd/include/v9_structs.h
 F:     include/uapi/linux/kfd_ioctl.h
 
+AMD MP2 I2C DRIVER
+M:     Elie Morisse <syniurge@gmail.com>
+M:     Nehal Shah <nehal-bakulchandra.shah@amd.com>
+M:     Shyam Sundar S K <shyam-sundar.s-k@amd.com>
+L:     linux-i2c@vger.kernel.org
+S:     Maintained
+F:     drivers/i2c/busses/i2c-amd-mp2*
+
 AMD POWERPLAY
 M:     Rex Zhu <rex.zhu@amd.com>
 M:     Evan Quan <evan.quan@amd.com>
@@ -2559,7 +2567,7 @@ F:        include/linux/dmaengine.h
 F:     include/linux/async_tx.h
 
 AT24 EEPROM DRIVER
-M:     Bartosz Golaszewski <brgl@bgdev.pl>
+M:     Bartosz Golaszewski <bgolaszewski@baylibre.com>
 L:     linux-i2c@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
 S:     Maintained
@@ -10111,7 +10119,8 @@ MICROCHIP I2C DRIVER
 M:     Ludovic Desroches <ludovic.desroches@microchip.com>
 L:     linux-i2c@vger.kernel.org
 S:     Supported
-F:     drivers/i2c/busses/i2c-at91.c
+F:     drivers/i2c/busses/i2c-at91.h
+F:     drivers/i2c/busses/i2c-at91-*.c
 
 MICROCHIP ISC DRIVER
 M:     Eugen Hristev <eugen.hristev@microchip.com>
index 5e5990a83da5d9ae13fc7cf244f474f8606e50de..913db013fe90111449abe4eff7ccb36f97b2c41c 100644 (file)
@@ -603,6 +603,23 @@ static int bit_xfer(struct i2c_adapter *i2c_adap,
        return ret;
 }
 
+/*
+ * We print a warning when we are not flagged to support atomic transfers but
+ * will try anyhow. That's what the I2C core would do as well. Sadly, we can't
+ * modify the algorithm struct at probe time because this struct is exported
+ * 'const'.
+ */
+static int bit_xfer_atomic(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
+                          int num)
+{
+       struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
+
+       if (!adap->can_do_atomic)
+               dev_warn(&i2c_adap->dev, "not flagged for atomic transfers\n");
+
+       return bit_xfer(i2c_adap, msgs, num);
+}
+
 static u32 bit_func(struct i2c_adapter *adap)
 {
        return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
@@ -615,8 +632,9 @@ static u32 bit_func(struct i2c_adapter *adap)
 /* -----exported algorithm data: ------------------------------------- */
 
 const struct i2c_algorithm i2c_bit_algo = {
-       .master_xfer    = bit_xfer,
-       .functionality  = bit_func,
+       .master_xfer = bit_xfer,
+       .master_xfer_atomic = bit_xfer_atomic,
+       .functionality = bit_func,
 };
 EXPORT_SYMBOL(i2c_bit_algo);
 
index f8979abb9a19ca963bf9625fc911ab74590b388a..977161bb7062a1156722f2d86390eb046d45df32 100644 (file)
@@ -77,6 +77,16 @@ config I2C_AMD8111
          This driver can also be built as a module.  If so, the module
          will be called i2c-amd8111.
 
+config I2C_AMD_MP2
+       tristate "AMD MP2 PCIe"
+       depends on PCI && ACPI
+       help
+         If you say yes to this option, support will be included for the AMD
+         MP2 PCIe I2C adapter.
+
+         This driver can also be built as modules.  If so, the modules will
+         be called i2c-amd-mp2-pci and i2c-amd-mp2-plat.
+
 config I2C_HIX5HD2
        tristate "Hix5hd2 high-speed I2C driver"
        depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST
@@ -388,6 +398,19 @@ config I2C_AT91
          the latency to fill the transmission register is too long. If you
          are facing this situation, use the i2c-gpio driver.
 
+config I2C_AT91_SLAVE_EXPERIMENTAL
+       tristate "Microchip AT91 I2C experimental slave mode"
+       depends on I2C_AT91
+       select I2C_SLAVE
+       help
+         If you say yes to this option, support for the slave mode will be
+         added. Caution: do not use it for production. This feature has not
+         been tested in a heavy way, help wanted.
+         There are known bugs:
+           - It can hang, on a SAMA5D4, after several transfers.
+           - There are some mismtaches with a SAMA5D4 as slave and a SAMA5D2 as
+           master.
+
 config I2C_AU1550
        tristate "Au1550/Au1200/Au1300 SMBus interface"
        depends on MIPS_ALCHEMY
@@ -425,6 +448,7 @@ config I2C_BCM_IPROC
        tristate "Broadcom iProc I2C controller"
        depends on ARCH_BCM_IPROC || COMPILE_TEST
        default ARCH_BCM_IPROC
+       select I2C_SLAVE
        help
          If you say yes to this option, support will be included for the
          Broadcom iProc I2C controller.
index 5f0cb6915969aa98d5722b02e0fe9cb9a1ae25a7..a3245231b0b7b9971ea85eec319204952705ebff 100644 (file)
@@ -33,8 +33,13 @@ obj-$(CONFIG_I2C_POWERMAC)   += i2c-powermac.o
 
 # Embedded system I2C/SMBus host controller drivers
 obj-$(CONFIG_I2C_ALTERA)       += i2c-altera.o
+obj-$(CONFIG_I2C_AMD_MP2)      += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
 obj-$(CONFIG_I2C_ASPEED)       += i2c-aspeed.o
 obj-$(CONFIG_I2C_AT91)         += i2c-at91.o
+i2c-at91-objs                  := i2c-at91-core.o i2c-at91-master.o
+ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y)
+       i2c-at91-objs           += i2c-at91-slave.o
+endif
 obj-$(CONFIG_I2C_AU1550)       += i2c-au1550.o
 obj-$(CONFIG_I2C_AXXIA)                += i2c-axxia.o
 obj-$(CONFIG_I2C_BCM2835)      += i2c-bcm2835.o
diff --git a/drivers/i2c/busses/i2c-amd-mp2-pci.c b/drivers/i2c/busses/i2c-amd-mp2-pci.c
new file mode 100644 (file)
index 0000000..455e1f3
--- /dev/null
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * AMD MP2 PCIe communication driver
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ *          Elie Morisse <syniurge@gmail.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "i2c-amd-mp2.h"
+
+#include <linux/io-64-nonatomic-lo-hi.h>
+
+static void amd_mp2_c2p_mutex_lock(struct amd_i2c_common *i2c_common)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+       /* there is only one data mailbox for two i2c adapters */
+       mutex_lock(&privdata->c2p_lock);
+       privdata->c2p_lock_busid = i2c_common->bus_id;
+}
+
+static void amd_mp2_c2p_mutex_unlock(struct amd_i2c_common *i2c_common)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+       if (unlikely(privdata->c2p_lock_busid != i2c_common->bus_id)) {
+               dev_warn(ndev_dev(privdata),
+                        "bus %d attempting to unlock C2P locked by bus %d\n",
+                        i2c_common->bus_id, privdata->c2p_lock_busid);
+               return;
+       }
+
+       mutex_unlock(&privdata->c2p_lock);
+}
+
+static int amd_mp2_cmd(struct amd_i2c_common *i2c_common,
+                      union i2c_cmd_base i2c_cmd_base)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+       void __iomem *reg;
+
+       i2c_common->reqcmd = i2c_cmd_base.s.i2c_cmd;
+
+       reg = privdata->mmio + ((i2c_cmd_base.s.bus_id == 1) ?
+                               AMD_C2P_MSG1 : AMD_C2P_MSG0);
+       writel(i2c_cmd_base.ul, reg);
+
+       return 0;
+}
+
+int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+       union i2c_cmd_base i2c_cmd_base;
+
+       dev_dbg(ndev_dev(privdata), "%s id: %d\n", __func__,
+               i2c_common->bus_id);
+
+       i2c_cmd_base.ul = 0;
+       i2c_cmd_base.s.i2c_cmd = enable ? i2c_enable : i2c_disable;
+       i2c_cmd_base.s.bus_id = i2c_common->bus_id;
+       i2c_cmd_base.s.i2c_speed = i2c_common->i2c_speed;
+
+       amd_mp2_c2p_mutex_lock(i2c_common);
+
+       return amd_mp2_cmd(i2c_common, i2c_cmd_base);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_bus_enable_set);
+
+static void amd_mp2_cmd_rw_fill(struct amd_i2c_common *i2c_common,
+                               union i2c_cmd_base *i2c_cmd_base,
+                               enum i2c_cmd reqcmd)
+{
+       i2c_cmd_base->s.i2c_cmd = reqcmd;
+       i2c_cmd_base->s.bus_id = i2c_common->bus_id;
+       i2c_cmd_base->s.i2c_speed = i2c_common->i2c_speed;
+       i2c_cmd_base->s.slave_addr = i2c_common->msg->addr;
+       i2c_cmd_base->s.length = i2c_common->msg->len;
+}
+
+int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+       union i2c_cmd_base i2c_cmd_base;
+
+       amd_mp2_cmd_rw_fill(i2c_common, &i2c_cmd_base, reqcmd);
+       amd_mp2_c2p_mutex_lock(i2c_common);
+
+       if (i2c_common->msg->len <= 32) {
+               i2c_cmd_base.s.mem_type = use_c2pmsg;
+               if (reqcmd == i2c_write)
+                       memcpy_toio(privdata->mmio + AMD_C2P_MSG2,
+                                   i2c_common->msg->buf,
+                                   i2c_common->msg->len);
+       } else {
+               i2c_cmd_base.s.mem_type = use_dram;
+               writeq((u64)i2c_common->dma_addr,
+                      privdata->mmio + AMD_C2P_MSG2);
+       }
+
+       return amd_mp2_cmd(i2c_common, i2c_cmd_base);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_rw);
+
+static void amd_mp2_pci_check_rw_event(struct amd_i2c_common *i2c_common)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+       int len = i2c_common->eventval.r.length;
+       u32 slave_addr = i2c_common->eventval.r.slave_addr;
+       bool err = false;
+
+       if (unlikely(len != i2c_common->msg->len)) {
+               dev_err(ndev_dev(privdata),
+                       "length %d in event doesn't match buffer length %d!\n",
+                       len, i2c_common->msg->len);
+               err = true;
+       }
+
+       if (unlikely(slave_addr != i2c_common->msg->addr)) {
+               dev_err(ndev_dev(privdata),
+                       "unexpected slave address %x (expected: %x)!\n",
+                       slave_addr, i2c_common->msg->addr);
+               err = true;
+       }
+
+       if (!err)
+               i2c_common->cmd_success = true;
+}
+
+static void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+       enum status_type sts = i2c_common->eventval.r.status;
+       enum response_type res = i2c_common->eventval.r.response;
+       int len = i2c_common->eventval.r.length;
+
+       if (res != command_success) {
+               if (res != command_failed)
+                       dev_err(ndev_dev(privdata), "invalid response to i2c command!\n");
+               return;
+       }
+
+       switch (i2c_common->reqcmd) {
+       case i2c_read:
+               if (sts == i2c_readcomplete_event) {
+                       amd_mp2_pci_check_rw_event(i2c_common);
+                       if (len <= 32)
+                               memcpy_fromio(i2c_common->msg->buf,
+                                             privdata->mmio + AMD_C2P_MSG2,
+                                             len);
+               } else if (sts != i2c_readfail_event) {
+                       dev_err(ndev_dev(privdata),
+                               "invalid i2c status after read (%d)!\n", sts);
+               }
+               break;
+       case i2c_write:
+               if (sts == i2c_writecomplete_event)
+                       amd_mp2_pci_check_rw_event(i2c_common);
+               else if (sts != i2c_writefail_event)
+                       dev_err(ndev_dev(privdata),
+                               "invalid i2c status after write (%d)!\n", sts);
+               break;
+       case i2c_enable:
+               if (sts == i2c_busenable_complete)
+                       i2c_common->cmd_success = true;
+               else if (sts != i2c_busenable_failed)
+                       dev_err(ndev_dev(privdata),
+                               "invalid i2c status after bus enable (%d)!\n",
+                               sts);
+               break;
+       case i2c_disable:
+               if (sts == i2c_busdisable_complete)
+                       i2c_common->cmd_success = true;
+               else if (sts != i2c_busdisable_failed)
+                       dev_err(ndev_dev(privdata),
+                               "invalid i2c status after bus disable (%d)!\n",
+                               sts);
+               break;
+       default:
+               break;
+       }
+}
+
+void amd_mp2_process_event(struct amd_i2c_common *i2c_common)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+       if (unlikely(i2c_common->reqcmd == i2c_none)) {
+               dev_warn(ndev_dev(privdata),
+                        "received msg but no cmd was sent (bus = %d)!\n",
+                        i2c_common->bus_id);
+               return;
+       }
+
+       __amd_mp2_process_event(i2c_common);
+
+       i2c_common->reqcmd = i2c_none;
+       amd_mp2_c2p_mutex_unlock(i2c_common);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_process_event);
+
+static irqreturn_t amd_mp2_irq_isr(int irq, void *dev)
+{
+       struct amd_mp2_dev *privdata = dev;
+       struct amd_i2c_common *i2c_common;
+       u32 val;
+       unsigned int bus_id;
+       void __iomem *reg;
+       enum irqreturn ret = IRQ_NONE;
+
+       for (bus_id = 0; bus_id < 2; bus_id++) {
+               i2c_common = privdata->busses[bus_id];
+               if (!i2c_common)
+                       continue;
+
+               reg = privdata->mmio + ((bus_id == 0) ?
+                                       AMD_P2C_MSG1 : AMD_P2C_MSG2);
+               val = readl(reg);
+               if (val != 0) {
+                       writel(0, reg);
+                       writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
+                       i2c_common->eventval.ul = val;
+                       i2c_common->cmd_completion(i2c_common);
+
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       if (ret != IRQ_HANDLED) {
+               val = readl(privdata->mmio + AMD_P2C_MSG_INTEN);
+               if (val != 0) {
+                       writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
+                       dev_warn(ndev_dev(privdata),
+                                "received irq without message\n");
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       return ret;
+}
+
+void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common)
+{
+       i2c_common->reqcmd = i2c_none;
+       amd_mp2_c2p_mutex_unlock(i2c_common);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_rw_timeout);
+
+int amd_mp2_register_cb(struct amd_i2c_common *i2c_common)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+       if (i2c_common->bus_id > 1)
+               return -EINVAL;
+
+       if (privdata->busses[i2c_common->bus_id]) {
+               dev_err(ndev_dev(privdata),
+                       "Bus %d already taken!\n", i2c_common->bus_id);
+               return -EINVAL;
+       }
+
+       privdata->busses[i2c_common->bus_id] = i2c_common;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(amd_mp2_register_cb);
+
+int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common)
+{
+       struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
+
+       privdata->busses[i2c_common->bus_id] = NULL;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(amd_mp2_unregister_cb);
+
+static void amd_mp2_clear_reg(struct amd_mp2_dev *privdata)
+{
+       int reg;
+
+       for (reg = AMD_C2P_MSG0; reg <= AMD_C2P_MSG9; reg += 4)
+               writel(0, privdata->mmio + reg);
+
+       for (reg = AMD_P2C_MSG1; reg <= AMD_P2C_MSG2; reg += 4)
+               writel(0, privdata->mmio + reg);
+}
+
+static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
+                           struct pci_dev *pci_dev)
+{
+       int rc;
+
+       pci_set_drvdata(pci_dev, privdata);
+
+       rc = pcim_enable_device(pci_dev);
+       if (rc) {
+               dev_err(ndev_dev(privdata), "Failed to enable MP2 PCI device\n");
+               goto err_pci_enable;
+       }
+
+       rc = pcim_iomap_regions(pci_dev, 1 << 2, pci_name(pci_dev));
+       if (rc) {
+               dev_err(ndev_dev(privdata), "I/O memory remapping failed\n");
+               goto err_pci_enable;
+       }
+       privdata->mmio = pcim_iomap_table(pci_dev)[2];
+
+       pci_set_master(pci_dev);
+
+       rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));
+       if (rc) {
+               rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
+               if (rc)
+                       goto err_dma_mask;
+       }
+
+       /* Set up intx irq */
+       writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
+       pci_intx(pci_dev, 1);
+       rc = devm_request_irq(&pci_dev->dev, pci_dev->irq, amd_mp2_irq_isr,
+                             IRQF_SHARED, dev_name(&pci_dev->dev), privdata);
+       if (rc)
+               dev_err(&pci_dev->dev, "Failure requesting irq %i: %d\n",
+                       pci_dev->irq, rc);
+
+       return rc;
+
+err_dma_mask:
+       pci_clear_master(pci_dev);
+err_pci_enable:
+       pci_set_drvdata(pci_dev, NULL);
+       return rc;
+}
+
+static int amd_mp2_pci_probe(struct pci_dev *pci_dev,
+                            const struct pci_device_id *id)
+{
+       struct amd_mp2_dev *privdata;
+       int rc;
+
+       privdata = devm_kzalloc(&pci_dev->dev, sizeof(*privdata), GFP_KERNEL);
+       if (!privdata)
+               return -ENOMEM;
+
+       rc = amd_mp2_pci_init(privdata, pci_dev);
+       if (rc)
+               return rc;
+
+       mutex_init(&privdata->c2p_lock);
+       privdata->pci_dev = pci_dev;
+
+       pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
+       pm_runtime_use_autosuspend(&pci_dev->dev);
+       pm_runtime_put_autosuspend(&pci_dev->dev);
+       pm_runtime_allow(&pci_dev->dev);
+
+       privdata->probed = true;
+
+       dev_info(&pci_dev->dev, "MP2 device registered.\n");
+       return 0;
+}
+
+static void amd_mp2_pci_remove(struct pci_dev *pci_dev)
+{
+       struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
+
+       pm_runtime_forbid(&pci_dev->dev);
+       pm_runtime_get_noresume(&pci_dev->dev);
+
+       pci_intx(pci_dev, 0);
+       pci_clear_master(pci_dev);
+
+       amd_mp2_clear_reg(privdata);
+}
+
+#ifdef CONFIG_PM
+static int amd_mp2_pci_suspend(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
+       struct amd_i2c_common *i2c_common;
+       unsigned int bus_id;
+       int ret = 0;
+
+       for (bus_id = 0; bus_id < 2; bus_id++) {
+               i2c_common = privdata->busses[bus_id];
+               if (i2c_common)
+                       i2c_common->suspend(i2c_common);
+       }
+
+       ret = pci_save_state(pci_dev);
+       if (ret) {
+               dev_err(ndev_dev(privdata),
+                       "pci_save_state failed = %d\n", ret);
+               return ret;
+       }
+
+       pci_disable_device(pci_dev);
+       return ret;
+}
+
+static int amd_mp2_pci_resume(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
+       struct amd_i2c_common *i2c_common;
+       unsigned int bus_id;
+       int ret = 0;
+
+       pci_restore_state(pci_dev);
+       ret = pci_enable_device(pci_dev);
+       if (ret < 0) {
+               dev_err(ndev_dev(privdata),
+                       "pci_enable_device failed = %d\n", ret);
+               return ret;
+       }
+
+       for (bus_id = 0; bus_id < 2; bus_id++) {
+               i2c_common = privdata->busses[bus_id];
+               if (i2c_common) {
+                       ret = i2c_common->resume(i2c_common);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
+static UNIVERSAL_DEV_PM_OPS(amd_mp2_pci_pm_ops, amd_mp2_pci_suspend,
+                           amd_mp2_pci_resume, NULL);
+#endif /* CONFIG_PM */
+
+static const struct pci_device_id amd_mp2_pci_tbl[] = {
+       {PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2)},
+       {0}
+};
+MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
+
+static struct pci_driver amd_mp2_pci_driver = {
+       .name           = "i2c_amd_mp2",
+       .id_table       = amd_mp2_pci_tbl,
+       .probe          = amd_mp2_pci_probe,
+       .remove         = amd_mp2_pci_remove,
+#ifdef CONFIG_PM
+       .driver = {
+               .pm     = &amd_mp2_pci_pm_ops,
+       },
+#endif
+};
+module_pci_driver(amd_mp2_pci_driver);
+
+static int amd_mp2_device_match(struct device *dev, void *data)
+{
+       return 1;
+}
+
+struct amd_mp2_dev *amd_mp2_find_device(void)
+{
+       struct device *dev;
+       struct pci_dev *pci_dev;
+
+       dev = driver_find_device(&amd_mp2_pci_driver.driver, NULL, NULL,
+                                amd_mp2_device_match);
+       if (!dev)
+               return NULL;
+
+       pci_dev = to_pci_dev(dev);
+       return (struct amd_mp2_dev *)pci_get_drvdata(pci_dev);
+}
+EXPORT_SYMBOL_GPL(amd_mp2_find_device);
+
+MODULE_DESCRIPTION("AMD(R) PCI-E MP2 I2C Controller Driver");
+MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
+MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c
new file mode 100644 (file)
index 0000000..f5b3f00
--- /dev/null
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * AMD MP2 platform driver
+ *
+ * Setup the I2C adapters enumerated in the ACPI namespace.
+ * MP2 controllers have 2 separate busses, up to 2 I2C adapters may be listed.
+ *
+ * Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
+ *          Elie Morisse <syniurge@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "i2c-amd-mp2.h"
+
+#define AMD_MP2_I2C_MAX_RW_LENGTH ((1 << 12) - 1)
+#define AMD_I2C_TIMEOUT (msecs_to_jiffies(250))
+
+/**
+ * struct amd_i2c_dev - MP2 bus/i2c adapter context
+ * @common: shared context with the MP2 PCI driver
+ * @pdev: platform driver node
+ * @adap: i2c adapter
+ * @cmd_complete: xfer completion object
+ */
+struct amd_i2c_dev {
+       struct amd_i2c_common common;
+       struct platform_device *pdev;
+       struct i2c_adapter adap;
+       struct completion cmd_complete;
+};
+
+#define amd_i2c_dev_common(__common) \
+       container_of(__common, struct amd_i2c_dev, common)
+
+static int i2c_amd_dma_map(struct amd_i2c_common *i2c_common)
+{
+       struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
+       struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+       enum dma_data_direction dma_direction =
+                       i2c_common->msg->flags & I2C_M_RD ?
+                       DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+       i2c_common->dma_buf = i2c_get_dma_safe_msg_buf(i2c_common->msg, 0);
+       i2c_common->dma_addr = dma_map_single(dev_pci, i2c_common->dma_buf,
+                                             i2c_common->msg->len,
+                                             dma_direction);
+
+       if (unlikely(dma_mapping_error(dev_pci, i2c_common->dma_addr))) {
+               dev_err(&i2c_dev->pdev->dev,
+                       "Error while mapping dma buffer %p\n",
+                       i2c_common->dma_buf);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void i2c_amd_dma_unmap(struct amd_i2c_common *i2c_common)
+{
+       struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
+       enum dma_data_direction dma_direction =
+                       i2c_common->msg->flags & I2C_M_RD ?
+                       DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+       dma_unmap_single(dev_pci, i2c_common->dma_addr,
+                        i2c_common->msg->len, dma_direction);
+
+       i2c_put_dma_safe_msg_buf(i2c_common->dma_buf, i2c_common->msg, true);
+}
+
+static void i2c_amd_start_cmd(struct amd_i2c_dev *i2c_dev)
+{
+       struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+       reinit_completion(&i2c_dev->cmd_complete);
+       i2c_common->cmd_success = false;
+}
+
+static void i2c_amd_cmd_completion(struct amd_i2c_common *i2c_common)
+{
+       struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+       union i2c_event *event = &i2c_common->eventval;
+
+       if (event->r.status == i2c_readcomplete_event)
+               dev_dbg(&i2c_dev->pdev->dev, "%s readdata:%*ph\n",
+                       __func__, event->r.length,
+                       i2c_common->msg->buf);
+
+       complete(&i2c_dev->cmd_complete);
+}
+
+static int i2c_amd_check_cmd_completion(struct amd_i2c_dev *i2c_dev)
+{
+       struct amd_i2c_common *i2c_common = &i2c_dev->common;
+       unsigned long timeout;
+
+       timeout = wait_for_completion_timeout(&i2c_dev->cmd_complete,
+                                             i2c_dev->adap.timeout);
+
+       if ((i2c_common->reqcmd == i2c_read ||
+            i2c_common->reqcmd == i2c_write) &&
+           i2c_common->msg->len > 32)
+               i2c_amd_dma_unmap(i2c_common);
+
+       if (timeout == 0) {
+               amd_mp2_rw_timeout(i2c_common);
+               return -ETIMEDOUT;
+       }
+
+       amd_mp2_process_event(i2c_common);
+
+       if (!i2c_common->cmd_success)
+               return -EIO;
+
+       return 0;
+}
+
+static int i2c_amd_enable_set(struct amd_i2c_dev *i2c_dev, bool enable)
+{
+       struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+       i2c_amd_start_cmd(i2c_dev);
+       amd_mp2_bus_enable_set(i2c_common, enable);
+
+       return i2c_amd_check_cmd_completion(i2c_dev);
+}
+
+static int i2c_amd_xfer_msg(struct amd_i2c_dev *i2c_dev, struct i2c_msg *pmsg)
+{
+       struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+       i2c_amd_start_cmd(i2c_dev);
+       i2c_common->msg = pmsg;
+
+       if (pmsg->len > 32)
+               if (i2c_amd_dma_map(i2c_common))
+                       return -EIO;
+
+       if (pmsg->flags & I2C_M_RD)
+               amd_mp2_rw(i2c_common, i2c_read);
+       else
+               amd_mp2_rw(i2c_common, i2c_write);
+
+       return i2c_amd_check_cmd_completion(i2c_dev);
+}
+
+static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct amd_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+       int i;
+       struct i2c_msg *pmsg;
+       int err;
+
+       /* the adapter might have been deleted while waiting for the bus lock */
+       if (unlikely(!i2c_dev->common.mp2_dev))
+               return -EINVAL;
+
+       amd_mp2_pm_runtime_get(i2c_dev->common.mp2_dev);
+
+       for (i = 0; i < num; i++) {
+               pmsg = &msgs[i];
+               err = i2c_amd_xfer_msg(i2c_dev, pmsg);
+               if (err)
+                       break;
+       }
+
+       amd_mp2_pm_runtime_put(i2c_dev->common.mp2_dev);
+       return err ? err : num;
+}
+
+static u32 i2c_amd_func(struct i2c_adapter *a)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm i2c_amd_algorithm = {
+       .master_xfer = i2c_amd_xfer,
+       .functionality = i2c_amd_func,
+};
+
+#ifdef CONFIG_PM
+static int i2c_amd_suspend(struct amd_i2c_common *i2c_common)
+{
+       struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+
+       i2c_amd_enable_set(i2c_dev, false);
+       return 0;
+}
+
+static int i2c_amd_resume(struct amd_i2c_common *i2c_common)
+{
+       struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
+
+       return i2c_amd_enable_set(i2c_dev, true);
+}
+#endif
+
+static enum speed_enum i2c_amd_get_bus_speed(struct platform_device *pdev)
+{
+       u32 acpi_speed;
+       int i;
+       static const u32 supported_speeds[] = {
+               0, 100000, 400000, 1000000, 1400000, 3400000
+       };
+
+       acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
+       /* round down to the lowest standard speed */
+       for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) {
+               if (acpi_speed < supported_speeds[i])
+                       break;
+       }
+       acpi_speed = supported_speeds[i - 1];
+
+       switch (acpi_speed) {
+       case 100000:
+               return speed100k;
+       case 400000:
+               return speed400k;
+       case 1000000:
+               return speed1000k;
+       case 1400000:
+               return speed1400k;
+       case 3400000:
+               return speed3400k;
+       default:
+               return speed400k;
+       }
+}
+
+static const struct i2c_adapter_quirks amd_i2c_dev_quirks = {
+       .max_read_len = AMD_MP2_I2C_MAX_RW_LENGTH,
+       .max_write_len = AMD_MP2_I2C_MAX_RW_LENGTH,
+};
+
+static int i2c_amd_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct amd_i2c_dev *i2c_dev;
+       acpi_handle handle = ACPI_HANDLE(&pdev->dev);
+       struct acpi_device *adev;
+       struct amd_mp2_dev *mp2_dev;
+       const char *uid;
+
+       if (acpi_bus_get_device(handle, &adev))
+               return -ENODEV;
+
+       /* The ACPI namespace doesn't contain information about which MP2 PCI
+        * device an AMDI0011 ACPI device is related to, so assume that there's
+        * only one MP2 PCI device per system.
+        */
+       mp2_dev = amd_mp2_find_device();
+       if (!mp2_dev || !mp2_dev->probed)
+               /* The MP2 PCI device should get probed later */
+               return -EPROBE_DEFER;
+
+       i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+       if (!i2c_dev)
+               return -ENOMEM;
+
+       i2c_dev->common.mp2_dev = mp2_dev;
+       i2c_dev->pdev = pdev;
+       platform_set_drvdata(pdev, i2c_dev);
+
+       i2c_dev->common.cmd_completion = &i2c_amd_cmd_completion;
+#ifdef CONFIG_PM
+       i2c_dev->common.suspend = &i2c_amd_suspend;
+       i2c_dev->common.resume = &i2c_amd_resume;
+#endif
+
+       uid = adev->pnp.unique_id;
+       if (!uid) {
+               dev_err(&pdev->dev, "missing UID/bus id!\n");
+               return -EINVAL;
+       } else if (strcmp(uid, "0") == 0) {
+               i2c_dev->common.bus_id = 0;
+       } else if (strcmp(uid, "1") == 0) {
+               i2c_dev->common.bus_id = 1;
+       } else {
+               dev_err(&pdev->dev, "incorrect UID/bus id \"%s\"!\n", uid);
+               return -EINVAL;
+       }
+       dev_dbg(&pdev->dev, "bus id is %u\n", i2c_dev->common.bus_id);
+
+       /* Register the adapter */
+       amd_mp2_pm_runtime_get(mp2_dev);
+
+       i2c_dev->common.reqcmd = i2c_none;
+       if (amd_mp2_register_cb(&i2c_dev->common))
+               return -EINVAL;
+       device_link_add(&i2c_dev->pdev->dev, &mp2_dev->pci_dev->dev,
+                       DL_FLAG_AUTOREMOVE_CONSUMER);
+
+       i2c_dev->common.i2c_speed = i2c_amd_get_bus_speed(pdev);
+
+       /* Setup i2c adapter description */
+       i2c_dev->adap.owner = THIS_MODULE;
+       i2c_dev->adap.algo = &i2c_amd_algorithm;
+       i2c_dev->adap.quirks = &amd_i2c_dev_quirks;
+       i2c_dev->adap.dev.parent = &pdev->dev;
+       i2c_dev->adap.algo_data = i2c_dev;
+       i2c_dev->adap.timeout = AMD_I2C_TIMEOUT;
+       ACPI_COMPANION_SET(&i2c_dev->adap.dev, ACPI_COMPANION(&pdev->dev));
+       i2c_dev->adap.dev.of_node = pdev->dev.of_node;
+       snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
+                "AMD MP2 i2c bus %u", i2c_dev->common.bus_id);
+       i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
+
+       init_completion(&i2c_dev->cmd_complete);
+
+       /* Enable the bus */
+       if (i2c_amd_enable_set(i2c_dev, true))
+               dev_err(&pdev->dev, "initial bus enable failed\n");
+
+       /* Attach to the i2c layer */
+       ret = i2c_add_adapter(&i2c_dev->adap);
+
+       amd_mp2_pm_runtime_put(mp2_dev);
+
+       if (ret < 0)
+               dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
+
+       return ret;
+}
+
+static int i2c_amd_remove(struct platform_device *pdev)
+{
+       struct amd_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+       struct amd_i2c_common *i2c_common = &i2c_dev->common;
+
+       i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
+
+       i2c_amd_enable_set(i2c_dev, false);
+       amd_mp2_unregister_cb(i2c_common);
+       i2c_common->mp2_dev = NULL;
+
+       i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
+
+       i2c_del_adapter(&i2c_dev->adap);
+       return 0;
+}
+
+static const struct acpi_device_id i2c_amd_acpi_match[] = {
+       { "AMDI0011" },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match);
+
+static struct platform_driver i2c_amd_plat_driver = {
+       .probe = i2c_amd_probe,
+       .remove = i2c_amd_remove,
+       .driver = {
+               .name = "i2c_amd_mp2",
+               .acpi_match_table = ACPI_PTR(i2c_amd_acpi_match),
+       },
+};
+module_platform_driver(i2c_amd_plat_driver);
+
+MODULE_DESCRIPTION("AMD(R) MP2 I2C Platform Driver");
+MODULE_AUTHOR("Nehal Shah <nehal-bakulchandra.shah@amd.com>");
+MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/i2c/busses/i2c-amd-mp2.h b/drivers/i2c/busses/i2c-amd-mp2.h
new file mode 100644 (file)
index 0000000..058362e
--- /dev/null
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * AMD MP2 I2C adapter driver
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ *          Elie Morisse <syniurge@gmail.com>
+ */
+
+#ifndef I2C_AMD_PCI_MP2_H
+#define I2C_AMD_PCI_MP2_H
+
+#include <linux/i2c.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+
+#define PCI_DEVICE_ID_AMD_MP2  0x15E6
+
+struct amd_i2c_common;
+struct amd_mp2_dev;
+
+enum {
+       /* MP2 C2P Message Registers */
+       AMD_C2P_MSG0 = 0x10500,                 /* MP2 Message for I2C0 */
+       AMD_C2P_MSG1 = 0x10504,                 /* MP2 Message for I2C1 */
+       AMD_C2P_MSG2 = 0x10508,                 /* DRAM Address Lo / Data 0 */
+       AMD_C2P_MSG3 = 0x1050c,                 /* DRAM Address HI / Data 1 */
+       AMD_C2P_MSG4 = 0x10510,                 /* Data 2 */
+       AMD_C2P_MSG5 = 0x10514,                 /* Data 3 */
+       AMD_C2P_MSG6 = 0x10518,                 /* Data 4 */
+       AMD_C2P_MSG7 = 0x1051c,                 /* Data 5 */
+       AMD_C2P_MSG8 = 0x10520,                 /* Data 6 */
+       AMD_C2P_MSG9 = 0x10524,                 /* Data 7 */
+
+       /* MP2 P2C Message Registers */
+       AMD_P2C_MSG0 = 0x10680,                 /* Do not use */
+       AMD_P2C_MSG1 = 0x10684,                 /* I2C0 interrupt register */
+       AMD_P2C_MSG2 = 0x10688,                 /* I2C1 interrupt register */
+       AMD_P2C_MSG3 = 0x1068C,                 /* MP2 debug info */
+       AMD_P2C_MSG_INTEN = 0x10690,            /* MP2 interrupt gen register */
+       AMD_P2C_MSG_INTSTS = 0x10694,           /* Interrupt status */
+};
+
+/* Command register data structures */
+
+#define i2c_none (-1)
+enum i2c_cmd {
+       i2c_read = 0,
+       i2c_write,
+       i2c_enable,
+       i2c_disable,
+       number_of_sensor_discovered,
+       is_mp2_active,
+       invalid_cmd = 0xF,
+};
+
+enum speed_enum {
+       speed100k = 0,
+       speed400k = 1,
+       speed1000k = 2,
+       speed1400k = 3,
+       speed3400k = 4
+};
+
+enum mem_type {
+       use_dram = 0,
+       use_c2pmsg = 1,
+};
+
+/**
+ * union i2c_cmd_base : bit access of C2P commands
+ * @i2c_cmd: bit 0..3 i2c R/W command
+ * @bus_id: bit 4..7 i2c bus index
+ * @slave_addr: bit 8..15 slave address
+ * @length: bit 16..27 read/write length
+ * @i2c_speed: bit 28..30 bus speed
+ * @mem_type: bit 31 0-DRAM; 1-C2P msg o/p
+ */
+union i2c_cmd_base {
+       u32 ul;
+       struct {
+               enum i2c_cmd i2c_cmd : 4;
+               u8 bus_id : 4;
+               u32 slave_addr : 8;
+               u32 length : 12;
+               enum speed_enum i2c_speed : 3;
+               enum mem_type mem_type : 1;
+       } s;
+};
+
+enum response_type {
+       invalid_response = 0,
+       command_success = 1,
+       command_failed = 2,
+};
+
+enum status_type {
+       i2c_readcomplete_event = 0,
+       i2c_readfail_event = 1,
+       i2c_writecomplete_event = 2,
+       i2c_writefail_event = 3,
+       i2c_busenable_complete = 4,
+       i2c_busenable_failed = 5,
+       i2c_busdisable_complete = 6,
+       i2c_busdisable_failed = 7,
+       invalid_data_length = 8,
+       invalid_slave_address = 9,
+       invalid_i2cbus_id = 10,
+       invalid_dram_addr = 11,
+       invalid_command = 12,
+       mp2_active = 13,
+       numberof_sensors_discovered_resp = 14,
+       i2c_bus_notinitialized
+};
+
+/**
+ * union i2c_event : bit access of P2C events
+ * @response: bit 0..1 i2c response type
+ * @status: bit 2..6 status_type
+ * @mem_type: bit 7 0-DRAM; 1-C2P msg o/p
+ * @bus_id: bit 8..11 i2c bus id
+ * @length: bit 12..23 message length
+ * @slave_addr: bit 24-31 slave address
+ */
+union i2c_event {
+       u32 ul;
+       struct {
+               enum response_type response : 2;
+               enum status_type status : 5;
+               enum mem_type mem_type : 1;
+               u8 bus_id : 4;
+               u32 length : 12;
+               u32 slave_addr : 8;
+       } r;
+};
+
+/**
+ * struct amd_i2c_common - per bus/i2c adapter context, shared
+ *     between the pci and the platform driver
+ * @eventval: MP2 event value set by the IRQ handler
+ * @mp2_dev: MP2 pci device this adapter is part of
+ * @msg: i2c message
+ * @cmd_completion: function called by the IRQ handler to signal
+ *                 the platform driver
+ * @reqcmd: requested i2c command type
+ * @cmd_success: set to true if the MP2 responded to a command with
+ *              the expected status and response type
+ * @bus_id: bus index
+ * @i2c_speed: i2c bus speed determined by the slowest slave
+ * @dma_buf: if msg length > 32, holds the DMA buffer virtual address
+ * @dma_addr: if msg length > 32, holds the DMA buffer address
+ */
+struct amd_i2c_common {
+       union i2c_event eventval;
+       struct amd_mp2_dev *mp2_dev;
+       struct i2c_msg *msg;
+       void (*cmd_completion)(struct amd_i2c_common *i2c_common);
+       enum i2c_cmd reqcmd;
+       u8 cmd_success;
+       u8 bus_id;
+       enum speed_enum i2c_speed;
+       u8 *dma_buf;
+       dma_addr_t dma_addr;
+#ifdef CONFIG_PM
+       int (*suspend)(struct amd_i2c_common *i2c_common);
+       int (*resume)(struct amd_i2c_common *i2c_common);
+#endif /* CONFIG_PM */
+};
+
+/**
+ * struct amd_mp2_dev - per PCI device context
+ * @pci_dev: PCI driver node
+ * @busses: MP2 devices may have up to two busses,
+ *         each bus corresponding to an i2c adapter
+ * @mmio: iommapped registers
+ * @c2p_lock: controls access to the C2P mailbox shared between
+ *           the two adapters
+ * @c2p_lock_busid: id of the adapter which locked c2p_lock
+ */
+struct amd_mp2_dev {
+       struct pci_dev *pci_dev;
+       struct amd_i2c_common *busses[2];
+       void __iomem *mmio;
+       struct mutex c2p_lock;
+       u8 c2p_lock_busid;
+       unsigned int probed;
+};
+
+#define ndev_pdev(ndev) ((ndev)->pci_dev)
+#define ndev_name(ndev) pci_name(ndev_pdev(ndev))
+#define ndev_dev(ndev) (&ndev_pdev(ndev)->dev)
+#define work_amd_i2c_common(__work) \
+       container_of(__work, struct amd_i2c_common, work.work)
+
+/* PCIe communication driver */
+
+int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd);
+int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable);
+
+void amd_mp2_process_event(struct amd_i2c_common *i2c_common);
+
+void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common);
+
+int amd_mp2_register_cb(struct amd_i2c_common *i2c_common);
+int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common);
+
+struct amd_mp2_dev *amd_mp2_find_device(void);
+
+static inline void amd_mp2_pm_runtime_get(struct amd_mp2_dev *mp2_dev)
+{
+       pm_runtime_get_sync(&mp2_dev->pci_dev->dev);
+}
+
+static inline void amd_mp2_pm_runtime_put(struct amd_mp2_dev *mp2_dev)
+{
+       pm_runtime_mark_last_busy(&mp2_dev->pci_dev->dev);
+       pm_runtime_put_autosuspend(&mp2_dev->pci_dev->dev);
+}
+
+#endif
diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c
new file mode 100644 (file)
index 0000000..8d55cdd
--- /dev/null
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ *  Copyright (C) 2011 Weinmann Medical GmbH
+ *  Author: Nikolaus Voss <n.voss@weinmann.de>
+ *
+ *  Evolved from original work by:
+ *  Copyright (C) 2004 Rick Bronson
+ *  Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
+ *
+ *  Borrowed heavily from original work by:
+ *  Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "i2c-at91.h"
+
+unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
+{
+       return readl_relaxed(dev->base + reg);
+}
+
+void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
+{
+       writel_relaxed(val, dev->base + reg);
+}
+
+void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
+{
+       at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
+}
+
+void at91_twi_irq_save(struct at91_twi_dev *dev)
+{
+       dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
+       at91_disable_twi_interrupts(dev);
+}
+
+void at91_twi_irq_restore(struct at91_twi_dev *dev)
+{
+       at91_twi_write(dev, AT91_TWI_IER, dev->imr);
+}
+
+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);
+       if (dev->slave_detected)
+               at91_init_twi_bus_slave(dev);
+       else
+               at91_init_twi_bus_master(dev);
+}
+
+static struct at91_twi_pdata at91rm9200_config = {
+       .clk_max_div = 5,
+       .clk_offset = 3,
+       .has_unre_flag = true,
+       .has_alt_cmd = false,
+       .has_hold_field = false,
+};
+
+static struct at91_twi_pdata at91sam9261_config = {
+       .clk_max_div = 5,
+       .clk_offset = 4,
+       .has_unre_flag = false,
+       .has_alt_cmd = false,
+       .has_hold_field = false,
+};
+
+static struct at91_twi_pdata at91sam9260_config = {
+       .clk_max_div = 7,
+       .clk_offset = 4,
+       .has_unre_flag = false,
+       .has_alt_cmd = false,
+       .has_hold_field = false,
+};
+
+static struct at91_twi_pdata at91sam9g20_config = {
+       .clk_max_div = 7,
+       .clk_offset = 4,
+       .has_unre_flag = false,
+       .has_alt_cmd = false,
+       .has_hold_field = false,
+};
+
+static struct at91_twi_pdata at91sam9g10_config = {
+       .clk_max_div = 7,
+       .clk_offset = 4,
+       .has_unre_flag = false,
+       .has_alt_cmd = false,
+       .has_hold_field = false,
+};
+
+static const struct platform_device_id at91_twi_devtypes[] = {
+       {
+               .name = "i2c-at91rm9200",
+               .driver_data = (unsigned long) &at91rm9200_config,
+       }, {
+               .name = "i2c-at91sam9261",
+               .driver_data = (unsigned long) &at91sam9261_config,
+       }, {
+               .name = "i2c-at91sam9260",
+               .driver_data = (unsigned long) &at91sam9260_config,
+       }, {
+               .name = "i2c-at91sam9g20",
+               .driver_data = (unsigned long) &at91sam9g20_config,
+       }, {
+               .name = "i2c-at91sam9g10",
+               .driver_data = (unsigned long) &at91sam9g10_config,
+       }, {
+               /* sentinel */
+       }
+};
+
+#if defined(CONFIG_OF)
+static struct at91_twi_pdata at91sam9x5_config = {
+       .clk_max_div = 7,
+       .clk_offset = 4,
+       .has_unre_flag = false,
+       .has_alt_cmd = false,
+       .has_hold_field = false,
+};
+
+static struct at91_twi_pdata sama5d4_config = {
+       .clk_max_div = 7,
+       .clk_offset = 4,
+       .has_unre_flag = false,
+       .has_alt_cmd = false,
+       .has_hold_field = true,
+};
+
+static struct at91_twi_pdata sama5d2_config = {
+       .clk_max_div = 7,
+       .clk_offset = 4,
+       .has_unre_flag = true,
+       .has_alt_cmd = true,
+       .has_hold_field = true,
+};
+
+static const struct of_device_id atmel_twi_dt_ids[] = {
+       {
+               .compatible = "atmel,at91rm9200-i2c",
+               .data = &at91rm9200_config,
+       }, {
+               .compatible = "atmel,at91sam9260-i2c",
+               .data = &at91sam9260_config,
+       }, {
+               .compatible = "atmel,at91sam9261-i2c",
+               .data = &at91sam9261_config,
+       }, {
+               .compatible = "atmel,at91sam9g20-i2c",
+               .data = &at91sam9g20_config,
+       }, {
+               .compatible = "atmel,at91sam9g10-i2c",
+               .data = &at91sam9g10_config,
+       }, {
+               .compatible = "atmel,at91sam9x5-i2c",
+               .data = &at91sam9x5_config,
+       }, {
+               .compatible = "atmel,sama5d4-i2c",
+               .data = &sama5d4_config,
+       }, {
+               .compatible = "atmel,sama5d2-i2c",
+               .data = &sama5d2_config,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
+#endif
+
+static struct at91_twi_pdata *at91_twi_get_driver_data(
+                                       struct platform_device *pdev)
+{
+       if (pdev->dev.of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
+               if (!match)
+                       return NULL;
+               return (struct at91_twi_pdata *)match->data;
+       }
+       return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
+}
+
+static int at91_twi_probe(struct platform_device *pdev)
+{
+       struct at91_twi_dev *dev;
+       struct resource *mem;
+       int rc;
+       u32 phy_addr;
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->dev = &pdev->dev;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem)
+               return -ENODEV;
+       phy_addr = mem->start;
+
+       dev->pdata = at91_twi_get_driver_data(pdev);
+       if (!dev->pdata)
+               return -ENODEV;
+
+       dev->base = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(dev->base))
+               return PTR_ERR(dev->base);
+
+       dev->irq = platform_get_irq(pdev, 0);
+       if (dev->irq < 0)
+               return dev->irq;
+
+       platform_set_drvdata(pdev, dev);
+
+       dev->clk = devm_clk_get(dev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               dev_err(dev->dev, "no clock defined\n");
+               return -ENODEV;
+       }
+       clk_prepare_enable(dev->clk);
+
+       snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
+       i2c_set_adapdata(&dev->adapter, dev);
+       dev->adapter.owner = THIS_MODULE;
+       dev->adapter.class = I2C_CLASS_DEPRECATED;
+       dev->adapter.dev.parent = dev->dev;
+       dev->adapter.nr = pdev->id;
+       dev->adapter.timeout = AT91_I2C_TIMEOUT;
+       dev->adapter.dev.of_node = pdev->dev.of_node;
+
+       dev->slave_detected = i2c_detect_slave_mode(&pdev->dev);
+
+       if (dev->slave_detected)
+               rc = at91_twi_probe_slave(pdev, phy_addr, dev);
+       else
+               rc = at91_twi_probe_master(pdev, phy_addr, dev);
+       if (rc)
+               return rc;
+
+       at91_init_twi_bus(dev);
+
+       pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
+       pm_runtime_use_autosuspend(dev->dev);
+       pm_runtime_set_active(dev->dev);
+       pm_runtime_enable(dev->dev);
+
+       rc = i2c_add_numbered_adapter(&dev->adapter);
+       if (rc) {
+               clk_disable_unprepare(dev->clk);
+
+               pm_runtime_disable(dev->dev);
+               pm_runtime_set_suspended(dev->dev);
+
+               return rc;
+       }
+
+       dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
+                at91_twi_read(dev, AT91_TWI_VER));
+       return 0;
+}
+
+static int at91_twi_remove(struct platform_device *pdev)
+{
+       struct at91_twi_dev *dev = platform_get_drvdata(pdev);
+
+       i2c_del_adapter(&dev->adapter);
+       clk_disable_unprepare(dev->clk);
+
+       pm_runtime_disable(dev->dev);
+       pm_runtime_set_suspended(dev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int at91_twi_runtime_suspend(struct device *dev)
+{
+       struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(twi_dev->clk);
+
+       pinctrl_pm_select_sleep_state(dev);
+
+       return 0;
+}
+
+static int at91_twi_runtime_resume(struct device *dev)
+{
+       struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
+
+       pinctrl_pm_select_default_state(dev);
+
+       return clk_prepare_enable(twi_dev->clk);
+}
+
+static int at91_twi_suspend_noirq(struct device *dev)
+{
+       if (!pm_runtime_status_suspended(dev))
+               at91_twi_runtime_suspend(dev);
+
+       return 0;
+}
+
+static int at91_twi_resume_noirq(struct device *dev)
+{
+       struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
+       int ret;
+
+       if (!pm_runtime_status_suspended(dev)) {
+               ret = at91_twi_runtime_resume(dev);
+               if (ret)
+                       return ret;
+       }
+
+       pm_runtime_mark_last_busy(dev);
+       pm_request_autosuspend(dev);
+
+       at91_init_twi_bus(twi_dev);
+
+       return 0;
+}
+
+static const struct dev_pm_ops at91_twi_pm = {
+       .suspend_noirq  = at91_twi_suspend_noirq,
+       .resume_noirq   = at91_twi_resume_noirq,
+       .runtime_suspend        = at91_twi_runtime_suspend,
+       .runtime_resume         = at91_twi_runtime_resume,
+};
+
+#define at91_twi_pm_ops (&at91_twi_pm)
+#else
+#define at91_twi_pm_ops NULL
+#endif
+
+static struct platform_driver at91_twi_driver = {
+       .probe          = at91_twi_probe,
+       .remove         = at91_twi_remove,
+       .id_table       = at91_twi_devtypes,
+       .driver         = {
+               .name   = "at91_i2c",
+               .of_match_table = of_match_ptr(atmel_twi_dt_ids),
+               .pm     = at91_twi_pm_ops,
+       },
+};
+
+static int __init at91_twi_init(void)
+{
+       return platform_driver_register(&at91_twi_driver);
+}
+
+static void __exit at91_twi_exit(void)
+{
+       platform_driver_unregister(&at91_twi_driver);
+}
+
+subsys_initcall(at91_twi_init);
+module_exit(at91_twi_exit);
+
+MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
+MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_i2c");
similarity index 65%
rename from drivers/i2c/busses/i2c-at91.c
rename to drivers/i2c/busses/i2c-at91-master.c
index 3f3e8b3bf5ff9df550991d18530fe45f41c870d3..e87232f2e70855c3c5a21b1028597b8fd298a114 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
  *
  *
  *  Borrowed heavily from original work by:
  *  Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
  */
 
 #include <linux/clk.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
-#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
-#include <linux/slab.h>
 #include <linux/platform_data/dma-atmel.h>
 #include <linux/pm_runtime.h>
-#include <linux/pinctrl/consumer.h>
-
-#define DEFAULT_TWI_CLK_HZ             100000          /* max 400 Kbits/s */
-#define AT91_I2C_TIMEOUT       msecs_to_jiffies(100)   /* transfer timeout */
-#define AT91_I2C_DMA_THRESHOLD 8                       /* enable DMA if transfer size is bigger than this threshold */
-#define AUTOSUSPEND_TIMEOUT            2000
-#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
-
-/* AT91 TWI register definitions */
-#define        AT91_TWI_CR             0x0000  /* Control Register */
-#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_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_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 */
-#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_CWGR_HOLD_MAX  0x1f
-#define        AT91_TWI_CWGR_HOLD(x)   (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
-
-#define        AT91_TWI_SR             0x0020  /* Status Register */
-#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_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)
-
-#define        AT91_TWI_IER            0x0024  /* Interrupt Enable Register */
-#define        AT91_TWI_IDR            0x0028  /* Interrupt Disable Register */
-#define        AT91_TWI_IMR            0x002c  /* Interrupt Mask Register */
-#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)
-
-#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 {
-       unsigned clk_max_div;
-       unsigned clk_offset;
-       bool has_unre_flag;
-       bool has_alt_cmd;
-       bool has_hold_field;
-       struct at_dma_slave dma_slave;
-};
-
-struct at91_twi_dma {
-       struct dma_chan *chan_rx;
-       struct dma_chan *chan_tx;
-       struct scatterlist sg[2];
-       struct dma_async_tx_descriptor *data_desc;
-       enum dma_data_direction direction;
-       bool buf_mapped;
-       bool xfer_in_progress;
-};
 
-struct at91_twi_dev {
-       struct device *dev;
-       void __iomem *base;
-       struct completion cmd_complete;
-       struct clk *clk;
-       u8 *buf;
-       size_t buf_len;
-       struct i2c_msg *msg;
-       int irq;
-       unsigned imr;
-       unsigned transfer_status;
-       struct i2c_adapter adapter;
-       unsigned twi_cwgr_reg;
-       struct at91_twi_pdata *pdata;
-       bool use_dma;
-       bool use_alt_cmd;
-       bool recv_len_abort;
-       u32 fifo_size;
-       struct at91_twi_dma dma;
-};
-
-static unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
-{
-       return readl_relaxed(dev->base + reg);
-}
-
-static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
-{
-       writel_relaxed(val, dev->base + reg);
-}
-
-static void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
-{
-       at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
-}
+#include "i2c-at91.h"
 
-static void at91_twi_irq_save(struct at91_twi_dev *dev)
+void at91_init_twi_bus_master(struct at91_twi_dev *dev)
 {
-       dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
-       at91_disable_twi_interrupts(dev);
-}
-
-static void at91_twi_irq_restore(struct at91_twi_dev *dev)
-{
-       at91_twi_write(dev, AT91_TWI_IER, dev->imr);
-}
-
-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);
@@ -190,16 +43,18 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
  * Calculate symmetric clock as stated in datasheet:
  * twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
  */
-static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
+static void at91_calc_twi_clock(struct at91_twi_dev *dev)
 {
        int ckdiv, cdiv, div, hold = 0;
        struct at91_twi_pdata *pdata = dev->pdata;
        int offset = pdata->clk_offset;
        int max_ckdiv = pdata->clk_max_div;
-       u32 twd_hold_time_ns = 0;
+       struct i2c_timings timings, *t = &timings;
+
+       i2c_parse_fw_timings(dev->dev, t, true);
 
        div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
-                                      2 * twi_clk) - offset);
+                                      2 * t->bus_freq_hz) - offset);
        ckdiv = fls(div >> 8);
        cdiv = div >> ckdiv;
 
@@ -211,15 +66,12 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
        }
 
        if (pdata->has_hold_field) {
-               of_property_read_u32(dev->dev->of_node, "i2c-sda-hold-time-ns",
-                                    &twd_hold_time_ns);
-
                /*
                 * hold time = HOLD + 3 x T_peripheral_clock
                 * Use clk rate in kHz to prevent overflows when computing
                 * hold.
                 */
-               hold = DIV_ROUND_UP(twd_hold_time_ns
+               hold = DIV_ROUND_UP(t->sda_hold_ns
                                    * (clk_get_rate(dev->clk) / 1000), 1000000);
                hold -= 3;
                if (hold < 0)
@@ -236,7 +88,7 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
                            | AT91_TWI_CWGR_HOLD(hold);
 
        dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
-               cdiv, ckdiv, hold, twd_hold_time_ns);
+               cdiv, ckdiv, hold, t->sda_hold_ns);
 }
 
 static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
@@ -833,124 +685,6 @@ static const struct i2c_algorithm at91_twi_algorithm = {
        .functionality  = at91_twi_func,
 };
 
-static struct at91_twi_pdata at91rm9200_config = {
-       .clk_max_div = 5,
-       .clk_offset = 3,
-       .has_unre_flag = true,
-       .has_alt_cmd = false,
-       .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9261_config = {
-       .clk_max_div = 5,
-       .clk_offset = 4,
-       .has_unre_flag = false,
-       .has_alt_cmd = false,
-       .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9260_config = {
-       .clk_max_div = 7,
-       .clk_offset = 4,
-       .has_unre_flag = false,
-       .has_alt_cmd = false,
-       .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9g20_config = {
-       .clk_max_div = 7,
-       .clk_offset = 4,
-       .has_unre_flag = false,
-       .has_alt_cmd = false,
-       .has_hold_field = false,
-};
-
-static struct at91_twi_pdata at91sam9g10_config = {
-       .clk_max_div = 7,
-       .clk_offset = 4,
-       .has_unre_flag = false,
-       .has_alt_cmd = false,
-       .has_hold_field = false,
-};
-
-static const struct platform_device_id at91_twi_devtypes[] = {
-       {
-               .name = "i2c-at91rm9200",
-               .driver_data = (unsigned long) &at91rm9200_config,
-       }, {
-               .name = "i2c-at91sam9261",
-               .driver_data = (unsigned long) &at91sam9261_config,
-       }, {
-               .name = "i2c-at91sam9260",
-               .driver_data = (unsigned long) &at91sam9260_config,
-       }, {
-               .name = "i2c-at91sam9g20",
-               .driver_data = (unsigned long) &at91sam9g20_config,
-       }, {
-               .name = "i2c-at91sam9g10",
-               .driver_data = (unsigned long) &at91sam9g10_config,
-       }, {
-               /* sentinel */
-       }
-};
-
-#if defined(CONFIG_OF)
-static struct at91_twi_pdata at91sam9x5_config = {
-       .clk_max_div = 7,
-       .clk_offset = 4,
-       .has_unre_flag = false,
-       .has_alt_cmd = false,
-       .has_hold_field = false,
-};
-
-static struct at91_twi_pdata sama5d4_config = {
-       .clk_max_div = 7,
-       .clk_offset = 4,
-       .has_unre_flag = false,
-       .has_alt_cmd = false,
-       .has_hold_field = true,
-};
-
-static struct at91_twi_pdata sama5d2_config = {
-       .clk_max_div = 7,
-       .clk_offset = 4,
-       .has_unre_flag = true,
-       .has_alt_cmd = true,
-       .has_hold_field = true,
-};
-
-static const struct of_device_id atmel_twi_dt_ids[] = {
-       {
-               .compatible = "atmel,at91rm9200-i2c",
-               .data = &at91rm9200_config,
-       } , {
-               .compatible = "atmel,at91sam9260-i2c",
-               .data = &at91sam9260_config,
-       } , {
-               .compatible = "atmel,at91sam9261-i2c",
-               .data = &at91sam9261_config,
-       } , {
-               .compatible = "atmel,at91sam9g20-i2c",
-               .data = &at91sam9g20_config,
-       } , {
-               .compatible = "atmel,at91sam9g10-i2c",
-               .data = &at91sam9g10_config,
-       }, {
-               .compatible = "atmel,at91sam9x5-i2c",
-               .data = &at91sam9x5_config,
-       }, {
-               .compatible = "atmel,sama5d4-i2c",
-               .data = &sama5d4_config,
-       }, {
-               .compatible = "atmel,sama5d2-i2c",
-               .data = &sama5d2_config,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
-#endif
-
 static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
 {
        int ret = 0;
@@ -1033,74 +767,24 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
        return ret;
 }
 
-static struct at91_twi_pdata *at91_twi_get_driver_data(
-                                       struct platform_device *pdev)
+int at91_twi_probe_master(struct platform_device *pdev,
+                         u32 phy_addr, struct at91_twi_dev *dev)
 {
-       if (pdev->dev.of_node) {
-               const struct of_device_id *match;
-               match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
-               if (!match)
-                       return NULL;
-               return (struct at91_twi_pdata *)match->data;
-       }
-       return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
-}
-
-static int at91_twi_probe(struct platform_device *pdev)
-{
-       struct at91_twi_dev *dev;
-       struct resource *mem;
        int rc;
-       u32 phy_addr;
-       u32 bus_clk_rate;
 
-       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
-       if (!dev)
-               return -ENOMEM;
        init_completion(&dev->cmd_complete);
-       dev->dev = &pdev->dev;
-
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem)
-               return -ENODEV;
-       phy_addr = mem->start;
-
-       dev->pdata = at91_twi_get_driver_data(pdev);
-       if (!dev->pdata)
-               return -ENODEV;
-
-       dev->base = devm_ioremap_resource(&pdev->dev, mem);
-       if (IS_ERR(dev->base))
-               return PTR_ERR(dev->base);
-
-       dev->irq = platform_get_irq(pdev, 0);
-       if (dev->irq < 0)
-               return dev->irq;
 
        rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0,
-                        dev_name(dev->dev), dev);
+                             dev_name(dev->dev), dev);
        if (rc) {
                dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
                return rc;
        }
 
-       platform_set_drvdata(pdev, dev);
-
-       dev->clk = devm_clk_get(dev->dev, NULL);
-       if (IS_ERR(dev->clk)) {
-               dev_err(dev->dev, "no clock defined\n");
-               return -ENODEV;
-       }
-       rc = clk_prepare_enable(dev->clk);
-       if (rc)
-               return rc;
-
        if (dev->dev->of_node) {
                rc = at91_twi_configure_dma(dev, phy_addr);
-               if (rc == -EPROBE_DEFER) {
-                       clk_disable_unprepare(dev->clk);
+               if (rc == -EPROBE_DEFER)
                        return rc;
-               }
        }
 
        if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
@@ -1108,144 +792,10 @@ static int at91_twi_probe(struct platform_device *pdev)
                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)
-               bus_clk_rate = DEFAULT_TWI_CLK_HZ;
-
-       at91_calc_twi_clock(dev, bus_clk_rate);
-       at91_init_twi_bus(dev);
+       at91_calc_twi_clock(dev);
 
-       snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
-       i2c_set_adapdata(&dev->adapter, dev);
-       dev->adapter.owner = THIS_MODULE;
-       dev->adapter.class = I2C_CLASS_DEPRECATED;
        dev->adapter.algo = &at91_twi_algorithm;
        dev->adapter.quirks = &at91_twi_quirks;
-       dev->adapter.dev.parent = dev->dev;
-       dev->adapter.nr = pdev->id;
-       dev->adapter.timeout = AT91_I2C_TIMEOUT;
-       dev->adapter.dev.of_node = pdev->dev.of_node;
-
-       pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
-       pm_runtime_use_autosuspend(dev->dev);
-       pm_runtime_set_active(dev->dev);
-       pm_runtime_enable(dev->dev);
-
-       rc = i2c_add_numbered_adapter(&dev->adapter);
-       if (rc) {
-               clk_disable_unprepare(dev->clk);
-
-               pm_runtime_disable(dev->dev);
-               pm_runtime_set_suspended(dev->dev);
-
-               return rc;
-       }
-
-       dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
-                at91_twi_read(dev, AT91_TWI_VER));
-       return 0;
-}
-
-static int at91_twi_remove(struct platform_device *pdev)
-{
-       struct at91_twi_dev *dev = platform_get_drvdata(pdev);
-
-       i2c_del_adapter(&dev->adapter);
-       clk_disable_unprepare(dev->clk);
-
-       pm_runtime_disable(dev->dev);
-       pm_runtime_set_suspended(dev->dev);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int at91_twi_runtime_suspend(struct device *dev)
-{
-       struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
-
-       clk_disable_unprepare(twi_dev->clk);
-
-       pinctrl_pm_select_sleep_state(dev);
-
-       return 0;
-}
-
-static int at91_twi_runtime_resume(struct device *dev)
-{
-       struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
-
-       pinctrl_pm_select_default_state(dev);
-
-       return clk_prepare_enable(twi_dev->clk);
-}
-
-static int at91_twi_suspend_noirq(struct device *dev)
-{
-       if (!pm_runtime_status_suspended(dev))
-               at91_twi_runtime_suspend(dev);
-
-       return 0;
-}
-
-static int at91_twi_resume_noirq(struct device *dev)
-{
-       struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
-       int ret;
-
-       if (!pm_runtime_status_suspended(dev)) {
-               ret = at91_twi_runtime_resume(dev);
-               if (ret)
-                       return ret;
-       }
-
-       pm_runtime_mark_last_busy(dev);
-       pm_request_autosuspend(dev);
-
-       at91_init_twi_bus(twi_dev);
 
        return 0;
 }
-
-static const struct dev_pm_ops at91_twi_pm = {
-       .suspend_noirq  = at91_twi_suspend_noirq,
-       .resume_noirq   = at91_twi_resume_noirq,
-       .runtime_suspend        = at91_twi_runtime_suspend,
-       .runtime_resume         = at91_twi_runtime_resume,
-};
-
-#define at91_twi_pm_ops (&at91_twi_pm)
-#else
-#define at91_twi_pm_ops NULL
-#endif
-
-static struct platform_driver at91_twi_driver = {
-       .probe          = at91_twi_probe,
-       .remove         = at91_twi_remove,
-       .id_table       = at91_twi_devtypes,
-       .driver         = {
-               .name   = "at91_i2c",
-               .of_match_table = of_match_ptr(atmel_twi_dt_ids),
-               .pm     = at91_twi_pm_ops,
-       },
-};
-
-static int __init at91_twi_init(void)
-{
-       return platform_driver_register(&at91_twi_driver);
-}
-
-static void __exit at91_twi_exit(void)
-{
-       platform_driver_unregister(&at91_twi_driver);
-}
-
-subsys_initcall(at91_twi_init);
-module_exit(at91_twi_exit);
-
-MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
-MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:at91_i2c");
diff --git a/drivers/i2c/busses/i2c-at91-slave.c b/drivers/i2c/busses/i2c-at91-slave.c
new file mode 100644 (file)
index 0000000..d6eeea5
--- /dev/null
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  i2c slave support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ *  Copyright (C) 2017 Juergen Fitschen <me@jue.yt>
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+
+#include "i2c-at91.h"
+
+static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id)
+{
+       struct at91_twi_dev *dev = dev_id;
+       const unsigned status = at91_twi_read(dev, AT91_TWI_SR);
+       const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR);
+       u8 value;
+
+       if (!irqstatus)
+               return IRQ_NONE;
+
+       /* slave address has been detected on I2C bus */
+       if (irqstatus & AT91_TWI_SVACC) {
+               if (status & AT91_TWI_SVREAD) {
+                       i2c_slave_event(dev->slave,
+                                       I2C_SLAVE_READ_REQUESTED, &value);
+                       writeb_relaxed(value, dev->base + AT91_TWI_THR);
+                       at91_twi_write(dev, AT91_TWI_IER,
+                                      AT91_TWI_TXRDY | AT91_TWI_EOSACC);
+               } else {
+                       i2c_slave_event(dev->slave,
+                                       I2C_SLAVE_WRITE_REQUESTED, &value);
+                       at91_twi_write(dev, AT91_TWI_IER,
+                                      AT91_TWI_RXRDY | AT91_TWI_EOSACC);
+               }
+               at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC);
+       }
+
+       /* byte transmitted to remote master */
+       if (irqstatus & AT91_TWI_TXRDY) {
+               i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value);
+               writeb_relaxed(value, dev->base + AT91_TWI_THR);
+       }
+
+       /* byte received from remote master */
+       if (irqstatus & AT91_TWI_RXRDY) {
+               value = readb_relaxed(dev->base + AT91_TWI_RHR);
+               i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+       }
+
+       /* master sent stop */
+       if (irqstatus & AT91_TWI_EOSACC) {
+               at91_twi_write(dev, AT91_TWI_IDR,
+                              AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC);
+               at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
+               i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int at91_reg_slave(struct i2c_client *slave)
+{
+       struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
+
+       if (dev->slave)
+               return -EBUSY;
+
+       if (slave->flags & I2C_CLIENT_TEN)
+               return -EAFNOSUPPORT;
+
+       /* Make sure twi_clk doesn't get turned off! */
+       pm_runtime_get_sync(dev->dev);
+
+       dev->slave = slave;
+       dev->smr = AT91_TWI_SMR_SADR(slave->addr);
+
+       at91_init_twi_bus(dev);
+       at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
+
+       dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr);
+
+       return 0;
+}
+
+static int at91_unreg_slave(struct i2c_client *slave)
+{
+       struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
+
+       WARN_ON(!dev->slave);
+
+       dev_info(dev->dev, "leaving slave mode\n");
+
+       dev->slave = NULL;
+       dev->smr = 0;
+
+       at91_init_twi_bus(dev);
+
+       pm_runtime_put(dev->dev);
+
+       return 0;
+}
+
+static u32 at91_twi_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_SLAVE | I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
+               | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm at91_twi_algorithm_slave = {
+       .reg_slave      = at91_reg_slave,
+       .unreg_slave    = at91_unreg_slave,
+       .functionality  = at91_twi_func,
+};
+
+int at91_twi_probe_slave(struct platform_device *pdev,
+                        u32 phy_addr, struct at91_twi_dev *dev)
+{
+       int rc;
+
+       rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave,
+                             0, dev_name(dev->dev), dev);
+       if (rc) {
+               dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
+               return rc;
+       }
+
+       dev->adapter.algo = &at91_twi_algorithm_slave;
+
+       return 0;
+}
+
+void at91_init_twi_bus_slave(struct at91_twi_dev *dev)
+{
+       at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS);
+       if (dev->slave_detected && dev->smr) {
+               at91_twi_write(dev, AT91_TWI_SMR, dev->smr);
+               at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN);
+       }
+}
diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h
new file mode 100644 (file)
index 0000000..499b506
--- /dev/null
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
+ *
+ *  Copyright (C) 2011 Weinmann Medical GmbH
+ *  Author: Nikolaus Voss <n.voss@weinmann.de>
+ *
+ *  Evolved from original work by:
+ *  Copyright (C) 2004 Rick Bronson
+ *  Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
+ *
+ *  Borrowed heavily from original work by:
+ *  Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/i2c.h>
+#include <linux/platform_data/dma-atmel.h>
+#include <linux/platform_device.h>
+
+#define AT91_I2C_TIMEOUT       msecs_to_jiffies(100)   /* transfer timeout */
+#define AT91_I2C_DMA_THRESHOLD 8                       /* enable DMA if transfer size is bigger than this threshold */
+#define AUTOSUSPEND_TIMEOUT            2000
+#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
+
+/* AT91 TWI register definitions */
+#define        AT91_TWI_CR             0x0000  /* Control Register */
+#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_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_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 */
+#define        AT91_TWI_MREAD          BIT(12) /* Master Read Direction */
+
+#define        AT91_TWI_SMR            0x0008  /* Slave Mode Register */
+#define        AT91_TWI_SMR_SADR_MAX   0x007f
+#define        AT91_TWI_SMR_SADR(x)    (((x) & AT91_TWI_SMR_SADR_MAX) << 16)
+
+#define        AT91_TWI_IADR           0x000c  /* Internal Address Register */
+
+#define        AT91_TWI_CWGR           0x0010  /* Clock Waveform Generator Reg */
+#define        AT91_TWI_CWGR_HOLD_MAX  0x1f
+#define        AT91_TWI_CWGR_HOLD(x)   (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
+
+#define        AT91_TWI_SR             0x0020  /* Status Register */
+#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_SVREAD         BIT(3)  /* Slave Read */
+#define        AT91_TWI_SVACC          BIT(4)  /* Slave Access */
+#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_EOSACC         BIT(11) /* End Of Slave Access */
+#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 \
+       | AT91_TWI_SVACC | AT91_TWI_EOSACC)
+
+#define        AT91_TWI_IER            0x0024  /* Interrupt Enable Register */
+#define        AT91_TWI_IDR            0x0028  /* Interrupt Disable Register */
+#define        AT91_TWI_IMR            0x002c  /* Interrupt Mask Register */
+#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)
+
+#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 {
+       unsigned clk_max_div;
+       unsigned clk_offset;
+       bool has_unre_flag;
+       bool has_alt_cmd;
+       bool has_hold_field;
+       struct at_dma_slave dma_slave;
+};
+
+struct at91_twi_dma {
+       struct dma_chan *chan_rx;
+       struct dma_chan *chan_tx;
+       struct scatterlist sg[2];
+       struct dma_async_tx_descriptor *data_desc;
+       enum dma_data_direction direction;
+       bool buf_mapped;
+       bool xfer_in_progress;
+};
+
+struct at91_twi_dev {
+       struct device *dev;
+       void __iomem *base;
+       struct completion cmd_complete;
+       struct clk *clk;
+       u8 *buf;
+       size_t buf_len;
+       struct i2c_msg *msg;
+       int irq;
+       unsigned imr;
+       unsigned transfer_status;
+       struct i2c_adapter adapter;
+       unsigned twi_cwgr_reg;
+       struct at91_twi_pdata *pdata;
+       bool use_dma;
+       bool use_alt_cmd;
+       bool recv_len_abort;
+       u32 fifo_size;
+       struct at91_twi_dma dma;
+       bool slave_detected;
+#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
+       unsigned smr;
+       struct i2c_client *slave;
+#endif
+};
+
+unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg);
+void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val);
+void at91_disable_twi_interrupts(struct at91_twi_dev *dev);
+void at91_twi_irq_save(struct at91_twi_dev *dev);
+void at91_twi_irq_restore(struct at91_twi_dev *dev);
+void at91_init_twi_bus(struct at91_twi_dev *dev);
+
+void at91_init_twi_bus_master(struct at91_twi_dev *dev);
+int at91_twi_probe_master(struct platform_device *pdev, u32 phy_addr,
+                         struct at91_twi_dev *dev);
+
+#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
+void at91_init_twi_bus_slave(struct at91_twi_dev *dev);
+int at91_twi_probe_slave(struct platform_device *pdev, u32 phy_addr,
+                        struct at91_twi_dev *dev);
+
+#else
+static inline void at91_init_twi_bus_slave(struct at91_twi_dev *dev) {}
+static inline int at91_twi_probe_slave(struct platform_device *pdev,
+                                      u32 phy_addr, struct at91_twi_dev *dev)
+{
+       return -EINVAL;
+}
+
+#endif
index bf564391091f3014bc7fb582cf96eeb666453252..1c7b41f45c83226e6ac025d5835f8a0d84d93546 100644 (file)
@@ -99,6 +99,7 @@
  * @adapter: core i2c abstraction
  * @i2c_clk: clock reference for i2c input clock
  * @bus_clk_rate: current i2c bus clock rate
+ * @last: a flag indicating is this is last message in transfer
  */
 struct axxia_i2c_dev {
        void __iomem *base;
@@ -112,6 +113,7 @@ struct axxia_i2c_dev {
        struct i2c_adapter adapter;
        struct clk *i2c_clk;
        u32 bus_clk_rate;
+       bool last;
 };
 
 static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask)
@@ -324,15 +326,14 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
                /* Stop completed */
                i2c_int_disable(idev, ~MST_STATUS_TSS);
                complete(&idev->msg_complete);
-       } else if (status & MST_STATUS_SNS) {
+       } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) {
                /* Transfer done */
-               i2c_int_disable(idev, ~MST_STATUS_TSS);
+               int mask = idev->last ? ~0 : ~MST_STATUS_TSS;
+
+               i2c_int_disable(idev, mask);
                if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len)
                        axxia_i2c_empty_rx_fifo(idev);
                complete(&idev->msg_complete);
-       } else if (status & MST_STATUS_SS) {
-               /* Auto/Sequence transfer done */
-               complete(&idev->msg_complete);
        } else if (status & MST_STATUS_TSS) {
                /* Transfer timeout */
                idev->msg_err = -ETIMEDOUT;
@@ -405,6 +406,7 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
        idev->msg_r = &msgs[1];
        idev->msg_xfrd = 0;
        idev->msg_xfrd_r = 0;
+       idev->last = true;
        axxia_i2c_fill_tx_fifo(idev);
 
        writel(CMD_SEQUENCE, idev->base + MST_COMMAND);
@@ -415,10 +417,6 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
        time_left = wait_for_completion_timeout(&idev->msg_complete,
                                                I2C_XFER_TIMEOUT);
 
-       i2c_int_disable(idev, int_mask);
-
-       axxia_i2c_empty_rx_fifo(idev);
-
        if (idev->msg_err == -ENXIO) {
                if (axxia_i2c_handle_seq_nak(idev))
                        axxia_i2c_init(idev);
@@ -438,9 +436,10 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
        return idev->msg_err;
 }
 
-static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
+static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg,
+                             bool last)
 {
-       u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS;
+       u32 int_mask = MST_STATUS_ERR;
        u32 rx_xfer, tx_xfer;
        unsigned long time_left;
        unsigned int wt_value;
@@ -449,6 +448,7 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
        idev->msg_r = msg;
        idev->msg_xfrd = 0;
        idev->msg_xfrd_r = 0;
+       idev->last = last;
        reinit_completion(&idev->msg_complete);
 
        axxia_i2c_set_addr(idev, msg);
@@ -478,8 +478,13 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
        if (idev->msg_err)
                goto out;
 
-       /* Start manual mode */
-       writel(CMD_MANUAL, idev->base + MST_COMMAND);
+       if (!last) {
+               writel(CMD_MANUAL, idev->base + MST_COMMAND);
+               int_mask |= MST_STATUS_SNS;
+       } else {
+               writel(CMD_AUTO, idev->base + MST_COMMAND);
+               int_mask |= MST_STATUS_SS;
+       }
 
        writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL);
 
@@ -507,28 +512,6 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
        return idev->msg_err;
 }
 
-static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
-{
-       u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC | MST_STATUS_TSS;
-       unsigned long time_left;
-
-       reinit_completion(&idev->msg_complete);
-
-       /* Issue stop */
-       writel(0xb, idev->base + MST_COMMAND);
-       i2c_int_enable(idev, int_mask);
-       time_left = wait_for_completion_timeout(&idev->msg_complete,
-                                             I2C_STOP_TIMEOUT);
-       i2c_int_disable(idev, int_mask);
-       if (time_left == 0)
-               return -ETIMEDOUT;
-
-       if (readl(idev->base + MST_COMMAND) & CMD_BUSY)
-               dev_warn(idev->dev, "busy after stop\n");
-
-       return 0;
-}
-
 /* This function checks if the msgs[] array contains messages compatible with
  * Sequence mode of operation. This mode assumes there will be exactly one
  * write of non-zero length followed by exactly one read of non-zero length,
@@ -558,9 +541,7 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        i2c_int_enable(idev, MST_STATUS_TSS);
 
        for (i = 0; ret == 0 && i < num; ++i)
-               ret = axxia_i2c_xfer_msg(idev, &msgs[i]);
-
-       axxia_i2c_stop(idev);
+               ret = axxia_i2c_xfer_msg(idev, &msgs[i], i == (num - 1));
 
        return ret ? : i;
 }
index 4c8c3bc4669c8b690ce78d7762a6364dc62e0ee5..a845b8decac8fe22ce5afd36a254b4de6f5eb1ad 100644 (file)
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
+#define IDM_CTRL_DIRECT_OFFSET       0x00
 #define CFG_OFFSET                   0x00
 #define CFG_RESET_SHIFT              31
 #define CFG_EN_SHIFT                 30
+#define CFG_SLAVE_ADDR_0_SHIFT       28
 #define CFG_M_RETRY_CNT_SHIFT        16
 #define CFG_M_RETRY_CNT_MASK         0x0f
 
 #define TIM_CFG_OFFSET               0x04
 #define TIM_CFG_MODE_400_SHIFT       31
+#define TIM_RAND_SLAVE_STRETCH_SHIFT      24
+#define TIM_RAND_SLAVE_STRETCH_MASK       0x7f
+#define TIM_PERIODIC_SLAVE_STRETCH_SHIFT  16
+#define TIM_PERIODIC_SLAVE_STRETCH_MASK   0x7f
+
+#define S_CFG_SMBUS_ADDR_OFFSET           0x08
+#define S_CFG_EN_NIC_SMB_ADDR3_SHIFT      31
+#define S_CFG_NIC_SMB_ADDR3_SHIFT         24
+#define S_CFG_NIC_SMB_ADDR3_MASK          0x7f
+#define S_CFG_EN_NIC_SMB_ADDR2_SHIFT      23
+#define S_CFG_NIC_SMB_ADDR2_SHIFT         16
+#define S_CFG_NIC_SMB_ADDR2_MASK          0x7f
+#define S_CFG_EN_NIC_SMB_ADDR1_SHIFT      15
+#define S_CFG_NIC_SMB_ADDR1_SHIFT         8
+#define S_CFG_NIC_SMB_ADDR1_MASK          0x7f
+#define S_CFG_EN_NIC_SMB_ADDR0_SHIFT      7
+#define S_CFG_NIC_SMB_ADDR0_SHIFT         0
+#define S_CFG_NIC_SMB_ADDR0_MASK          0x7f
 
 #define M_FIFO_CTRL_OFFSET           0x0c
 #define M_FIFO_RX_FLUSH_SHIFT        31
 #define M_FIFO_RX_THLD_SHIFT         8
 #define M_FIFO_RX_THLD_MASK          0x3f
 
+#define S_FIFO_CTRL_OFFSET           0x10
+#define S_FIFO_RX_FLUSH_SHIFT        31
+#define S_FIFO_TX_FLUSH_SHIFT        30
+#define S_FIFO_RX_CNT_SHIFT          16
+#define S_FIFO_RX_CNT_MASK           0x7f
+#define S_FIFO_RX_THLD_SHIFT         8
+#define S_FIFO_RX_THLD_MASK          0x3f
+
 #define M_CMD_OFFSET                 0x30
 #define M_CMD_START_BUSY_SHIFT       31
 #define M_CMD_STATUS_SHIFT           25
@@ -46,6 +75,8 @@
 #define M_CMD_STATUS_NACK_ADDR       0x2
 #define M_CMD_STATUS_NACK_DATA       0x3
 #define M_CMD_STATUS_TIMEOUT         0x4
+#define M_CMD_STATUS_FIFO_UNDERRUN   0x5
+#define M_CMD_STATUS_RX_FIFO_FULL    0x6
 #define M_CMD_PROTOCOL_SHIFT         9
 #define M_CMD_PROTOCOL_MASK          0xf
 #define M_CMD_PROTOCOL_BLK_WR        0x7
 #define M_CMD_RD_CNT_SHIFT           0
 #define M_CMD_RD_CNT_MASK            0xff
 
+#define S_CMD_OFFSET                 0x34
+#define S_CMD_START_BUSY_SHIFT       31
+#define S_CMD_STATUS_SHIFT           23
+#define S_CMD_STATUS_MASK            0x07
+#define S_CMD_STATUS_SUCCESS         0x0
+#define S_CMD_STATUS_TIMEOUT         0x5
+
 #define IE_OFFSET                    0x38
 #define IE_M_RX_FIFO_FULL_SHIFT      31
 #define IE_M_RX_THLD_SHIFT           30
 #define IE_M_START_BUSY_SHIFT        28
 #define IE_M_TX_UNDERRUN_SHIFT       27
+#define IE_S_RX_FIFO_FULL_SHIFT      26
+#define IE_S_RX_THLD_SHIFT           25
+#define IE_S_RX_EVENT_SHIFT          24
+#define IE_S_START_BUSY_SHIFT        23
+#define IE_S_TX_UNDERRUN_SHIFT       22
+#define IE_S_RD_EVENT_SHIFT          21
 
 #define IS_OFFSET                    0x3c
 #define IS_M_RX_FIFO_FULL_SHIFT      31
 #define IS_M_RX_THLD_SHIFT           30
 #define IS_M_START_BUSY_SHIFT        28
 #define IS_M_TX_UNDERRUN_SHIFT       27
+#define IS_S_RX_FIFO_FULL_SHIFT      26
+#define IS_S_RX_THLD_SHIFT           25
+#define IS_S_RX_EVENT_SHIFT          24
+#define IS_S_START_BUSY_SHIFT        23
+#define IS_S_TX_UNDERRUN_SHIFT       22
+#define IS_S_RD_EVENT_SHIFT          21
 
 #define M_TX_OFFSET                  0x40
 #define M_TX_WR_STATUS_SHIFT         31
 #define M_RX_DATA_SHIFT              0
 #define M_RX_DATA_MASK               0xff
 
+#define S_TX_OFFSET                  0x48
+#define S_TX_WR_STATUS_SHIFT         31
+#define S_TX_DATA_SHIFT              0
+#define S_TX_DATA_MASK               0xff
+
+#define S_RX_OFFSET                  0x4c
+#define S_RX_STATUS_SHIFT            30
+#define S_RX_STATUS_MASK             0x03
+#define S_RX_PEC_ERR_SHIFT           29
+#define S_RX_DATA_SHIFT              0
+#define S_RX_DATA_MASK               0xff
+
 #define I2C_TIMEOUT_MSEC             50000
 #define M_TX_RX_FIFO_SIZE            64
+#define M_RX_FIFO_MAX_THLD_VALUE     (M_TX_RX_FIFO_SIZE - 1)
+
+#define M_RX_MAX_READ_LEN            255
+#define M_RX_FIFO_THLD_VALUE         50
+
+#define IE_M_ALL_INTERRUPT_SHIFT     27
+#define IE_M_ALL_INTERRUPT_MASK      0x1e
+
+#define SLAVE_READ_WRITE_BIT_MASK    0x1
+#define SLAVE_READ_WRITE_BIT_SHIFT   0x1
+#define SLAVE_MAX_SIZE_TRANSACTION   64
+#define SLAVE_CLOCK_STRETCH_TIME     25
+
+#define IE_S_ALL_INTERRUPT_SHIFT     21
+#define IE_S_ALL_INTERRUPT_MASK      0x3f
+
+enum i2c_slave_read_status {
+       I2C_SLAVE_RX_FIFO_EMPTY = 0,
+       I2C_SLAVE_RX_START,
+       I2C_SLAVE_RX_DATA,
+       I2C_SLAVE_RX_END,
+};
+
+enum i2c_slave_xfer_dir {
+       I2C_SLAVE_DIR_READ = 0,
+       I2C_SLAVE_DIR_WRITE,
+       I2C_SLAVE_DIR_NONE,
+};
 
 enum bus_speed_index {
        I2C_SPD_100K = 0,
        I2C_SPD_400K,
 };
 
+enum bcm_iproc_i2c_type {
+       IPROC_I2C,
+       IPROC_I2C_NIC
+};
+
 struct bcm_iproc_i2c_dev {
        struct device *device;
+       enum bcm_iproc_i2c_type type;
        int irq;
 
        void __iomem *base;
+       void __iomem *idm_base;
+
+       u32 ape_addr_mask;
+
+       /* lock for indirect access through IDM */
+       spinlock_t idm_lock;
 
        struct i2c_adapter adapter;
        unsigned int bus_speed;
@@ -100,68 +202,332 @@ struct bcm_iproc_i2c_dev {
 
        struct i2c_msg *msg;
 
+       struct i2c_client *slave;
+       enum i2c_slave_xfer_dir xfer_dir;
+
        /* bytes that have been transferred */
        unsigned int tx_bytes;
+       /* bytes that have been read */
+       unsigned int rx_bytes;
+       unsigned int thld_bytes;
 };
 
 /*
  * Can be expanded in the future if more interrupt status bits are utilized
  */
-#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT))
+#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT)\
+               | BIT(IS_M_RX_THLD_SHIFT))
 
-static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
+#define ISR_MASK_SLAVE (BIT(IS_S_START_BUSY_SHIFT)\
+               | BIT(IS_S_RX_EVENT_SHIFT) | BIT(IS_S_RD_EVENT_SHIFT))
+
+static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave);
+static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave);
+static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
+                                        bool enable);
+
+static inline u32 iproc_i2c_rd_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
+                                  u32 offset)
 {
-       struct bcm_iproc_i2c_dev *iproc_i2c = data;
-       u32 status = readl(iproc_i2c->base + IS_OFFSET);
+       u32 val;
 
-       status &= ISR_MASK;
+       if (iproc_i2c->idm_base) {
+               spin_lock(&iproc_i2c->idm_lock);
+               writel(iproc_i2c->ape_addr_mask,
+                      iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET);
+               val = readl(iproc_i2c->base + offset);
+               spin_unlock(&iproc_i2c->idm_lock);
+       } else {
+               val = readl(iproc_i2c->base + offset);
+       }
 
-       if (!status)
-               return IRQ_NONE;
+       return val;
+}
 
-       /* TX FIFO is empty and we have more data to send */
-       if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) {
-               struct i2c_msg *msg = iproc_i2c->msg;
-               unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
-               unsigned int i;
-               u32 val;
-
-               /* can only fill up to the FIFO size */
-               tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
-               for (i = 0; i < tx_bytes; i++) {
-                       /* start from where we left over */
-                       unsigned int idx = iproc_i2c->tx_bytes + i;
+static inline void iproc_i2c_wr_reg(struct bcm_iproc_i2c_dev *iproc_i2c,
+                                   u32 offset, u32 val)
+{
+       if (iproc_i2c->idm_base) {
+               spin_lock(&iproc_i2c->idm_lock);
+               writel(iproc_i2c->ape_addr_mask,
+                      iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET);
+               writel(val, iproc_i2c->base + offset);
+               spin_unlock(&iproc_i2c->idm_lock);
+       } else {
+               writel(val, iproc_i2c->base + offset);
+       }
+}
 
-                       val = msg->buf[idx];
+static void bcm_iproc_i2c_slave_init(
+       struct bcm_iproc_i2c_dev *iproc_i2c, bool need_reset)
+{
+       u32 val;
 
-                       /* mark the last byte */
-                       if (idx == msg->len - 1) {
-                               u32 tmp;
+       if (need_reset) {
+               /* put controller in reset */
+               val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
+               val |= BIT(CFG_RESET_SHIFT);
+               iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
 
-                               val |= BIT(M_TX_WR_STATUS_SHIFT);
+               /* wait 100 usec per spec */
+               udelay(100);
+
+               /* bring controller out of reset */
+               val &= ~(BIT(CFG_RESET_SHIFT));
+               iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
+       }
+
+       /* flush TX/RX FIFOs */
+       val = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT));
+       iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val);
+
+       /* Maximum slave stretch time */
+       val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET);
+       val &= ~(TIM_RAND_SLAVE_STRETCH_MASK << TIM_RAND_SLAVE_STRETCH_SHIFT);
+       val |= (SLAVE_CLOCK_STRETCH_TIME << TIM_RAND_SLAVE_STRETCH_SHIFT);
+       iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val);
+
+       /* Configure the slave address */
+       val = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
+       val |= BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);
+       val &= ~(S_CFG_NIC_SMB_ADDR3_MASK << S_CFG_NIC_SMB_ADDR3_SHIFT);
+       val |= (iproc_i2c->slave->addr << S_CFG_NIC_SMB_ADDR3_SHIFT);
+       iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, val);
+
+       /* clear all pending slave interrupts */
+       iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE);
+
+       /* Enable interrupt register for any READ event */
+       val = BIT(IE_S_RD_EVENT_SHIFT);
+       /* Enable interrupt register to indicate a valid byte in receive fifo */
+       val |= BIT(IE_S_RX_EVENT_SHIFT);
+       /* Enable interrupt register for the Slave BUSY command */
+       val |= BIT(IE_S_START_BUSY_SHIFT);
+       iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
+
+       iproc_i2c->xfer_dir = I2C_SLAVE_DIR_NONE;
+}
+
+static void bcm_iproc_i2c_check_slave_status(
+       struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+       u32 val;
+
+       val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET);
+       val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK;
+
+       if (val == S_CMD_STATUS_TIMEOUT) {
+               dev_err(iproc_i2c->device, "slave random stretch time timeout\n");
+
+               /* re-initialize i2c for recovery */
+               bcm_iproc_i2c_enable_disable(iproc_i2c, false);
+               bcm_iproc_i2c_slave_init(iproc_i2c, true);
+               bcm_iproc_i2c_enable_disable(iproc_i2c, true);
+       }
+}
+
+static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
+                               u32 status)
+{
+       u8 value;
+       u32 val;
+       u32 rd_status;
+       u32 tmp;
+
+       /* Start of transaction. check address and populate the direction */
+       if (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_NONE) {
+               tmp = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
+               rd_status = (tmp >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK;
+               /* This condition checks whether the request is a new request */
+               if (((rd_status == I2C_SLAVE_RX_START) &&
+                       (status & BIT(IS_S_RX_EVENT_SHIFT))) ||
+                       ((rd_status == I2C_SLAVE_RX_END) &&
+                       (status & BIT(IS_S_RD_EVENT_SHIFT)))) {
+
+                       /* Last bit is W/R bit.
+                        * If 1 then its a read request(by master).
+                        */
+                       iproc_i2c->xfer_dir = tmp & SLAVE_READ_WRITE_BIT_MASK;
+                       if (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_WRITE)
+                               i2c_slave_event(iproc_i2c->slave,
+                                       I2C_SLAVE_READ_REQUESTED, &value);
+                       else
+                               i2c_slave_event(iproc_i2c->slave,
+                                       I2C_SLAVE_WRITE_REQUESTED, &value);
+               }
+       }
+
+       /* read request from master */
+       if ((status & BIT(IS_S_RD_EVENT_SHIFT)) &&
+               (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_WRITE)) {
+               i2c_slave_event(iproc_i2c->slave,
+                       I2C_SLAVE_READ_PROCESSED, &value);
+               iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value);
+
+               val = BIT(S_CMD_START_BUSY_SHIFT);
+               iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);
+       }
+
+       /* write request from master */
+       if ((status & BIT(IS_S_RX_EVENT_SHIFT)) &&
+               (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_READ)) {
+               val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
+               /* Its a write request by Master to Slave.
+                * We read data present in receive FIFO
+                */
+               value = (u8)((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
+               i2c_slave_event(iproc_i2c->slave,
+                       I2C_SLAVE_WRITE_RECEIVED, &value);
+
+               /* check the status for the last byte of the transaction */
+               rd_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK;
+               if (rd_status == I2C_SLAVE_RX_END)
+                       iproc_i2c->xfer_dir = I2C_SLAVE_DIR_NONE;
+
+               dev_dbg(iproc_i2c->device, "\nread value = 0x%x\n", value);
+       }
+
+       /* Stop */
+       if (status & BIT(IS_S_START_BUSY_SHIFT)) {
+               i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value);
+               iproc_i2c->xfer_dir = I2C_SLAVE_DIR_NONE;
+       }
+
+       /* clear interrupt status */
+       iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status);
+
+       bcm_iproc_i2c_check_slave_status(iproc_i2c);
+       return true;
+}
+
+static void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+       struct i2c_msg *msg = iproc_i2c->msg;
+
+       /* Read valid data from RX FIFO */
+       while (iproc_i2c->rx_bytes < msg->len) {
+               if (!((iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET) >> M_FIFO_RX_CNT_SHIFT)
+                     & M_FIFO_RX_CNT_MASK))
+                       break;
+
+               msg->buf[iproc_i2c->rx_bytes] =
+                       (iproc_i2c_rd_reg(iproc_i2c, M_RX_OFFSET) >>
+                       M_RX_DATA_SHIFT) & M_RX_DATA_MASK;
+               iproc_i2c->rx_bytes++;
+       }
+}
+
+static void bcm_iproc_i2c_send(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+       struct i2c_msg *msg = iproc_i2c->msg;
+       unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
+       unsigned int i;
+       u32 val;
+
+       /* can only fill up to the FIFO size */
+       tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
+       for (i = 0; i < tx_bytes; i++) {
+               /* start from where we left over */
+               unsigned int idx = iproc_i2c->tx_bytes + i;
+
+               val = msg->buf[idx];
+
+               /* mark the last byte */
+               if (idx == msg->len - 1) {
+                       val |= BIT(M_TX_WR_STATUS_SHIFT);
+
+                       if (iproc_i2c->irq) {
+                               u32 tmp;
 
                                /*
-                                * Since this is the last byte, we should
-                                * now disable TX FIFO underrun interrupt
+                                * Since this is the last byte, we should now
+                                * disable TX FIFO underrun interrupt
                                 */
-                               tmp = readl(iproc_i2c->base + IE_OFFSET);
+                               tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
                                tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT);
-                               writel(tmp, iproc_i2c->base + IE_OFFSET);
+                               iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET,
+                                                tmp);
                        }
+               }
+
+               /* load data into TX FIFO */
+               iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
+       }
 
-                       /* load data into TX FIFO */
-                       writel(val, iproc_i2c->base + M_TX_OFFSET);
+       /* update number of transferred bytes */
+       iproc_i2c->tx_bytes += tx_bytes;
+}
+
+static void bcm_iproc_i2c_read(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+       struct i2c_msg *msg = iproc_i2c->msg;
+       u32 bytes_left, val;
+
+       bcm_iproc_i2c_read_valid_bytes(iproc_i2c);
+       bytes_left = msg->len - iproc_i2c->rx_bytes;
+       if (bytes_left == 0) {
+               if (iproc_i2c->irq) {
+                       /* finished reading all data, disable rx thld event */
+                       val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+                       val &= ~BIT(IS_M_RX_THLD_SHIFT);
+                       iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
                }
-               /* update number of transferred bytes */
-               iproc_i2c->tx_bytes += tx_bytes;
+       } else if (bytes_left < iproc_i2c->thld_bytes) {
+               /* set bytes left as threshold */
+               val = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET);
+               val &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT);
+               val |= (bytes_left << M_FIFO_RX_THLD_SHIFT);
+               iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
+               iproc_i2c->thld_bytes = bytes_left;
        }
+       /*
+        * bytes_left >= iproc_i2c->thld_bytes,
+        * hence no need to change the THRESHOLD SET.
+        * It will remain as iproc_i2c->thld_bytes itself
+        */
+}
+
+static void bcm_iproc_i2c_process_m_event(struct bcm_iproc_i2c_dev *iproc_i2c,
+                                         u32 status)
+{
+       /* TX FIFO is empty and we have more data to send */
+       if (status & BIT(IS_M_TX_UNDERRUN_SHIFT))
+               bcm_iproc_i2c_send(iproc_i2c);
 
+       /* RX FIFO threshold is reached and data needs to be read out */
+       if (status & BIT(IS_M_RX_THLD_SHIFT))
+               bcm_iproc_i2c_read(iproc_i2c);
+
+       /* transfer is done */
        if (status & BIT(IS_M_START_BUSY_SHIFT)) {
                iproc_i2c->xfer_is_done = 1;
-               complete(&iproc_i2c->done);
+               if (iproc_i2c->irq)
+                       complete(&iproc_i2c->done);
        }
+}
 
-       writel(status, iproc_i2c->base + IS_OFFSET);
+static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
+{
+       struct bcm_iproc_i2c_dev *iproc_i2c = data;
+       u32 status = iproc_i2c_rd_reg(iproc_i2c, IS_OFFSET);
+       bool ret;
+       u32 sl_status = status & ISR_MASK_SLAVE;
+
+       if (sl_status) {
+               ret = bcm_iproc_i2c_slave_isr(iproc_i2c, sl_status);
+               if (ret)
+                       return IRQ_HANDLED;
+               else
+                       return IRQ_NONE;
+       }
+
+       status &= ISR_MASK;
+       if (!status)
+               return IRQ_NONE;
+
+       /* process all master based events */
+       bcm_iproc_i2c_process_m_event(iproc_i2c, status);
+       iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status);
 
        return IRQ_HANDLED;
 }
@@ -171,26 +537,29 @@ static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c)
        u32 val;
 
        /* put controller in reset */
-       val = readl(iproc_i2c->base + CFG_OFFSET);
-       val |= 1 << CFG_RESET_SHIFT;
-       val &= ~(1 << CFG_EN_SHIFT);
-       writel(val, iproc_i2c->base + CFG_OFFSET);
+       val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
+       val |= BIT(CFG_RESET_SHIFT);
+       val &= ~(BIT(CFG_EN_SHIFT));
+       iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
 
        /* wait 100 usec per spec */
        udelay(100);
 
        /* bring controller out of reset */
-       val &= ~(1 << CFG_RESET_SHIFT);
-       writel(val, iproc_i2c->base + CFG_OFFSET);
+       val &= ~(BIT(CFG_RESET_SHIFT));
+       iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
 
        /* flush TX/RX FIFOs and set RX FIFO threshold to zero */
-       val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT);
-       writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
+       val = (BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT));
+       iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
        /* disable all interrupts */
-       writel(0, iproc_i2c->base + IE_OFFSET);
+       val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+       val &= ~(IE_M_ALL_INTERRUPT_MASK <<
+                       IE_M_ALL_INTERRUPT_SHIFT);
+       iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
 
        /* clear all pending interrupts */
-       writel(0xffffffff, iproc_i2c->base + IS_OFFSET);
+       iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff);
 
        return 0;
 }
@@ -200,12 +569,12 @@ static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c,
 {
        u32 val;
 
-       val = readl(iproc_i2c->base + CFG_OFFSET);
+       val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
        if (enable)
                val |= BIT(CFG_EN_SHIFT);
        else
                val &= ~BIT(CFG_EN_SHIFT);
-       writel(val, iproc_i2c->base + CFG_OFFSET);
+       iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val);
 }
 
 static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
@@ -213,7 +582,7 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
 {
        u32 val;
 
-       val = readl(iproc_i2c->base + M_CMD_OFFSET);
+       val = iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET);
        val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK;
 
        switch (val) {
@@ -236,6 +605,14 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
                dev_dbg(iproc_i2c->device, "bus timeout\n");
                return -ETIMEDOUT;
 
+       case M_CMD_STATUS_FIFO_UNDERRUN:
+               dev_dbg(iproc_i2c->device, "FIFO under-run\n");
+               return -ENXIO;
+
+       case M_CMD_STATUS_RX_FIFO_FULL:
+               dev_dbg(iproc_i2c->device, "RX FIFO full\n");
+               return -ETIMEDOUT;
+
        default:
                dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val);
 
@@ -248,18 +625,76 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
        }
 }
 
+static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
+                                  struct i2c_msg *msg,
+                                  u32 cmd)
+{
+       unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
+       u32 val, status;
+       int ret;
+
+       iproc_i2c_wr_reg(iproc_i2c, M_CMD_OFFSET, cmd);
+
+       if (iproc_i2c->irq) {
+               time_left = wait_for_completion_timeout(&iproc_i2c->done,
+                                                       time_left);
+               /* disable all interrupts */
+               iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0);
+               /* read it back to flush the write */
+               iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+               /* make sure the interrupt handler isn't running */
+               synchronize_irq(iproc_i2c->irq);
+
+       } else { /* polling mode */
+               unsigned long timeout = jiffies + time_left;
+
+               do {
+                       status = iproc_i2c_rd_reg(iproc_i2c,
+                                                 IS_OFFSET) & ISR_MASK;
+                       bcm_iproc_i2c_process_m_event(iproc_i2c, status);
+                       iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status);
+
+                       if (time_after(jiffies, timeout)) {
+                               time_left = 0;
+                               break;
+                       }
+
+                       cpu_relax();
+                       cond_resched();
+               } while (!iproc_i2c->xfer_is_done);
+       }
+
+       if (!time_left && !iproc_i2c->xfer_is_done) {
+               dev_err(iproc_i2c->device, "transaction timed out\n");
+
+               /* flush both TX/RX FIFOs */
+               val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
+               iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
+               return -ETIMEDOUT;
+       }
+
+       ret = bcm_iproc_i2c_check_status(iproc_i2c, msg);
+       if (ret) {
+               /* flush both TX/RX FIFOs */
+               val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
+               iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
                                         struct i2c_msg *msg)
 {
-       int ret, i;
+       int i;
        u8 addr;
-       u32 val;
+       u32 val, tmp, val_intr_en;
        unsigned int tx_bytes;
-       unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
 
        /* check if bus is busy */
-       if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) &
-              BIT(M_CMD_START_BUSY_SHIFT))) {
+       if (!!(iproc_i2c_rd_reg(iproc_i2c,
+                               M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT))) {
                dev_warn(iproc_i2c->device, "bus is busy\n");
                return -EBUSY;
        }
@@ -268,7 +703,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
 
        /* format and load slave address into the TX FIFO */
        addr = i2c_8bit_addr_from_msg(msg);
-       writel(addr, iproc_i2c->base + M_TX_OFFSET);
+       iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, addr);
 
        /*
         * For a write transaction, load data into the TX FIFO. Only allow
@@ -282,15 +717,17 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
 
                        /* mark the last byte */
                        if (i == msg->len - 1)
-                               val |= 1 << M_TX_WR_STATUS_SHIFT;
+                               val |= BIT(M_TX_WR_STATUS_SHIFT);
 
-                       writel(val, iproc_i2c->base + M_TX_OFFSET);
+                       iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
                }
                iproc_i2c->tx_bytes = tx_bytes;
        }
 
        /* mark as incomplete before starting the transaction */
-       reinit_completion(&iproc_i2c->done);
+       if (iproc_i2c->irq)
+               reinit_completion(&iproc_i2c->done);
+
        iproc_i2c->xfer_is_done = 0;
 
        /*
@@ -298,7 +735,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
         * transaction is done, i.e., the internal start_busy bit, transitions
         * from 1 to 0.
         */
-       val = BIT(IE_M_START_BUSY_SHIFT);
+       val_intr_en = BIT(IE_M_START_BUSY_SHIFT);
 
        /*
         * If TX data size is larger than the TX FIFO, need to enable TX
@@ -307,9 +744,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
         */
        if (!(msg->flags & I2C_M_RD) &&
            msg->len > iproc_i2c->tx_bytes)
-               val |= BIT(IE_M_TX_UNDERRUN_SHIFT);
-
-       writel(val, iproc_i2c->base + IE_OFFSET);
+               val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
 
        /*
         * Now we can activate the transfer. For a read operation, specify the
@@ -317,54 +752,31 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
         */
        val = BIT(M_CMD_START_BUSY_SHIFT);
        if (msg->flags & I2C_M_RD) {
+               iproc_i2c->rx_bytes = 0;
+               if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
+                       iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
+               else
+                       iproc_i2c->thld_bytes = msg->len;
+
+               /* set threshold value */
+               tmp = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET);
+               tmp &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT);
+               tmp |= iproc_i2c->thld_bytes << M_FIFO_RX_THLD_SHIFT;
+               iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, tmp);
+
+               /* enable the RX threshold interrupt */
+               val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
+
                val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
                       (msg->len << M_CMD_RD_CNT_SHIFT);
        } else {
                val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
        }
-       writel(val, iproc_i2c->base + M_CMD_OFFSET);
-
-       time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left);
-
-       /* disable all interrupts */
-       writel(0, iproc_i2c->base + IE_OFFSET);
-       /* read it back to flush the write */
-       readl(iproc_i2c->base + IE_OFFSET);
-
-       /* make sure the interrupt handler isn't running */
-       synchronize_irq(iproc_i2c->irq);
 
-       if (!time_left && !iproc_i2c->xfer_is_done) {
-               dev_err(iproc_i2c->device, "transaction timed out\n");
+       if (iproc_i2c->irq)
+               iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val_intr_en);
 
-               /* flush FIFOs */
-               val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
-                     (1 << M_FIFO_TX_FLUSH_SHIFT);
-               writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
-               return -ETIMEDOUT;
-       }
-
-       ret = bcm_iproc_i2c_check_status(iproc_i2c, msg);
-       if (ret) {
-               /* flush both TX/RX FIFOs */
-               val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
-                     (1 << M_FIFO_TX_FLUSH_SHIFT);
-               writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
-               return ret;
-       }
-
-       /*
-        * For a read operation, we now need to load the data from FIFO
-        * into the memory buffer
-        */
-       if (msg->flags & I2C_M_RD) {
-               for (i = 0; i < msg->len; i++) {
-                       msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >>
-                                     M_RX_DATA_SHIFT) & M_RX_DATA_MASK;
-               }
-       }
-
-       return 0;
+       return bcm_iproc_i2c_xfer_wait(iproc_i2c, msg, val);
 }
 
 static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
@@ -387,17 +799,23 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
 
 static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
 {
-       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+       u32 val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+
+       if (adap->algo->reg_slave)
+               val |= I2C_FUNC_SLAVE;
+
+       return val;
 }
 
-static const struct i2c_algorithm bcm_iproc_algo = {
+static struct i2c_algorithm bcm_iproc_algo = {
        .master_xfer = bcm_iproc_i2c_xfer,
        .functionality = bcm_iproc_i2c_functionality,
+       .reg_slave = bcm_iproc_i2c_reg_slave,
+       .unreg_slave = bcm_iproc_i2c_unreg_slave,
 };
 
-static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
-       /* need to reserve one byte in the FIFO for the slave address */
-       .max_read_len = M_TX_RX_FIFO_SIZE - 1,
+static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
+       .max_read_len = M_RX_MAX_READ_LEN,
 };
 
 static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
@@ -425,10 +843,10 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
        }
 
        iproc_i2c->bus_speed = bus_speed;
-       val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
-       val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
+       val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET);
+       val &= ~BIT(TIM_CFG_MODE_400_SHIFT);
        val |= (bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT;
-       writel(val, iproc_i2c->base + TIM_CFG_OFFSET);
+       iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val);
 
        dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed);
 
@@ -449,6 +867,8 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, iproc_i2c);
        iproc_i2c->device = &pdev->dev;
+       iproc_i2c->type =
+               (enum bcm_iproc_i2c_type)of_device_get_match_data(&pdev->dev);
        init_completion(&iproc_i2c->done);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -456,6 +876,29 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
        if (IS_ERR(iproc_i2c->base))
                return PTR_ERR(iproc_i2c->base);
 
+       if (iproc_i2c->type == IPROC_I2C_NIC) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+               iproc_i2c->idm_base = devm_ioremap_resource(iproc_i2c->device,
+                                                           res);
+               if (IS_ERR(iproc_i2c->idm_base))
+                       return PTR_ERR(iproc_i2c->idm_base);
+
+               ret = of_property_read_u32(iproc_i2c->device->of_node,
+                                          "brcm,ape-hsls-addr-mask",
+                                          &iproc_i2c->ape_addr_mask);
+               if (ret < 0) {
+                       dev_err(iproc_i2c->device,
+                               "'brcm,ape-hsls-addr-mask' missing\n");
+                       return -EINVAL;
+               }
+
+               spin_lock_init(&iproc_i2c->idm_lock);
+
+               /* no slave support */
+               bcm_iproc_algo.reg_slave = NULL;
+               bcm_iproc_algo.unreg_slave = NULL;
+       }
+
        ret = bcm_iproc_i2c_init(iproc_i2c);
        if (ret)
                return ret;
@@ -465,17 +908,20 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
                return ret;
 
        irq = platform_get_irq(pdev, 0);
-       if (irq <= 0) {
-               dev_err(iproc_i2c->device, "no irq resource\n");
-               return irq;
-       }
-       iproc_i2c->irq = irq;
+       if (irq > 0) {
+               ret = devm_request_irq(iproc_i2c->device, irq,
+                                      bcm_iproc_i2c_isr, 0, pdev->name,
+                                      iproc_i2c);
+               if (ret < 0) {
+                       dev_err(iproc_i2c->device,
+                               "unable to request irq %i\n", irq);
+                       return ret;
+               }
 
-       ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0,
-                              pdev->name, iproc_i2c);
-       if (ret < 0) {
-               dev_err(iproc_i2c->device, "unable to request irq %i\n", irq);
-               return ret;
+               iproc_i2c->irq = irq;
+       } else {
+               dev_warn(iproc_i2c->device,
+                        "no irq resource, falling back to poll mode\n");
        }
 
        bcm_iproc_i2c_enable_disable(iproc_i2c, true);
@@ -495,10 +941,15 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev)
 {
        struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev);
 
-       /* make sure there's no pending interrupt when we remove the adapter */
-       writel(0, iproc_i2c->base + IE_OFFSET);
-       readl(iproc_i2c->base + IE_OFFSET);
-       synchronize_irq(iproc_i2c->irq);
+       if (iproc_i2c->irq) {
+               /*
+                * Make sure there's no pending interrupt when we remove the
+                * adapter
+                */
+               iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0);
+               iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+               synchronize_irq(iproc_i2c->irq);
+       }
 
        i2c_del_adapter(&iproc_i2c->adapter);
        bcm_iproc_i2c_enable_disable(iproc_i2c, false);
@@ -512,10 +963,15 @@ static int bcm_iproc_i2c_suspend(struct device *dev)
 {
        struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev);
 
-       /* 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);
+       if (iproc_i2c->irq) {
+               /*
+                * Make sure there's no pending interrupt when we go into
+                * suspend
+                */
+               iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0);
+               iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+               synchronize_irq(iproc_i2c->irq);
+       }
 
        /* now disable the controller */
        bcm_iproc_i2c_enable_disable(iproc_i2c, false);
@@ -538,10 +994,10 @@ static int bcm_iproc_i2c_resume(struct device *dev)
                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_rd_reg(iproc_i2c, TIM_CFG_OFFSET);
+       val &= ~BIT(TIM_CFG_MODE_400_SHIFT);
        val |= (iproc_i2c->bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT;
-       writel(val, iproc_i2c->base + TIM_CFG_OFFSET);
+       iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val);
 
        bcm_iproc_i2c_enable_disable(iproc_i2c, true);
 
@@ -558,8 +1014,54 @@ static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = {
 #define BCM_IPROC_I2C_PM_OPS NULL
 #endif /* CONFIG_PM_SLEEP */
 
+
+static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave)
+{
+       struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
+
+       if (iproc_i2c->slave)
+               return -EBUSY;
+
+       if (slave->flags & I2C_CLIENT_TEN)
+               return -EAFNOSUPPORT;
+
+       iproc_i2c->slave = slave;
+       bcm_iproc_i2c_slave_init(iproc_i2c, false);
+       return 0;
+}
+
+static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave)
+{
+       u32 tmp;
+       struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter);
+
+       if (!iproc_i2c->slave)
+               return -EINVAL;
+
+       iproc_i2c->slave = NULL;
+
+       /* disable all slave interrupts */
+       tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
+       tmp &= ~(IE_S_ALL_INTERRUPT_MASK <<
+                       IE_S_ALL_INTERRUPT_SHIFT);
+       iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp);
+
+       /* Erase the slave address programmed */
+       tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
+       tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);
+       iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, tmp);
+
+       return 0;
+}
+
 static const struct of_device_id bcm_iproc_i2c_of_match[] = {
-       { .compatible = "brcm,iproc-i2c" },
+       {
+               .compatible = "brcm,iproc-i2c",
+               .data = (int *)IPROC_I2C,
+       }, {
+               .compatible = "brcm,iproc-nic-i2c",
+               .data = (int *)IPROC_I2C_NIC,
+       },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match);
index f4d862234980b58a5fc36dc65acff6adc4cdbf38..506991596b68d59d2ffc5d44dcd8756364bc8eab 100644 (file)
@@ -165,7 +165,6 @@ static const struct bsc_clk_param bsc_clk[] = {
 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;
index a4730111d290317b246e29b410a82f2ec20630d4..2de7452fcd6d70117931b0fe36f807a76742d77b 100644 (file)
@@ -251,13 +251,27 @@ unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
 
 int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare)
 {
+       int ret;
+
        if (IS_ERR(dev->clk))
                return PTR_ERR(dev->clk);
 
-       if (prepare)
-               return clk_prepare_enable(dev->clk);
+       if (prepare) {
+               /* Optional interface clock */
+               ret = clk_prepare_enable(dev->pclk);
+               if (ret)
+                       return ret;
+
+               ret = clk_prepare_enable(dev->clk);
+               if (ret)
+                       clk_disable_unprepare(dev->pclk);
+
+               return ret;
+       }
 
        clk_disable_unprepare(dev->clk);
+       clk_disable_unprepare(dev->pclk);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(i2c_dw_prepare_clk);
index 6b4ef1d38fb29c5fb80c523f3f8e89827bbee9e9..67edbbde1070a1efd5794da9cbce77b827a2a0f3 100644 (file)
  * @base: IO registers pointer
  * @cmd_complete: tx completion indicator
  * @clk: input reference clock
+ * @pclk: clock required to access the registers
  * @slave: represent an I2C slave device
  * @cmd_err: run time hadware error code
  * @msgs: points to an array of messages currently being transferred
@@ -227,6 +228,7 @@ struct dw_i2c_dev {
        void __iomem            *ext;
        struct completion       cmd_complete;
        struct clk              *clk;
+       struct clk              *pclk;
        struct reset_control    *rst;
        struct i2c_client               *slave;
        u32                     (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
index 416f89b8f8812598abcb73f44430e089f94b8c51..ddfb8187290662e60ba335e279a7466d8379e7d1 100644 (file)
@@ -344,6 +344,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
        else
                i2c_dw_configure_master(dev);
 
+       /* Optional interface clock */
+       dev->pclk = devm_clk_get_optional(&pdev->dev, "pclk");
+       if (IS_ERR(dev->pclk))
+               return PTR_ERR(dev->pclk);
+
        dev->clk = devm_clk_get(&pdev->dev, NULL);
        if (!i2c_dw_prepare_clk(dev, true)) {
                u64 clk_khz;
index bba5c4627de3c5818fed727299f76f06bcd19bff..9684a0ac2a6d968b590a76ff4ce350b0ebd9fce5 100644 (file)
@@ -413,6 +413,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
 
        if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl))
                dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing");
+       else
+               bit_data->can_do_atomic = true;
 
        bit_data->setsda = i2c_gpio_setsda_val;
        bit_data->setscl = i2c_gpio_setscl_val;
index 5c754bf659e2801df5fc72a2d805dba58a53d171..f64c1d72f73f76f208ca2836fd23ca0f5b41429d 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/ioport.h>
 #include <linux/i2c.h>
 #include <linux/io.h>
-#include <linux/acpi.h>
 
 /* SCH SMBus address offsets */
 #define SMBHSTCNT      (0 + sch_smba)
index 684d651612b3066a46a16e5fdcd0390bd513fac0..745b0d03e88358d0be41b1901c8c28b825d229d8 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/slab.h>
 
 #define I2C_RS_TRANSFER                        (1 << 4)
+#define I2C_ARB_LOST                   (1 << 3)
 #define I2C_HS_NACKERR                 (1 << 2)
 #define I2C_ACKERR                     (1 << 1)
 #define I2C_TRANSAC_COMP               (1 << 0)
@@ -76,6 +77,8 @@
 #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_DMAACK_EN           (0x1 << 8)
+#define I2C_CONTROL_ASYNC_MODE          (0x1 << 9)
 #define I2C_CONTROL_WRAPPER             (0x1 << 0)
 
 #define I2C_DRV_NAME           "i2c-mt65xx"
@@ -106,40 +109,97 @@ enum mtk_trans_op {
 };
 
 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,
-       OFFSET_CLOCK_DIV = 0x70,
+       OFFSET_DATA_PORT,
+       OFFSET_SLAVE_ADDR,
+       OFFSET_INTR_MASK,
+       OFFSET_INTR_STAT,
+       OFFSET_CONTROL,
+       OFFSET_TRANSFER_LEN,
+       OFFSET_TRANSAC_LEN,
+       OFFSET_DELAY_LEN,
+       OFFSET_TIMING,
+       OFFSET_START,
+       OFFSET_EXT_CONF,
+       OFFSET_FIFO_STAT,
+       OFFSET_FIFO_THRESH,
+       OFFSET_FIFO_ADDR_CLR,
+       OFFSET_IO_CONFIG,
+       OFFSET_RSV_DEBUG,
+       OFFSET_HS,
+       OFFSET_SOFTRESET,
+       OFFSET_DCM_EN,
+       OFFSET_PATH_DIR,
+       OFFSET_DEBUGSTAT,
+       OFFSET_DEBUGCTRL,
+       OFFSET_TRANSFER_LEN_AUX,
+       OFFSET_CLOCK_DIV,
+       OFFSET_LTIMING,
+};
+
+static const u16 mt_i2c_regs_v1[] = {
+       [OFFSET_DATA_PORT] = 0x0,
+       [OFFSET_SLAVE_ADDR] = 0x4,
+       [OFFSET_INTR_MASK] = 0x8,
+       [OFFSET_INTR_STAT] = 0xc,
+       [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,
+       [OFFSET_CLOCK_DIV] = 0x70,
+};
+
+static const u16 mt_i2c_regs_v2[] = {
+       [OFFSET_DATA_PORT] = 0x0,
+       [OFFSET_SLAVE_ADDR] = 0x4,
+       [OFFSET_INTR_MASK] = 0x8,
+       [OFFSET_INTR_STAT] = 0xc,
+       [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_LTIMING] = 0x2c,
+       [OFFSET_HS] = 0x30,
+       [OFFSET_IO_CONFIG] = 0x34,
+       [OFFSET_FIFO_ADDR_CLR] = 0x38,
+       [OFFSET_TRANSFER_LEN_AUX] = 0x44,
+       [OFFSET_CLOCK_DIV] = 0x48,
+       [OFFSET_SOFTRESET] = 0x50,
+       [OFFSET_DEBUGSTAT] = 0xe0,
+       [OFFSET_DEBUGCTRL] = 0xe8,
+       [OFFSET_FIFO_STAT] = 0xf4,
+       [OFFSET_FIFO_THRESH] = 0xf8,
+       [OFFSET_DCM_EN] = 0xf88,
 };
 
 struct mtk_i2c_compatible {
        const struct i2c_adapter_quirks *quirks;
+       const u16 *regs;
        unsigned char pmic_i2c: 1;
        unsigned char dcm: 1;
        unsigned char auto_restart: 1;
        unsigned char aux_len_reg: 1;
        unsigned char support_33bits: 1;
        unsigned char timing_adjust: 1;
+       unsigned char dma_sync: 1;
+       unsigned char ltiming_adjust: 1;
 };
 
 struct mtk_i2c {
@@ -153,6 +213,7 @@ struct mtk_i2c {
        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 */
+       struct clk *clk_arb;            /* Arbitrator clock for i2c */
        bool have_pmic;                 /* can use i2c pins from PMIC */
        bool use_push_pull;             /* IO config push-pull mode */
 
@@ -162,6 +223,7 @@ struct mtk_i2c {
        enum mtk_trans_op op;
        u16 timing_reg;
        u16 high_speed_reg;
+       u16 ltiming_reg;
        unsigned char auto_restart;
        bool ignore_restart_irq;
        const struct mtk_i2c_compatible *dev_comp;
@@ -181,51 +243,78 @@ static const struct i2c_adapter_quirks mt7622_i2c_quirks = {
 };
 
 static const struct mtk_i2c_compatible mt2712_compat = {
+       .regs = mt_i2c_regs_v1,
        .pmic_i2c = 0,
        .dcm = 1,
        .auto_restart = 1,
        .aux_len_reg = 1,
        .support_33bits = 1,
        .timing_adjust = 1,
+       .dma_sync = 0,
+       .ltiming_adjust = 0,
 };
 
 static const struct mtk_i2c_compatible mt6577_compat = {
        .quirks = &mt6577_i2c_quirks,
+       .regs = mt_i2c_regs_v1,
        .pmic_i2c = 0,
        .dcm = 1,
        .auto_restart = 0,
        .aux_len_reg = 0,
        .support_33bits = 0,
        .timing_adjust = 0,
+       .dma_sync = 0,
+       .ltiming_adjust = 0,
 };
 
 static const struct mtk_i2c_compatible mt6589_compat = {
        .quirks = &mt6577_i2c_quirks,
+       .regs = mt_i2c_regs_v1,
        .pmic_i2c = 1,
        .dcm = 0,
        .auto_restart = 0,
        .aux_len_reg = 0,
        .support_33bits = 0,
        .timing_adjust = 0,
+       .dma_sync = 0,
+       .ltiming_adjust = 0,
 };
 
 static const struct mtk_i2c_compatible mt7622_compat = {
        .quirks = &mt7622_i2c_quirks,
+       .regs = mt_i2c_regs_v1,
        .pmic_i2c = 0,
        .dcm = 1,
        .auto_restart = 1,
        .aux_len_reg = 1,
        .support_33bits = 0,
        .timing_adjust = 0,
+       .dma_sync = 0,
+       .ltiming_adjust = 0,
 };
 
 static const struct mtk_i2c_compatible mt8173_compat = {
+       .regs = mt_i2c_regs_v1,
        .pmic_i2c = 0,
        .dcm = 1,
        .auto_restart = 1,
        .aux_len_reg = 1,
        .support_33bits = 1,
        .timing_adjust = 0,
+       .dma_sync = 0,
+       .ltiming_adjust = 0,
+};
+
+static const struct mtk_i2c_compatible mt8183_compat = {
+       .regs = mt_i2c_regs_v2,
+       .pmic_i2c = 0,
+       .dcm = 0,
+       .auto_restart = 1,
+       .aux_len_reg = 1,
+       .support_33bits = 1,
+       .timing_adjust = 1,
+       .dma_sync = 1,
+       .ltiming_adjust = 1,
 };
 
 static const struct of_device_id mtk_i2c_of_match[] = {
@@ -234,10 +323,22 @@ static const struct of_device_id mtk_i2c_of_match[] = {
        { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
        { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
        { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
+       { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
        {}
 };
 MODULE_DEVICE_TABLE(of, mtk_i2c_of_match);
 
+static u16 mtk_i2c_readw(struct mtk_i2c *i2c, enum I2C_REGS_OFFSET reg)
+{
+       return readw(i2c->base + i2c->dev_comp->regs[reg]);
+}
+
+static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val,
+                          enum I2C_REGS_OFFSET reg)
+{
+       writew(val, i2c->base + i2c->dev_comp->regs[reg]);
+}
+
 static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
 {
        int ret;
@@ -255,8 +356,18 @@ static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
                if (ret)
                        goto err_pmic;
        }
+
+       if (i2c->clk_arb) {
+               ret = clk_prepare_enable(i2c->clk_arb);
+               if (ret)
+                       goto err_arb;
+       }
+
        return 0;
 
+err_arb:
+       if (i2c->have_pmic)
+               clk_disable_unprepare(i2c->clk_pmic);
 err_pmic:
        clk_disable_unprepare(i2c->clk_main);
 err_main:
@@ -267,6 +378,9 @@ static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
 
 static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
 {
+       if (i2c->clk_arb)
+               clk_disable_unprepare(i2c->clk_arb);
+
        if (i2c->have_pmic)
                clk_disable_unprepare(i2c->clk_pmic);
 
@@ -278,31 +392,36 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
 {
        u16 control_reg;
 
-       writew(I2C_SOFT_RST, i2c->base + OFFSET_SOFTRESET);
+       mtk_i2c_writew(i2c, I2C_SOFT_RST, OFFSET_SOFTRESET);
 
        /* Set ioconfig */
        if (i2c->use_push_pull)
-               writew(I2C_IO_CONFIG_PUSH_PULL, i2c->base + OFFSET_IO_CONFIG);
+               mtk_i2c_writew(i2c, I2C_IO_CONFIG_PUSH_PULL, OFFSET_IO_CONFIG);
        else
-               writew(I2C_IO_CONFIG_OPEN_DRAIN, i2c->base + OFFSET_IO_CONFIG);
+               mtk_i2c_writew(i2c, I2C_IO_CONFIG_OPEN_DRAIN, OFFSET_IO_CONFIG);
 
        if (i2c->dev_comp->dcm)
-               writew(I2C_DCM_DISABLE, i2c->base + OFFSET_DCM_EN);
+               mtk_i2c_writew(i2c, I2C_DCM_DISABLE, OFFSET_DCM_EN);
 
        if (i2c->dev_comp->timing_adjust)
-               writew(I2C_DEFAULT_CLK_DIV - 1, i2c->base + OFFSET_CLOCK_DIV);
+               mtk_i2c_writew(i2c, I2C_DEFAULT_CLK_DIV - 1, OFFSET_CLOCK_DIV);
 
-       writew(i2c->timing_reg, i2c->base + OFFSET_TIMING);
-       writew(i2c->high_speed_reg, i2c->base + OFFSET_HS);
+       mtk_i2c_writew(i2c, i2c->timing_reg, OFFSET_TIMING);
+       mtk_i2c_writew(i2c, i2c->high_speed_reg, OFFSET_HS);
+       if (i2c->dev_comp->ltiming_adjust)
+               mtk_i2c_writew(i2c, i2c->ltiming_reg, OFFSET_LTIMING);
 
        /* 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);
+               mtk_i2c_writew(i2c, I2C_CONTROL_WRAPPER, 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);
+       if (i2c->dev_comp->dma_sync)
+               control_reg |= I2C_CONTROL_DMAACK_EN | I2C_CONTROL_ASYNC_MODE;
+
+       mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
+       mtk_i2c_writew(i2c, I2C_DELAY_LEN, OFFSET_DELAY_LEN);
 
        writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
        udelay(50);
@@ -390,6 +509,8 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
        unsigned int clk_src;
        unsigned int step_cnt;
        unsigned int sample_cnt;
+       unsigned int l_step_cnt;
+       unsigned int l_sample_cnt;
        unsigned int target_speed;
        int ret;
 
@@ -399,11 +520,11 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
        if (target_speed > MAX_FS_MODE_SPEED) {
                /* Set master code speed register */
                ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED,
-                                             &step_cnt, &sample_cnt);
+                                             &l_step_cnt, &l_sample_cnt);
                if (ret < 0)
                        return ret;
 
-               i2c->timing_reg = (sample_cnt << 8) | step_cnt;
+               i2c->timing_reg = (l_sample_cnt << 8) | l_step_cnt;
 
                /* Set the high speed mode register */
                ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
@@ -413,6 +534,10 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
 
                i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
                        (sample_cnt << 12) | (step_cnt << 8);
+
+               if (i2c->dev_comp->ltiming_adjust)
+                       i2c->ltiming_reg = (l_sample_cnt << 6) | l_step_cnt |
+                                          (sample_cnt << 12) | (step_cnt << 9);
        } else {
                ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
                                              &step_cnt, &sample_cnt);
@@ -423,6 +548,9 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
 
                /* Disable the high speed transaction */
                i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
+
+               if (i2c->dev_comp->ltiming_adjust)
+                       i2c->ltiming_reg = (sample_cnt << 6) | step_cnt;
        }
 
        return 0;
@@ -454,7 +582,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
 
        reinit_completion(&i2c->msg_complete);
 
-       control_reg = readw(i2c->base + OFFSET_CONTROL) &
+       control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) &
                        ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
        if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1))
                control_reg |= I2C_CONTROL_RS;
@@ -462,40 +590,41 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
        if (i2c->op == I2C_MASTER_WRRD)
                control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS;
 
-       writew(control_reg, i2c->base + OFFSET_CONTROL);
+       mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
 
        /* set start condition */
        if (i2c->speed_hz <= I2C_DEFAULT_SPEED)
-               writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF);
+               mtk_i2c_writew(i2c, I2C_ST_START_CON, OFFSET_EXT_CONF);
        else
-               writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
+               mtk_i2c_writew(i2c, I2C_FS_START_CON, OFFSET_EXT_CONF);
 
        addr_reg = i2c_8bit_addr_from_msg(msgs);
-       writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR);
+       mtk_i2c_writew(i2c, addr_reg, OFFSET_SLAVE_ADDR);
 
        /* Clear interrupt status */
-       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);
+       mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+                           I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_STAT);
+
+       mtk_i2c_writew(i2c, I2C_FIFO_ADDR_CLR, OFFSET_FIFO_ADDR_CLR);
 
        /* Enable interrupt */
-       writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
-              I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK);
+       mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+                           I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_MASK);
 
        /* Set transfer and transaction len */
        if (i2c->op == I2C_MASTER_WRRD) {
                if (i2c->dev_comp->aux_len_reg) {
-                       writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
-                       writew((msgs + 1)->len, i2c->base +
-                              OFFSET_TRANSFER_LEN_AUX);
+                       mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
+                       mtk_i2c_writew(i2c, (msgs + 1)->len,
+                                           OFFSET_TRANSFER_LEN_AUX);
                } else {
-                       writew(msgs->len | ((msgs + 1)->len) << 8,
-                              i2c->base + OFFSET_TRANSFER_LEN);
+                       mtk_i2c_writew(i2c, msgs->len | ((msgs + 1)->len) << 8,
+                                           OFFSET_TRANSFER_LEN);
                }
-               writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
+               mtk_i2c_writew(i2c, I2C_WRRD_TRANAC_VALUE, OFFSET_TRANSAC_LEN);
        } else {
-               writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
-               writew(num, i2c->base + OFFSET_TRANSAC_LEN);
+               mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
+               mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
        }
 
        /* Prepare buffer data to start transfer */
@@ -607,14 +736,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
                if (left_num >= 1)
                        start_reg |= I2C_RS_MUL_CNFG;
        }
-       writew(start_reg, i2c->base + OFFSET_START);
+       mtk_i2c_writew(i2c, start_reg, OFFSET_START);
 
        ret = wait_for_completion_timeout(&i2c->msg_complete,
                                          i2c->adap.timeout);
 
        /* Clear interrupt mask */
-       writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
-              I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK);
+       mtk_i2c_writew(i2c, ~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
+                           I2C_ARB_LOST | I2C_TRANSAC_COMP), OFFSET_INTR_MASK);
 
        if (i2c->op == I2C_MASTER_WR) {
                dma_unmap_single(i2c->dev, wpaddr,
@@ -724,8 +853,8 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
        if (i2c->auto_restart)
                restart_flag = I2C_RS_TRANSFER;
 
-       intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
-       writew(intr_stat, i2c->base + OFFSET_INTR_STAT);
+       intr_stat = mtk_i2c_readw(i2c, OFFSET_INTR_STAT);
+       mtk_i2c_writew(i2c, intr_stat, OFFSET_INTR_STAT);
 
        /*
         * when occurs ack error, i2c controller generate two interrupts
@@ -737,8 +866,8 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
        if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
                i2c->ignore_restart_irq = false;
                i2c->irq_stat = 0;
-               writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START,
-                      i2c->base + OFFSET_START);
+               mtk_i2c_writew(i2c, I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG |
+                                   I2C_TRANSAC_START, OFFSET_START);
        } else {
                if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
                        complete(&i2c->msg_complete);
@@ -839,6 +968,10 @@ static int mtk_i2c_probe(struct platform_device *pdev)
                return PTR_ERR(i2c->clk_dma);
        }
 
+       i2c->clk_arb = devm_clk_get(&pdev->dev, "arb");
+       if (IS_ERR(i2c->clk_arb))
+               i2c->clk_arb = NULL;
+
        clk = i2c->clk_main;
        if (i2c->have_pmic) {
                i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
index 0ed5a41804dcf8d5d33d043a3d0f8e29b5430e4f..4f30a43b63da27aab5a3ef7e6d60dc8f9054ec59 100644 (file)
@@ -1070,8 +1070,7 @@ static int nmk_i2c_remove(struct amba_device *adev)
        /* disable the controller */
        i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
        clk_disable_unprepare(dev->clk);
-       if (res)
-               release_mem_region(res->start, resource_size(res));
+       release_mem_region(res->start, resource_size(res));
 
        return 0;
 }
index 4e1a077fb688116e36ff82ce589751e6b9cc22f7..c3dabee0aa359e781f1e9d4b6fbcbca06955c043 100644 (file)
@@ -26,8 +26,6 @@
 #include <linux/spinlock.h>
 #include <linux/jiffies.h>
 
-#define OCORES_FLAG_POLL BIT(0)
-
 /*
  * 'process_lock' exists because ocores_process() and ocores_process_timeout()
  * can't run in parallel.
@@ -37,7 +35,6 @@ struct ocores_i2c {
        int iobase;
        u32 reg_shift;
        u32 reg_io_width;
-       unsigned long flags;
        wait_queue_head_t wait;
        struct i2c_adapter adap;
        struct i2c_msg *msg;
@@ -403,11 +400,7 @@ static int ocores_xfer_polling(struct i2c_adapter *adap,
 static int ocores_xfer(struct i2c_adapter *adap,
                       struct i2c_msg *msgs, int num)
 {
-       struct ocores_i2c *i2c = i2c_get_adapdata(adap);
-
-       if (i2c->flags & OCORES_FLAG_POLL)
-               return ocores_xfer_polling(adap, msgs, num);
-       return ocores_xfer_core(i2c, msgs, num, false);
+       return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, false);
 }
 
 static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
@@ -447,8 +440,9 @@ static u32 ocores_func(struct i2c_adapter *adap)
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 }
 
-static const struct i2c_algorithm ocores_algorithm = {
+static struct i2c_algorithm ocores_algorithm = {
        .master_xfer = ocores_xfer,
+       .master_xfer_atomic = ocores_xfer_polling,
        .functionality = ocores_func,
 };
 
@@ -673,13 +667,13 @@ static int ocores_i2c_probe(struct platform_device *pdev)
 
        irq = platform_get_irq(pdev, 0);
        if (irq == -ENXIO) {
-               i2c->flags |= OCORES_FLAG_POLL;
+               ocores_algorithm.master_xfer = ocores_xfer_polling;
        } else {
                if (irq < 0)
                        return irq;
        }
 
-       if (!(i2c->flags & OCORES_FLAG_POLL)) {
+       if (ocores_algorithm.master_xfer != ocores_xfer_polling) {
                ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
                                       pdev->name, i2c);
                if (ret) {
index cd9c65f3d404ff92f0eef87d4d99236b370e82cc..faa0394048a0f49e240406d4ee1520508d9a05a8 100644 (file)
@@ -269,6 +269,8 @@ static const u8 reg_map_ip_v2[] = {
        [OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
 };
 
+static int omap_i2c_xfer_data(struct omap_i2c_dev *omap);
+
 static inline void omap_i2c_write_reg(struct omap_i2c_dev *omap,
                                      int reg, u16 val)
 {
@@ -648,15 +650,28 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *omap, u8 size, bool is_rx)
                        (1000 * omap->speed / 8);
 }
 
+static void omap_i2c_wait(struct omap_i2c_dev *omap)
+{
+       u16 stat;
+       u16 mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
+       int count = 0;
+
+       do {
+               stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
+               count++;
+       } while (!(stat & mask) && count < 5);
+}
+
 /*
  * Low level master read/write transaction.
  */
 static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
-                            struct i2c_msg *msg, int stop)
+                            struct i2c_msg *msg, int stop, bool polling)
 {
        struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
        unsigned long timeout;
        u16 w;
+       int ret;
 
        dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
                msg->addr, msg->len, msg->flags, stop);
@@ -680,7 +695,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
        w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
        omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w);
 
-       reinit_completion(&omap->cmd_complete);
+       if (!polling)
+               reinit_completion(&omap->cmd_complete);
        omap->cmd_err = 0;
 
        w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
@@ -732,8 +748,18 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
         * REVISIT: We should abort the transfer on signals, but the bus goes
         * into arbitration and we're currently unable to recover from it.
         */
-       timeout = wait_for_completion_timeout(&omap->cmd_complete,
-                                               OMAP_I2C_TIMEOUT);
+       if (!polling) {
+               timeout = wait_for_completion_timeout(&omap->cmd_complete,
+                                                     OMAP_I2C_TIMEOUT);
+       } else {
+               do {
+                       omap_i2c_wait(omap);
+                       ret = omap_i2c_xfer_data(omap);
+               } while (ret == -EAGAIN);
+
+               timeout = !ret;
+       }
+
        if (timeout == 0) {
                dev_err(omap->dev, "controller timed out\n");
                omap_i2c_reset(omap);
@@ -772,7 +798,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
  * to do the work during IRQ processing.
  */
 static int
-omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+omap_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg msgs[], int num,
+                    bool polling)
 {
        struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
        int i;
@@ -794,7 +821,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
                omap->set_mpu_wkup_lat(omap->dev, omap->latency);
 
        for (i = 0; i < num; i++) {
-               r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+               r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)),
+                                     polling);
                if (r != 0)
                        break;
        }
@@ -813,6 +841,18 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        return r;
 }
 
+static int
+omap_i2c_xfer_irq(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+       return omap_i2c_xfer_common(adap, msgs, num, false);
+}
+
+static int
+omap_i2c_xfer_polling(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+       return omap_i2c_xfer_common(adap, msgs, num, true);
+}
+
 static u32
 omap_i2c_func(struct i2c_adapter *adap)
 {
@@ -1035,10 +1075,8 @@ omap_i2c_isr(int irq, void *dev_id)
        return ret;
 }
 
-static irqreturn_t
-omap_i2c_isr_thread(int this_irq, void *dev_id)
+static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
 {
-       struct omap_i2c_dev *omap = dev_id;
        u16 bits;
        u16 stat;
        int err = 0, count = 0;
@@ -1056,7 +1094,8 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
 
                if (!stat) {
                        /* my work here is done */
-                       goto out;
+                       err = -EAGAIN;
+                       break;
                }
 
                dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat);
@@ -1165,14 +1204,25 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
                }
        } while (stat);
 
-       omap_i2c_complete_cmd(omap, err);
+       return err;
+}
+
+static irqreturn_t
+omap_i2c_isr_thread(int this_irq, void *dev_id)
+{
+       int ret;
+       struct omap_i2c_dev *omap = dev_id;
+
+       ret = omap_i2c_xfer_data(omap);
+       if (ret != -EAGAIN)
+               omap_i2c_complete_cmd(omap, ret);
 
-out:
        return IRQ_HANDLED;
 }
 
 static const struct i2c_algorithm omap_i2c_algo = {
-       .master_xfer    = omap_i2c_xfer,
+       .master_xfer    = omap_i2c_xfer_irq,
+       .master_xfer_atomic     = omap_i2c_xfer_polling,
        .functionality  = omap_i2c_func,
 };
 
index a7578f6da9796647724fb346a88bd4a2e91a3b14..d39a4606f72d3d7849d2727a4f83cad298bd58e3 100644 (file)
@@ -85,6 +85,7 @@
 /* ICFBSCR */
 #define TCYC17 0x0f            /* 17*Tcyc delay 1st bit between SDA and SCL */
 
+#define RCAR_MIN_DMA_LEN       8
 
 #define RCAR_BUS_PHASE_START   (MDBS | MIE | ESG)
 #define RCAR_BUS_PHASE_DATA    (MDBS | MIE)
@@ -398,7 +399,7 @@ static void rcar_i2c_dma_callback(void *data)
        rcar_i2c_dma_unmap(priv);
 }
 
-static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
+static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
 {
        struct device *dev = rcar_i2c_priv_to_dev(priv);
        struct i2c_msg *msg = priv->msg;
@@ -412,9 +413,9 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
        int len;
 
        /* Do various checks to see if DMA is feasible at all */
-       if (IS_ERR(chan) || msg->len < 8 || !(msg->flags & I2C_M_DMA_SAFE) ||
-           (read && priv->flags & ID_P_NO_RXDMA))
-               return;
+       if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
+           !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
+               return false;
 
        if (read) {
                /*
@@ -434,7 +435,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
        dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
        if (dma_mapping_error(chan->device->dev, dma_addr)) {
                dev_dbg(dev, "dma map failed, using PIO\n");
-               return;
+               return false;
        }
 
        sg_dma_len(&priv->sg) = len;
@@ -448,7 +449,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
        if (!txdesc) {
                dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
                rcar_i2c_cleanup_dma(priv);
-               return;
+               return false;
        }
 
        txdesc->callback = rcar_i2c_dma_callback;
@@ -458,7 +459,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
        if (dma_submit_error(cookie)) {
                dev_dbg(dev, "submitting dma failed, using PIO\n");
                rcar_i2c_cleanup_dma(priv);
-               return;
+               return false;
        }
 
        /* Enable DMA Master Received/Transmitted */
@@ -468,6 +469,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
                rcar_i2c_write(priv, ICDMAER, TMDMAE);
 
        dma_async_issue_pending(chan);
+       return true;
 }
 
 static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
@@ -478,6 +480,10 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
        if (!(msr & MDE))
                return;
 
+       /* Check if DMA can be enabled and take over */
+       if (priv->pos == 1 && rcar_i2c_dma(priv))
+               return;
+
        if (priv->pos < msg->len) {
                /*
                 * Prepare next data to ICRXTX register.
@@ -488,13 +494,6 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
                 */
                rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
                priv->pos++;
-
-               /*
-                * Try to use DMA to transmit the rest of the data if
-                * address transfer phase just finished.
-                */
-               if (msr & MAT)
-                       rcar_i2c_dma(priv);
        } else {
                /*
                 * The last data was pushed to ICRXTX on _PREV_ empty irq.
@@ -921,6 +920,9 @@ static int rcar_i2c_probe(struct platform_device *pdev)
        struct i2c_timings i2c_t;
        int irq, ret;
 
+       /* Otherwise logic will break because some bytes must always use PIO */
+       BUILD_BUG_ON_MSG(RCAR_MIN_DMA_LEN < 3, "Invalid min DMA length");
+
        priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
index b75ff144b5704293e0946c4919e747d91ede0b85..f31413fd9521ef9f2cd60d478c915fe8ae215323 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #define RIIC_ICCR1     0x00
 #define RIIC_ICCR2     0x04
@@ -112,12 +113,10 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 {
        struct riic_dev *riic = i2c_get_adapdata(adap);
        unsigned long time_left;
-       int i, ret;
+       int i;
        u8 start_bit;
 
-       ret = clk_prepare_enable(riic->clk);
-       if (ret)
-               return ret;
+       pm_runtime_get_sync(adap->dev.parent);
 
        if (readb(riic->base + RIIC_ICCR2) & ICCR2_BBSY) {
                riic->err = -EBUSY;
@@ -150,7 +149,7 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        }
 
  out:
-       clk_disable_unprepare(riic->clk);
+       pm_runtime_put(adap->dev.parent);
 
        return riic->err ?: num;
 }
@@ -281,20 +280,18 @@ static const struct i2c_algorithm riic_algo = {
 
 static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
 {
-       int ret;
+       int ret = 0;
        unsigned long rate;
        int total_ticks, cks, brl, brh;
 
-       ret = clk_prepare_enable(riic->clk);
-       if (ret)
-               return ret;
+       pm_runtime_get_sync(riic->adapter.dev.parent);
 
        if (t->bus_freq_hz > 400000) {
                dev_err(&riic->adapter.dev,
                        "unsupported bus speed (%dHz). 400000 max\n",
                        t->bus_freq_hz);
-               clk_disable_unprepare(riic->clk);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        rate = clk_get_rate(riic->clk);
@@ -332,8 +329,8 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
        if (brl > (0x1F + 3)) {
                dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
                        (unsigned long)t->bus_freq_hz);
-               clk_disable_unprepare(riic->clk);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        brh = total_ticks - brl;
@@ -378,9 +375,9 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
 
        riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
 
-       clk_disable_unprepare(riic->clk);
-
-       return 0;
+out:
+       pm_runtime_put(riic->adapter.dev.parent);
+       return ret;
 }
 
 static struct riic_irq_desc riic_irqs[] = {
@@ -439,28 +436,36 @@ static int riic_i2c_probe(struct platform_device *pdev)
 
        i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
 
+       pm_runtime_enable(&pdev->dev);
+
        ret = riic_init_hw(riic, &i2c_t);
        if (ret)
-               return ret;
-
+               goto out;
 
        ret = i2c_add_adapter(adap);
        if (ret)
-               return ret;
+               goto out;
 
        platform_set_drvdata(pdev, riic);
 
        dev_info(&pdev->dev, "registered with %dHz bus speed\n",
                 i2c_t.bus_freq_hz);
        return 0;
+
+out:
+       pm_runtime_disable(&pdev->dev);
+       return ret;
 }
 
 static int riic_i2c_remove(struct platform_device *pdev)
 {
        struct riic_dev *riic = platform_get_drvdata(pdev);
 
+       pm_runtime_get_sync(&pdev->dev);
        writeb(0, riic->base + RIIC_ICIER);
+       pm_runtime_put(&pdev->dev);
        i2c_del_adapter(&riic->adapter);
+       pm_runtime_disable(&pdev->dev);
 
        return 0;
 }
index 4284fc991cfd47e110d319b7b39b77d8866d436c..48337bef5b87bfdffd5ba966e742a702dbb1d972 100644 (file)
@@ -476,8 +476,12 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
 
                                        list_add_tail(&v->node,
                                                      &solutions);
+                                       break;
                                }
                        }
+
+                       if (p_prev == p)
+                               break;
                }
        }
 
index 5503fa171df0dbd3ef73d0e63852d8931f4d47a3..743c161b22c50eb67dacab12d3d519e5059da416 100644 (file)
@@ -328,12 +328,6 @@ static int stu300_start_and_await_event(struct stu300_dev *dev,
 {
        int ret;
 
-       if (unlikely(irqs_disabled())) {
-               /* TODO: implement polling for this case if need be. */
-               WARN(1, "irqs are disabled, cannot poll for event\n");
-               return -EIO;
-       }
-
        /* Lock command issue, fill in an event we wait for */
        spin_lock_irq(&dev->cmd_issue_lock);
        init_completion(&dev->cmd_complete);
@@ -380,13 +374,6 @@ static int stu300_await_event(struct stu300_dev *dev,
 {
        int ret;
 
-       if (unlikely(irqs_disabled())) {
-               /* TODO: implement polling for this case if need be. */
-               dev_err(&dev->pdev->dev, "irqs are disabled on this "
-                       "system!\n");
-               return -EIO;
-       }
-
        /* Is it already here? */
        spin_lock_irq(&dev->cmd_issue_lock);
        dev->cmd_err = STU300_ERROR_NONE;
@@ -846,6 +833,13 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
        return num;
 }
 
+static int stu300_xfer_todo(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       /* TODO: implement polling for this case if need be. */
+       WARN(1, "%s: atomic transfers not implemented\n", dev_name(&adap->dev));
+       return -EOPNOTSUPP;
+}
+
 static u32 stu300_func(struct i2c_adapter *adap)
 {
        /* This is the simplest thing you can think of... */
@@ -853,8 +847,9 @@ static u32 stu300_func(struct i2c_adapter *adap)
 }
 
 static const struct i2c_algorithm stu300_algo = {
-       .master_xfer    = stu300_xfer,
-       .functionality  = stu300_func,
+       .master_xfer = stu300_xfer,
+       .master_xfer_atomic = stu300_xfer_todo,
+       .functionality = stu300_func,
 };
 
 static const struct i2c_adapter_quirks stu300_quirks = {
index f6cd35d0a2ac0e6c37c3e9e982a20a2ee77df2fb..9bb085793a0cf7989e1c06dbd16a1445f0c6fb21 100644 (file)
@@ -207,7 +207,8 @@ static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num)
 
 static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
                                   struct mrq_i2c_request *request,
-                                  struct mrq_i2c_response *response)
+                                  struct mrq_i2c_response *response,
+                                  bool atomic)
 {
        struct tegra_bpmp_message msg;
        int err;
@@ -222,7 +223,7 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
        msg.rx.data = response;
        msg.rx.size = sizeof(*response);
 
-       if (irqs_disabled())
+       if (atomic)
                err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
        else
                err = tegra_bpmp_transfer(i2c->bpmp, &msg);
@@ -230,8 +231,9 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
        return err;
 }
 
-static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
-                              struct i2c_msg *msgs, int num)
+static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
+                                     struct i2c_msg *msgs, int num,
+                                     bool atomic)
 {
        struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
        struct mrq_i2c_response response;
@@ -253,7 +255,7 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
                return err;
        }
 
-       err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response);
+       err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic);
        if (err < 0) {
                dev_err(i2c->dev, "failed to transfer message: %d\n", err);
                return err;
@@ -268,6 +270,18 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
        return num;
 }
 
+static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
+                              struct i2c_msg *msgs, int num)
+{
+       return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, false);
+}
+
+static int tegra_bpmp_i2c_xfer_atomic(struct i2c_adapter *adapter,
+                                     struct i2c_msg *msgs, int num)
+{
+       return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, true);
+}
+
 static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
 {
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
@@ -276,6 +290,7 @@ static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
 
 static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
        .master_xfer = tegra_bpmp_i2c_xfer,
+       .master_xfer_atomic = tegra_bpmp_i2c_xfer_atomic,
        .functionality = tegra_bpmp_i2c_func,
 };
 
index 38af18645133cb486d6494bb642128414f2194eb..4e6300dc2c4e3d6fa43d4b618ca34d5e7c0b0811 100644 (file)
@@ -1890,7 +1890,11 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
        /* Retry automatically on arbitration loss */
        orig_jiffies = jiffies;
        for (ret = 0, try = 0; try <= adap->retries; try++) {
-               ret = adap->algo->master_xfer(adap, msgs, num);
+               if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
+                       ret = adap->algo->master_xfer_atomic(adap, msgs, num);
+               else
+                       ret = adap->algo->master_xfer(adap, msgs, num);
+
                if (ret != -EAGAIN)
                        break;
                if (time_after(jiffies, orig_jiffies + adap->timeout))
@@ -1946,14 +1950,9 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
         *    one (discarding status on the second message) or errno
         *    (discarding status on the first one).
         */
-       if (in_atomic() || irqs_disabled()) {
-               ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
-               if (!ret)
-                       /* I2C activity is ongoing. */
-                       return -EAGAIN;
-       } else {
-               i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
-       }
+       ret = __i2c_lock_bus_helper(adap);
+       if (ret)
+               return ret;
 
        ret = __i2c_transfer(adap, msgs, num);
        i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
index 1321191125965a9f5ef6c1b684ff29641a24496a..fdb0fb9fb9aab660e2ae4b1ed0a2942d348e0dca 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/i2c-smbus.h>
 #include <linux/slab.h>
 
+#include "i2c-core.h"
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/smbus.h>
 
@@ -530,7 +532,10 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
 {
        s32 res;
 
-       i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
+       res = __i2c_lock_bus_helper(adapter);
+       if (res)
+               return res;
+
        res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
                               command, protocol, data);
        i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
@@ -543,6 +548,9 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
                     unsigned short flags, char read_write,
                     u8 command, int protocol, union i2c_smbus_data *data)
 {
+       int (*xfer_func)(struct i2c_adapter *adap, u16 addr,
+                        unsigned short flags, char read_write,
+                        u8 command, int size, union i2c_smbus_data *data);
        unsigned long orig_jiffies;
        int try;
        s32 res;
@@ -557,13 +565,20 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
 
        flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
 
-       if (adapter->algo->smbus_xfer) {
+       xfer_func = adapter->algo->smbus_xfer;
+       if (i2c_in_atomic_xfer_mode()) {
+               if (adapter->algo->smbus_xfer_atomic)
+                       xfer_func = adapter->algo->smbus_xfer_atomic;
+               else if (adapter->algo->master_xfer_atomic)
+                       xfer_func = NULL; /* fallback to I2C emulation */
+       }
+
+       if (xfer_func) {
                /* Retry automatically on arbitration loss */
                orig_jiffies = jiffies;
                for (res = 0, try = 0; try <= adapter->retries; try++) {
-                       res = adapter->algo->smbus_xfer(adapter, addr, flags,
-                                                       read_write, command,
-                                                       protocol, data);
+                       res = xfer_func(adapter, addr, flags, read_write,
+                                       command, protocol, data);
                        if (res != -EAGAIN)
                                break;
                        if (time_after(jiffies,
index 37576f50fe20d75ebc58d71ff804adfa80afc961..f9d0c417b5a56061695e82abb781ae162c0113ae 100644 (file)
@@ -29,6 +29,31 @@ extern int           __i2c_first_dynamic_bus_num;
 
 int i2c_check_7bit_addr_validity_strict(unsigned short addr);
 
+/*
+ * We only allow atomic transfers for very late communication, e.g. to send
+ * the powerdown command to a PMIC. Atomic transfers are a corner case and not
+ * for generic use!
+ */
+static inline bool i2c_in_atomic_xfer_mode(void)
+{
+       return system_state > SYSTEM_RUNNING && irqs_disabled();
+}
+
+static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap)
+{
+       int ret = 0;
+
+       if (i2c_in_atomic_xfer_mode()) {
+               WARN(!adap->algo->master_xfer_atomic && !adap->algo->smbus_xfer_atomic,
+                    "No atomic I2C transfer handler for '%s'\n", dev_name(&adap->dev));
+               ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT) ? 0 : -EAGAIN;
+       } else {
+               i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
+       }
+
+       return ret;
+}
+
 #ifdef CONFIG_ACPI
 const struct acpi_device_id *
 i2c_acpi_match_device(const struct acpi_device_id *matches,
index f330690b41253ff75e352b321575cc008fcf0353..603252fa12843bf8f7aee3fb64ad476de6caa8a0 100644 (file)
@@ -310,12 +310,18 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
                else
                        priv->algo.master_xfer = __i2c_mux_master_xfer;
        }
+       if (parent->algo->master_xfer_atomic)
+               priv->algo.master_xfer_atomic = priv->algo.master_xfer;
+
        if (parent->algo->smbus_xfer) {
                if (muxc->mux_locked)
                        priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
                else
                        priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
        }
+       if (parent->algo->smbus_xfer_atomic)
+               priv->algo.smbus_xfer_atomic = priv->algo.smbus_xfer;
+
        priv->algo.functionality = i2c_mux_functionality;
 
        /* Now fill out new adapter structure */
index 012b45e447c303b779ce86d452c6f34d59569300..4eecffc265275ede52ba0c890cb27d416f550390 100644 (file)
@@ -99,6 +99,8 @@ static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 ne
 
        /* Now fill out current adapter structure. cur_chan must be up to date */
        priv->algo.master_xfer = i2c_demux_master_xfer;
+       if (adap->algo->master_xfer_atomic)
+               priv->algo.master_xfer_atomic = i2c_demux_master_xfer;
        priv->algo.functionality = i2c_demux_functionality;
 
        snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
index 69045df78e2d047124ad65e2bcedcc16a7e3210f..7fd5575a368f95d4208de03fb20b3e9be659dd65 100644 (file)
@@ -33,6 +33,7 @@ struct i2c_algo_bit_data {
                                   minimum 5 us for standard-mode I2C and SMBus,
                                   maximum 50 us for SMBus */
        int timeout;            /* in jiffies */
+       bool can_do_atomic;     /* callbacks don't sleep, we can be atomic */
 };
 
 int i2c_bit_add_bus(struct i2c_adapter *);
index 383510b4f0832d38a8e4ef574e90ac294a2f6a1b..03755d4c9229d53eda40f631f71ffab4c939e2ff 100644 (file)
@@ -499,9 +499,13 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
  * @master_xfer: Issue a set of i2c transactions to the given I2C adapter
  *   defined by the msgs array, with num messages available to transfer via
  *   the adapter specified by adap.
+ * @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context
+ *   so e.g. PMICs can be accessed very late before shutdown. Optional.
  * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
  *   is not present, then the bus layer will try and convert the SMBus calls
  *   into I2C transfers instead.
+ * @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context
+ *   so e.g. PMICs can be accessed very late before shutdown. Optional.
  * @functionality: Return the flags that this algorithm/adapter pair supports
  *   from the I2C_FUNC_* flags.
  * @reg_slave: Register given client to I2C slave mode of this adapter
@@ -512,25 +516,33 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
  * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
  * to name two of the most common.
  *
- * The return codes from the @master_xfer field should indicate the type of
- * error code that occurred during the transfer, as documented in the kernel
- * Documentation file Documentation/i2c/fault-codes.
+ * The return codes from the @master_xfer{_atomic} fields should indicate the
+ * type of error code that occurred during the transfer, as documented in the
+ * Kernel Documentation file Documentation/i2c/fault-codes.
  */
 struct i2c_algorithm {
-       /* If an adapter algorithm can't do I2C-level access, set master_xfer
-          to NULL. If an adapter algorithm can do SMBus access, set
-          smbus_xfer. If set to NULL, the SMBus protocol is simulated
-          using common I2C messages */
-       /* master_xfer should return the number of messages successfully
-          processed, or a negative value on error */
+       /*
+        * If an adapter algorithm can't do I2C-level access, set master_xfer
+        * to NULL. If an adapter algorithm can do SMBus access, set
+        * smbus_xfer. If set to NULL, the SMBus protocol is simulated
+        * using common I2C messages.
+        *
+        * master_xfer should return the number of messages successfully
+        * processed, or a negative value on error
+        */
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
                           int num);
-       int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
-                          unsigned short flags, char read_write,
-                          u8 command, int size, union i2c_smbus_data *data);
+       int (*master_xfer_atomic)(struct i2c_adapter *adap,
+                                  struct i2c_msg *msgs, int num);
+       int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
+                         unsigned short flags, char read_write,
+                         u8 command, int size, union i2c_smbus_data *data);
+       int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,
+                                unsigned short flags, char read_write,
+                                u8 command, int size, union i2c_smbus_data *data);
 
        /* To determine what the adapter supports */
-       u32 (*functionality) (struct i2c_adapter *);
+       u32 (*functionality)(struct i2c_adapter *adap);
 
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
        int (*reg_slave)(struct i2c_client *client);