Merge branch 'i2c/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 30 Apr 2021 20:01:02 +0000 (13:01 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 30 Apr 2021 20:01:02 +0000 (13:01 -0700)
Pull i2c updates from Wolfram Sang:

 - new drivers for Silicon Labs CP2615 and the HiSilicon I2C unit

 - bigger refactoring for the MPC driver

 - support for full software nodes - no need to work around with only
   properties anymore

 - we now have 'devm_i2c_add_adapter', too

 - sub-system wide fixes for the RPM refcounting problem which often
   caused a leak when an error was encountered during probe

 - the rest is usual driver updates and improvements

* 'i2c/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (77 commits)
  i2c: mediatek: Use scl_int_delay_ns to compensate clock-stretching
  i2c: mediatek: Fix wrong dma sync flag
  i2c: mediatek: Fix send master code at more than 1MHz
  i2c: sh7760: fix IRQ error path
  i2c: i801: Add support for Intel Alder Lake PCH-M
  i2c: core: Fix spacing error by checkpatch
  i2c: s3c2410: simplify getting of_device_id match data
  i2c: nomadik: Fix space errors
  i2c: iop3xx: Fix coding style issues
  i2c: amd8111: Fix coding style issues
  i2c: mpc: Drop duplicate message from devm_platform_ioremap_resource()
  i2c: mpc: Use device_get_match_data() helper
  i2c: mpc: Remove CONFIG_PM_SLEEP ifdeffery
  i2c: mpc: Use devm_clk_get_optional()
  i2c: mpc: Update license and copyright
  i2c: mpc: Interrupt driven transfer
  i2c: sh7760: add IRQ check
  i2c: rcar: add IRQ check
  i2c: mlxbf: add IRQ check
  i2c: jz4780: add IRQ check
  ...

56 files changed:
Documentation/devicetree/bindings/i2c/i2c-mpc.txt [deleted file]
Documentation/devicetree/bindings/i2c/i2c-mpc.yaml [new file with mode: 0644]
MAINTAINERS
arch/arm/mach-davinci/board-da830-evm.c
arch/arm/mach-davinci/board-dm365-evm.c
arch/arm/mach-davinci/board-dm644x-evm.c
arch/arm/mach-davinci/board-dm646x-evm.c
arch/arm/mach-davinci/board-mityomapl138.c
arch/arm/mach-davinci/board-sffsdr.c
arch/arm/mach-omap1/board-osk.c
arch/arm/mach-pxa/stargate2.c
arch/arm/mach-s3c/mach-mini2440.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-amd8111.c
drivers/i2c/busses/i2c-brcmstb.c
drivers/i2c/busses/i2c-cadence.c
drivers/i2c/busses/i2c-cht-wc.c
drivers/i2c/busses/i2c-cp2615.c [new file with mode: 0644]
drivers/i2c/busses/i2c-designware-common.c
drivers/i2c/busses/i2c-designware-core.h
drivers/i2c/busses/i2c-designware-master.c
drivers/i2c/busses/i2c-designware-pcidrv.c
drivers/i2c/busses/i2c-emev2.c
drivers/i2c/busses/i2c-hisi.c [new file with mode: 0644]
drivers/i2c/busses/i2c-i801.c
drivers/i2c/busses/i2c-icy.c
drivers/i2c/busses/i2c-img-scb.c
drivers/i2c/busses/i2c-imx-lpi2c.c
drivers/i2c/busses/i2c-imx.c
drivers/i2c/busses/i2c-iop3xx.c
drivers/i2c/busses/i2c-jz4780.c
drivers/i2c/busses/i2c-mlxbf.c
drivers/i2c/busses/i2c-mpc.c
drivers/i2c/busses/i2c-mt65xx.c
drivers/i2c/busses/i2c-nomadik.c
drivers/i2c/busses/i2c-nvidia-gpu.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-powermac.c
drivers/i2c/busses/i2c-qcom-cci.c
drivers/i2c/busses/i2c-rcar.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/busses/i2c-scmi.c
drivers/i2c/busses/i2c-sh7760.c
drivers/i2c/busses/i2c-sprd.c
drivers/i2c/busses/i2c-stm32f7.c
drivers/i2c/busses/i2c-tegra-bpmp.c
drivers/i2c/busses/i2c-xgene-slimpro.c
drivers/i2c/busses/i2c-xiic.c
drivers/i2c/i2c-boardinfo.c
drivers/i2c/i2c-core-base.c
drivers/i2c/i2c-dev.c
drivers/input/mouse/elantech.c
drivers/platform/chrome/chromeos_laptop.c
drivers/platform/x86/intel_cht_int33fe_microb.c
include/linux/i2c.h

diff --git a/Documentation/devicetree/bindings/i2c/i2c-mpc.txt b/Documentation/devicetree/bindings/i2c/i2c-mpc.txt
deleted file mode 100644 (file)
index 42a3905..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-* I2C
-
-Required properties :
-
- - reg : Offset and length of the register set for the device
- - compatible : should be "fsl,CHIP-i2c" where CHIP is the name of a
-   compatible processor, e.g. mpc8313, mpc8543, mpc8544, mpc5121,
-   mpc5200 or mpc5200b. For the mpc5121, an additional node
-   "fsl,mpc5121-i2c-ctrl" is required as shown in the example below.
-
-Recommended properties :
-
- - interrupts : <a b> where a is the interrupt number and b is a
-   field that represents an encoding of the sense and level
-   information for the interrupt.  This should be encoded based on
-   the information in section 2) depending on the type of interrupt
-   controller you have.
- - fsl,preserve-clocking : boolean; if defined, the clock settings
-   from the bootloader are preserved (not touched).
- - clock-frequency : desired I2C bus clock frequency in Hz.
- - fsl,timeout : I2C bus timeout in microseconds.
-
-Examples :
-
-       /* MPC5121 based board */
-       i2c@1740 {
-               #address-cells = <1>;
-               #size-cells = <0>;
-               compatible = "fsl,mpc5121-i2c", "fsl-i2c";
-               reg = <0x1740 0x20>;
-               interrupts = <11 0x8>;
-               interrupt-parent = <&ipic>;
-               clock-frequency = <100000>;
-       };
-
-       i2ccontrol@1760 {
-               compatible = "fsl,mpc5121-i2c-ctrl";
-               reg = <0x1760 0x8>;
-       };
-
-       /* MPC5200B based board */
-       i2c@3d00 {
-               #address-cells = <1>;
-               #size-cells = <0>;
-               compatible = "fsl,mpc5200b-i2c","fsl,mpc5200-i2c","fsl-i2c";
-               reg = <0x3d00 0x40>;
-               interrupts = <2 15 0>;
-               interrupt-parent = <&mpc5200_pic>;
-               fsl,preserve-clocking;
-       };
-
-       /* MPC8544 base board */
-       i2c@3100 {
-               #address-cells = <1>;
-               #size-cells = <0>;
-               compatible = "fsl,mpc8544-i2c", "fsl-i2c";
-               reg = <0x3100 0x100>;
-               interrupts = <43 2>;
-               interrupt-parent = <&mpic>;
-               clock-frequency = <400000>;
-               fsl,timeout = <10000>;
-       };
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml b/Documentation/devicetree/bindings/i2c/i2c-mpc.yaml
new file mode 100644 (file)
index 0000000..7b553d5
--- /dev/null
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/i2c-mpc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: I2C-Bus adapter for MPC824x/83xx/85xx/86xx/512x/52xx SoCs
+
+maintainers:
+  - Chris Packham <chris.packham@alliedtelesis.co.nz>
+
+allOf:
+  - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - mpc5200-i2c
+              - fsl,mpc5200-i2c
+              - fsl,mpc5121-i2c
+              - fsl,mpc8313-i2c
+              - fsl,mpc8543-i2c
+              - fsl,mpc8544-i2c
+          - const: fsl-i2c
+      - items:
+          - const: fsl,mpc5200b-i2c
+          - const: fsl,mpc5200-i2c
+          - const: fsl-i2c
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  fsl,preserve-clocking:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description: |
+      if defined, the clock settings from the bootloader are
+      preserved (not touched)
+
+  fsl,timeout:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: |
+      I2C bus timeout in microseconds
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    /* MPC5121 based board */
+    i2c@1740 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        compatible = "fsl,mpc5121-i2c", "fsl-i2c";
+        reg = <0x1740 0x20>;
+        interrupts = <11 0x8>;
+        interrupt-parent = <&ipic>;
+        clock-frequency = <100000>;
+    };
+
+    /* MPC5200B based board */
+    i2c@3d00 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        compatible = "fsl,mpc5200b-i2c", "fsl,mpc5200-i2c", "fsl-i2c";
+        reg = <0x3d00 0x40>;
+        interrupts = <2 15 0>;
+        interrupt-parent = <&mpc5200_pic>;
+        fsl,preserve-clocking;
+    };
+
+    /* MPC8544 base board */
+    i2c@3100 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        compatible = "fsl,mpc8544-i2c", "fsl-i2c";
+        reg = <0x3100 0x100>;
+        interrupts = <43 2>;
+        interrupt-parent = <&mpic>;
+        clock-frequency = <400000>;
+        fsl,timeout = <10000>;
+    };
+...
index 30aa825..55490e2 100644 (file)
@@ -4662,6 +4662,11 @@ F:       drivers/counter/
 F:     include/linux/counter.h
 F:     include/linux/counter_enum.h
 
+CP2615 I2C DRIVER
+M:     Bence Csókás <bence98@sch.bme.hu>
+S:     Maintained
+F:     drivers/i2c/busses/i2c-cp2615.c
+
 CPMAC ETHERNET DRIVER
 M:     Florian Fainelli <f.fainelli@gmail.com>
 L:     netdev@vger.kernel.org
@@ -7235,6 +7240,13 @@ S:       Maintained
 F:     Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml
 F:     drivers/i2c/busses/i2c-imx-lpi2c.c
 
+FREESCALE MPC I2C DRIVER
+M:     Chris Packham <chris.packham@alliedtelesis.co.nz>
+L:     linux-i2c@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/i2c/i2c-mpc.yaml
+F:     drivers/i2c/busses/i2c-mpc.c
+
 FREESCALE QORIQ DPAA ETHERNET DRIVER
 M:     Madalin Bucur <madalin.bucur@nxp.com>
 L:     netdev@vger.kernel.org
@@ -8157,6 +8169,13 @@ F:       drivers/crypto/hisilicon/hpre/hpre.h
 F:     drivers/crypto/hisilicon/hpre/hpre_crypto.c
 F:     drivers/crypto/hisilicon/hpre/hpre_main.c
 
+HISILICON I2C CONTROLLER DRIVER
+M:     Yicong Yang <yangyicong@hisilicon.com>
+L:     linux-i2c@vger.kernel.org
+S:     Maintained
+W:     https://www.hisilicon.com
+F:     drivers/i2c/busses/i2c-hisi.c
+
 HISILICON LPC BUS DRIVER
 M:     john.garry@huawei.com
 S:     Maintained
index a20ba12..823c9cc 100644 (file)
@@ -454,6 +454,10 @@ static const struct property_entry da830_evm_i2c_eeprom_properties[] = {
        { }
 };
 
+static const struct software_node da830_evm_i2c_eeprom_node = {
+       .properties = da830_evm_i2c_eeprom_properties,
+};
+
 static int __init da830_evm_ui_expander_setup(struct i2c_client *client,
                int gpio, unsigned ngpio, void *context)
 {
@@ -485,7 +489,7 @@ static struct pcf857x_platform_data __initdata da830_evm_ui_expander_info = {
 static struct i2c_board_info __initdata da830_evm_i2c_devices[] = {
        {
                I2C_BOARD_INFO("24c256", 0x50),
-               .properties = da830_evm_i2c_eeprom_properties,
+               .swnode = &da830_evm_i2c_eeprom_node,
        },
        {
                I2C_BOARD_INFO("tlv320aic3x", 0x18),
index bdf31eb..b3bef74 100644 (file)
@@ -232,10 +232,14 @@ static const struct property_entry eeprom_properties[] = {
        { }
 };
 
+static const struct software_node eeprom_node = {
+       .properties = eeprom_properties,
+};
+
 static struct i2c_board_info i2c_info[] = {
        {
                I2C_BOARD_INFO("24c256", 0x50),
-               .properties = eeprom_properties,
+               .swnode = &eeprom_node,
        },
        {
                I2C_BOARD_INFO("tlv320aic3x", 0x18),
index 7755ccc..cce3a62 100644 (file)
@@ -541,6 +541,10 @@ static const struct property_entry eeprom_properties[] = {
        { }
 };
 
+static const struct software_node eeprom_node = {
+       .properties = eeprom_properties,
+};
+
 /*
  * MSP430 supports RTC, card detection, input from IR remote, and
  * a bit more.  It triggers interrupts on GPIO(7) from pressing
@@ -647,7 +651,7 @@ static struct i2c_board_info __initdata i2c_info[] =  {
        },
        {
                I2C_BOARD_INFO("24c256", 0x50),
-               .properties = eeprom_properties,
+               .swnode = &eeprom_node,
        },
        {
                I2C_BOARD_INFO("tlv320aic33", 0x1b),
index 952ddab..ee91d81 100644 (file)
@@ -362,6 +362,10 @@ static const struct property_entry eeprom_properties[] = {
        PROPERTY_ENTRY_U32("pagesize", 64),
        { }
 };
+
+static const struct software_node eeprom_node = {
+       .properties = eeprom_properties,
+};
 #endif
 
 static u8 dm646x_iis_serializer_direction[] = {
@@ -430,7 +434,7 @@ static void evm_init_cpld(void)
 static struct i2c_board_info __initdata i2c_info[] =  {
        {
                I2C_BOARD_INFO("24c256", 0x50),
-               .properties  = eeprom_properties,
+               .swnode = &eeprom_node,
        },
        {
                I2C_BOARD_INFO("pcf8574a", 0x38),
index 5205008..2127969 100644 (file)
@@ -197,6 +197,10 @@ static const struct property_entry mityomapl138_fd_chip_properties[] = {
        { }
 };
 
+static const struct software_node mityomapl138_fd_chip_node = {
+       .properties = mityomapl138_fd_chip_properties,
+};
+
 static struct davinci_i2c_platform_data mityomap_i2c_0_pdata = {
        .bus_freq       = 100,  /* kHz */
        .bus_delay      = 0,    /* usec */
@@ -323,7 +327,7 @@ static struct i2c_board_info __initdata mityomap_tps65023_info[] = {
        },
        {
                I2C_BOARD_INFO("24c02", 0x50),
-               .properties = mityomapl138_fd_chip_properties,
+               .swnode = &mityomapl138_fd_chip_node,
        },
 };
 
index 79b4795..6930b2f 100644 (file)
@@ -84,10 +84,14 @@ static const struct property_entry eeprom_properties[] = {
        { }
 };
 
+static const struct software_node eeprom_node = {
+       .properties = eeprom_properties,
+};
+
 static struct i2c_board_info __initdata i2c_info[] =  {
        {
                I2C_BOARD_INFO("24c64", 0x50),
-               .properties = eeprom_properties,
+               .swnode = &eeprom_node,
        },
        /* Other I2C devices:
         * MSP430,  addr 0x23 (not used)
index 0a4c9b0..e18b6f1 100644 (file)
@@ -332,11 +332,15 @@ static const struct property_entry mistral_at24_properties[] = {
        { }
 };
 
+static const struct software_node mistral_at24_node = {
+       .properties = mistral_at24_properties,
+};
+
 static struct i2c_board_info __initdata mistral_i2c_board_info[] = {
        {
                /* NOTE:  powered from LCD supply */
                I2C_BOARD_INFO("24c04", 0x50),
-               .properties = mistral_at24_properties,
+               .swnode = &mistral_at24_node,
        },
        /* TODO when driver support is ready:
         *  - optionally ov9640 camera sensor at 0x30
index e2353f7..7ad6274 100644 (file)
@@ -794,6 +794,10 @@ static const struct property_entry pca9500_eeprom_properties[] = {
        { }
 };
 
+static const struct software_node pca9500_eeprom_node = {
+       .properties = pca9500_eeprom_properties,
+};
+
 /**
  * stargate2_reset_bluetooth() reset the bluecore to ensure consistent state
  **/
@@ -929,7 +933,7 @@ static struct i2c_board_info __initdata stargate2_i2c_board_info[] = {
        }, {
                .type = "24c02",
                .addr = 0x57,
-               .properties = pca9500_eeprom_properties,
+               .swnode = &pca9500_eeprom_node,
        }, {
                .type = "max1238",
                .addr = 0x35,
index 4100905..551ec66 100644 (file)
@@ -542,10 +542,14 @@ static const struct property_entry mini2440_at24_properties[] = {
        { }
 };
 
+static const struct software_node mini2440_at24_node = {
+       .properties = mini2440_at24_properties,
+};
+
 static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
        {
                I2C_BOARD_INFO("24c08", 0x50),
-               .properties = mini2440_at24_properties,
+               .swnode = &mini2440_at24_node,
        },
 };
 
index 543a05e..281a65d 100644 (file)
@@ -645,6 +645,16 @@ config I2C_HIGHLANDER
          This driver can also be built as a module.  If so, the module
          will be called i2c-highlander.
 
+config I2C_HISI
+       tristate "HiSilicon I2C controller"
+       depends on ARM64 || COMPILE_TEST
+       help
+         Say Y here if you want to have Hisilicon I2C controller support
+         available on the Kunpeng Server.
+
+         This driver can also be built as a module. If so, the module
+         will be called i2c-hisi.
+
 config I2C_IBM_IIC
        tristate "IBM PPC 4xx on-chip I2C interface"
        depends on 4xx
@@ -1199,6 +1209,16 @@ config I2C_DLN2
         This driver can also be built as a module.  If so, the module
         will be called i2c-dln2.
 
+config I2C_CP2615
+       tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
+       depends on USB
+       help
+         If you say yes to this option, support will be included for Silicon
+         Labs CP2615's I2C interface.
+
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-cp2615.
+
 config I2C_PARPORT
        tristate "Parallel port adapter"
        depends on PARPORT
index 615f35e..69e9963 100644 (file)
@@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_EMEV2)               += i2c-emev2.o
 obj-$(CONFIG_I2C_EXYNOS5)      += i2c-exynos5.o
 obj-$(CONFIG_I2C_GPIO)         += i2c-gpio.o
 obj-$(CONFIG_I2C_HIGHLANDER)   += i2c-highlander.o
+obj-$(CONFIG_I2C_HISI)         += i2c-hisi.o
 obj-$(CONFIG_I2C_HIX5HD2)      += i2c-hix5hd2.o
 obj-$(CONFIG_I2C_IBM_IIC)      += i2c-ibm_iic.o
 obj-$(CONFIG_I2C_IMG)          += i2c-img-scb.o
@@ -123,6 +124,7 @@ obj-$(CONFIG_I2C_RCAR)              += i2c-rcar.o
 # External I2C/SMBus adapter drivers
 obj-$(CONFIG_I2C_DIOLAN_U2C)   += i2c-diolan-u2c.o
 obj-$(CONFIG_I2C_DLN2)         += i2c-dln2.o
+obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
 obj-$(CONFIG_I2C_PARPORT)      += i2c-parport.o
 obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF)       += i2c-robotfuzz-osif.o
 obj-$(CONFIG_I2C_TAOS_EVM)     += i2c-taos-evm.o
index 34862ad..1ed7e94 100644 (file)
@@ -186,9 +186,9 @@ static int amd_ec_write(struct amd_smbus *smbus, unsigned char address,
 #define AMD_SMB_PRTCL_PEC              0x80
 
 
-static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
+static s32 amd8111_access(struct i2c_adapter *adap, u16 addr,
                unsigned short flags, char read_write, u8 command, int size,
-               union i2c_smbus_data * data)
+               union i2c_smbus_data *data)
 {
        struct amd_smbus *smbus = adap->algo_data;
        unsigned char protocol, len, pec, temp[2];
@@ -199,130 +199,130 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
        pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
 
        switch (size) {
-               case I2C_SMBUS_QUICK:
-                       protocol |= AMD_SMB_PRTCL_QUICK;
-                       read_write = I2C_SMBUS_WRITE;
-                       break;
-
-               case I2C_SMBUS_BYTE:
-                       if (read_write == I2C_SMBUS_WRITE) {
-                               status = amd_ec_write(smbus, AMD_SMB_CMD,
-                                                     command);
-                               if (status)
-                                       return status;
-                       }
-                       protocol |= AMD_SMB_PRTCL_BYTE;
-                       break;
-
-               case I2C_SMBUS_BYTE_DATA:
-                       status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+       case I2C_SMBUS_QUICK:
+               protocol |= AMD_SMB_PRTCL_QUICK;
+               read_write = I2C_SMBUS_WRITE;
+               break;
+
+       case I2C_SMBUS_BYTE:
+               if (read_write == I2C_SMBUS_WRITE) {
+                       status = amd_ec_write(smbus, AMD_SMB_CMD,
+                                               command);
                        if (status)
                                return status;
-                       if (read_write == I2C_SMBUS_WRITE) {
-                               status = amd_ec_write(smbus, AMD_SMB_DATA,
-                                                     data->byte);
-                               if (status)
-                                       return status;
-                       }
-                       protocol |= AMD_SMB_PRTCL_BYTE_DATA;
-                       break;
+               }
+               protocol |= AMD_SMB_PRTCL_BYTE;
+               break;
 
-               case I2C_SMBUS_WORD_DATA:
-                       status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+       case I2C_SMBUS_BYTE_DATA:
+               status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+               if (status)
+                       return status;
+               if (read_write == I2C_SMBUS_WRITE) {
+                       status = amd_ec_write(smbus, AMD_SMB_DATA,
+                                               data->byte);
                        if (status)
                                return status;
-                       if (read_write == I2C_SMBUS_WRITE) {
-                               status = amd_ec_write(smbus, AMD_SMB_DATA,
-                                                     data->word & 0xff);
-                               if (status)
-                                       return status;
-                               status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
-                                                     data->word >> 8);
-                               if (status)
-                                       return status;
-                       }
-                       protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
-                       break;
+               }
+               protocol |= AMD_SMB_PRTCL_BYTE_DATA;
+               break;
 
-               case I2C_SMBUS_BLOCK_DATA:
-                       status = amd_ec_write(smbus, AMD_SMB_CMD, command);
-                       if (status)
-                               return status;
-                       if (read_write == I2C_SMBUS_WRITE) {
-                               len = min_t(u8, data->block[0],
-                                           I2C_SMBUS_BLOCK_MAX);
-                               status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
-                               if (status)
-                                       return status;
-                               for (i = 0; i < len; i++) {
-                                       status =
-                                         amd_ec_write(smbus, AMD_SMB_DATA + i,
-                                                      data->block[i + 1]);
-                                       if (status)
-                                               return status;
-                               }
-                       }
-                       protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
-                       break;
-
-               case I2C_SMBUS_I2C_BLOCK_DATA:
-                       len = min_t(u8, data->block[0],
-                                   I2C_SMBUS_BLOCK_MAX);
-                       status = amd_ec_write(smbus, AMD_SMB_CMD, command);
-                       if (status)
-                               return status;
-                       status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
-                       if (status)
-                               return status;
-                       if (read_write == I2C_SMBUS_WRITE)
-                               for (i = 0; i < len; i++) {
-                                       status =
-                                         amd_ec_write(smbus, AMD_SMB_DATA + i,
-                                                      data->block[i + 1]);
-                                       if (status)
-                                               return status;
-                               }
-                       protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
-                       break;
-
-               case I2C_SMBUS_PROC_CALL:
-                       status = amd_ec_write(smbus, AMD_SMB_CMD, command);
-                       if (status)
-                               return status;
+       case I2C_SMBUS_WORD_DATA:
+               status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+               if (status)
+                       return status;
+               if (read_write == I2C_SMBUS_WRITE) {
                        status = amd_ec_write(smbus, AMD_SMB_DATA,
-                                             data->word & 0xff);
+                                               data->word & 0xff);
                        if (status)
                                return status;
                        status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
-                                             data->word >> 8);
+                                               data->word >> 8);
                        if (status)
                                return status;
-                       protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
-                       read_write = I2C_SMBUS_READ;
-                       break;
+               }
+               protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
+               break;
 
-               case I2C_SMBUS_BLOCK_PROC_CALL:
+       case I2C_SMBUS_BLOCK_DATA:
+               status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+               if (status)
+                       return status;
+               if (read_write == I2C_SMBUS_WRITE) {
                        len = min_t(u8, data->block[0],
-                                   I2C_SMBUS_BLOCK_MAX - 1);
-                       status = amd_ec_write(smbus, AMD_SMB_CMD, command);
-                       if (status)
-                               return status;
+                                       I2C_SMBUS_BLOCK_MAX);
                        status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
                        if (status)
                                return status;
                        for (i = 0; i < len; i++) {
-                               status = amd_ec_write(smbus, AMD_SMB_DATA + i,
-                                                     data->block[i + 1]);
+                               status =
+                                       amd_ec_write(smbus, AMD_SMB_DATA + i,
+                                               data->block[i + 1]);
                                if (status)
                                        return status;
                        }
-                       protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
-                       read_write = I2C_SMBUS_READ;
-                       break;
+               }
+               protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
+               break;
+
+       case I2C_SMBUS_I2C_BLOCK_DATA:
+               len = min_t(u8, data->block[0],
+                               I2C_SMBUS_BLOCK_MAX);
+               status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+               if (status)
+                       return status;
+               status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
+               if (status)
+                       return status;
+               if (read_write == I2C_SMBUS_WRITE)
+                       for (i = 0; i < len; i++) {
+                               status =
+                                       amd_ec_write(smbus, AMD_SMB_DATA + i,
+                                               data->block[i + 1]);
+                               if (status)
+                                       return status;
+                       }
+               protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
+               break;
 
-               default:
-                       dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
-                       return -EOPNOTSUPP;
+       case I2C_SMBUS_PROC_CALL:
+               status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+               if (status)
+                       return status;
+               status = amd_ec_write(smbus, AMD_SMB_DATA,
+                                       data->word & 0xff);
+               if (status)
+                       return status;
+               status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
+                                       data->word >> 8);
+               if (status)
+                       return status;
+               protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
+               read_write = I2C_SMBUS_READ;
+               break;
+
+       case I2C_SMBUS_BLOCK_PROC_CALL:
+               len = min_t(u8, data->block[0],
+                               I2C_SMBUS_BLOCK_MAX - 1);
+               status = amd_ec_write(smbus, AMD_SMB_CMD, command);
+               if (status)
+                       return status;
+               status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
+               if (status)
+                       return status;
+               for (i = 0; i < len; i++) {
+                       status = amd_ec_write(smbus, AMD_SMB_DATA + i,
+                                               data->block[i + 1]);
+                       if (status)
+                               return status;
+               }
+               protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
+               read_write = I2C_SMBUS_READ;
+               break;
+
+       default:
+               dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+               return -EOPNOTSUPP;
        }
 
        status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
@@ -357,40 +357,40 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
                return 0;
 
        switch (size) {
-               case I2C_SMBUS_BYTE:
-               case I2C_SMBUS_BYTE_DATA:
-                       status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
-                       if (status)
-                               return status;
-                       break;
+       case I2C_SMBUS_BYTE:
+       case I2C_SMBUS_BYTE_DATA:
+               status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
+               if (status)
+                       return status;
+               break;
 
-               case I2C_SMBUS_WORD_DATA:
-               case I2C_SMBUS_PROC_CALL:
-                       status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
-                       if (status)
-                               return status;
-                       status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
-                       if (status)
-                               return status;
-                       data->word = (temp[1] << 8) | temp[0];
-                       break;
+       case I2C_SMBUS_WORD_DATA:
+       case I2C_SMBUS_PROC_CALL:
+               status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
+               if (status)
+                       return status;
+               status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
+               if (status)
+                       return status;
+               data->word = (temp[1] << 8) | temp[0];
+               break;
 
-               case I2C_SMBUS_BLOCK_DATA:
-               case I2C_SMBUS_BLOCK_PROC_CALL:
-                       status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);
+       case I2C_SMBUS_BLOCK_DATA:
+       case I2C_SMBUS_BLOCK_PROC_CALL:
+               status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);
+               if (status)
+                       return status;
+               len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
+               fallthrough;
+       case I2C_SMBUS_I2C_BLOCK_DATA:
+               for (i = 0; i < len; i++) {
+                       status = amd_ec_read(smbus, AMD_SMB_DATA + i,
+                                               data->block + i + 1);
                        if (status)
                                return status;
-                       len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
-                       fallthrough;
-               case I2C_SMBUS_I2C_BLOCK_DATA:
-                       for (i = 0; i < len; i++) {
-                               status = amd_ec_read(smbus, AMD_SMB_DATA + i,
-                                                    data->block + i + 1);
-                               if (status)
-                                       return status;
-                       }
-                       data->block[0] = len;
-                       break;
+               }
+               data->block[0] = len;
+               break;
        }
 
        return 0;
index ba766d2..490ee39 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
-#include <linux/version.h>
 
 #define N_DATA_REGS                                    8
 
index e4b7f2a..c1bbc4c 100644 (file)
@@ -789,7 +789,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
        bool change_role = false;
 #endif
 
-       ret = pm_runtime_get_sync(id->dev);
+       ret = pm_runtime_resume_and_get(id->dev);
        if (ret < 0)
                return ret;
 
@@ -911,7 +911,7 @@ static int cdns_reg_slave(struct i2c_client *slave)
        if (slave->flags & I2C_CLIENT_TEN)
                return -EAFNOSUPPORT;
 
-       ret = pm_runtime_get_sync(id->dev);
+       ret = pm_runtime_resume_and_get(id->dev);
        if (ret < 0)
                return ret;
 
@@ -1200,7 +1200,10 @@ static int cdns_i2c_probe(struct platform_device *pdev)
        if (IS_ERR(id->membase))
                return PTR_ERR(id->membase);
 
-       id->irq = platform_get_irq(pdev, 0);
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0)
+               return ret;
+       id->irq = ret;
 
        id->adap.owner = THIS_MODULE;
        id->adap.dev.of_node = pdev->dev.of_node;
index f80d79e..08f491e 100644 (file)
@@ -280,6 +280,10 @@ static const struct property_entry bq24190_props[] = {
        { }
 };
 
+static const struct software_node bq24190_node = {
+       .properties = bq24190_props,
+};
+
 static struct regulator_consumer_supply fusb302_consumer = {
        .supply = "vbus",
        /* Must match fusb302 dev_name in intel_cht_int33fe.c */
@@ -308,7 +312,7 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
                .type = "bq24190",
                .addr = 0x6b,
                .dev_name = "bq24190",
-               .properties = bq24190_props,
+               .swnode = &bq24190_node,
                .platform_data = &bq24190_pdata,
        };
        int ret, reg, irq;
diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c
new file mode 100644 (file)
index 0000000..78cfecd
--- /dev/null
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * i2c support for Silicon Labs' CP2615 Digital Audio Bridge
+ *
+ * (c) 2021, Bence Csókás <bence98@sch.bme.hu>
+ */
+
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+
+/** CP2615 I/O Protocol implementation */
+
+#define CP2615_VID 0x10c4
+#define CP2615_PID 0xeac1
+
+#define IOP_EP_IN  0x82
+#define IOP_EP_OUT 0x02
+#define IOP_IFN 1
+#define IOP_ALTSETTING 2
+
+#define MAX_IOP_SIZE 64
+#define MAX_IOP_PAYLOAD_SIZE (MAX_IOP_SIZE - 6)
+#define MAX_I2C_SIZE (MAX_IOP_PAYLOAD_SIZE - 4)
+
+enum cp2615_iop_msg_type {
+       iop_GetAccessoryInfo = 0xD100,
+       iop_AccessoryInfo = 0xA100,
+       iop_GetPortConfiguration = 0xD203,
+       iop_PortConfiguration = 0xA203,
+       iop_DoI2cTransfer = 0xD400,
+       iop_I2cTransferResult = 0xA400,
+       iop_GetSerialState = 0xD501,
+       iop_SerialState = 0xA501
+};
+
+struct __packed cp2615_iop_msg {
+       __be16 preamble, length, msg;
+       u8 data[MAX_IOP_PAYLOAD_SIZE];
+};
+
+#define PART_ID_A01 0x1400
+#define PART_ID_A02 0x1500
+
+struct __packed cp2615_iop_accessory_info {
+       __be16 part_id, option_id, proto_ver;
+};
+
+struct __packed cp2615_i2c_transfer {
+       u8 tag, i2caddr, read_len, write_len;
+       u8 data[MAX_I2C_SIZE];
+};
+
+/* Possible values for struct cp2615_i2c_transfer_result.status */
+enum cp2615_i2c_status {
+       /* Writing to the internal EEPROM failed, because it is locked */
+       CP2615_CFG_LOCKED = -6,
+       /* read_len or write_len out of range */
+       CP2615_INVALID_PARAM = -4,
+       /* I2C slave did not ACK in time */
+       CP2615_TIMEOUT,
+       /* I2C bus busy */
+       CP2615_BUS_BUSY,
+       /* I2C bus error (ie. device NAK'd the request) */
+       CP2615_BUS_ERROR,
+       CP2615_SUCCESS
+};
+
+struct __packed cp2615_i2c_transfer_result {
+       u8 tag, i2caddr;
+       s8 status;
+       u8 read_len;
+       u8 data[MAX_I2C_SIZE];
+};
+
+static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_type msg,
+                       const void *data, size_t data_len)
+{
+       if (data_len > MAX_IOP_PAYLOAD_SIZE)
+               return -EFBIG;
+
+       if (!ret)
+               return -EINVAL;
+
+       ret->preamble = 0x2A2A;
+       ret->length = htons(data_len + 6);
+       ret->msg = htons(msg);
+       if (data && data_len)
+               memcpy(&ret->data, data, data_len);
+       return 0;
+}
+
+static int cp2615_init_i2c_msg(struct cp2615_iop_msg *ret, const struct cp2615_i2c_transfer *data)
+{
+       return cp2615_init_iop_msg(ret, iop_DoI2cTransfer, data, 4 + data->write_len);
+}
+
+/* Translates status codes to Linux errno's */
+static int cp2615_check_status(enum cp2615_i2c_status status)
+{
+       switch (status) {
+       case CP2615_SUCCESS:
+                       return 0;
+       case CP2615_BUS_ERROR:
+               return -ENXIO;
+       case CP2615_BUS_BUSY:
+               return -EAGAIN;
+       case CP2615_TIMEOUT:
+               return -ETIMEDOUT;
+       case CP2615_INVALID_PARAM:
+               return -EINVAL;
+       case CP2615_CFG_LOCKED:
+               return -EPERM;
+       }
+       /* Unknown error code */
+       return -EPROTO;
+}
+
+/** Driver code */
+
+static int
+cp2615_i2c_send(struct usb_interface *usbif, struct cp2615_i2c_transfer *i2c_w)
+{
+       struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+       int res = cp2615_init_i2c_msg(msg, i2c_w);
+
+       if (!res)
+               res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
+                                  msg, ntohs(msg->length), NULL, 0);
+       kfree(msg);
+       return res;
+}
+
+static int
+cp2615_i2c_recv(struct usb_interface *usbif, unsigned char tag, void *buf)
+{
+       struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       struct cp2615_i2c_transfer_result *i2c_r = (struct cp2615_i2c_transfer_result *)&msg->data;
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+       int res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
+                              msg, sizeof(struct cp2615_iop_msg), NULL, 0);
+
+       if (res < 0) {
+               kfree(msg);
+               return res;
+       }
+
+       if (msg->msg != htons(iop_I2cTransferResult) || i2c_r->tag != tag) {
+               kfree(msg);
+               return -EIO;
+       }
+
+       res = cp2615_check_status(i2c_r->status);
+       if (!res)
+               memcpy(buf, &i2c_r->data, i2c_r->read_len);
+
+       kfree(msg);
+       return res;
+}
+
+/* Checks if the IOP is functional by querying the part's ID */
+static int cp2615_check_iop(struct usb_interface *usbif)
+{
+       struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       struct cp2615_iop_accessory_info *info = (struct cp2615_iop_accessory_info *)&msg->data;
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+       int res = cp2615_init_iop_msg(msg, iop_GetAccessoryInfo, NULL, 0);
+
+       if (res)
+               goto out;
+
+       res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
+                                  msg, ntohs(msg->length), NULL, 0);
+       if (res)
+               goto out;
+
+       res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
+                              msg, sizeof(struct cp2615_iop_msg), NULL, 0);
+       if (res)
+               goto out;
+
+       if (msg->msg != htons(iop_AccessoryInfo)) {
+               res = -EIO;
+               goto out;
+       }
+
+       switch (ntohs(info->part_id)) {
+       case PART_ID_A01:
+               dev_dbg(&usbif->dev, "Found A01 part. (WARNING: errata exists!)\n");
+               break;
+       case PART_ID_A02:
+               dev_dbg(&usbif->dev, "Found good A02 part.\n");
+               break;
+       default:
+               dev_warn(&usbif->dev, "Unknown part ID %04X\n", ntohs(info->part_id));
+       }
+
+out:
+       kfree(msg);
+       return res;
+}
+
+static int
+cp2615_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct usb_interface *usbif = adap->algo_data;
+       int i = 0, ret = 0;
+       struct i2c_msg *msg;
+       struct cp2615_i2c_transfer i2c_w = {0};
+
+       dev_dbg(&usbif->dev, "Doing %d I2C transactions\n", num);
+
+       for (; !ret && i < num; i++) {
+               msg = &msgs[i];
+
+               i2c_w.tag = 0xdd;
+               i2c_w.i2caddr = i2c_8bit_addr_from_msg(msg);
+               if (msg->flags & I2C_M_RD) {
+                       i2c_w.read_len = msg->len;
+                       i2c_w.write_len = 0;
+               } else {
+                       i2c_w.read_len = 0;
+                       i2c_w.write_len = msg->len;
+                       memcpy(&i2c_w.data, msg->buf, i2c_w.write_len);
+               }
+               ret = cp2615_i2c_send(usbif, &i2c_w);
+               if (ret)
+                       break;
+               ret = cp2615_i2c_recv(usbif, i2c_w.tag, msg->buf);
+       }
+       if (ret < 0)
+               return ret;
+       return i;
+}
+
+static u32
+cp2615_i2c_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm cp2615_i2c_algo = {
+       .master_xfer    = cp2615_i2c_master_xfer,
+       .functionality  = cp2615_i2c_func,
+};
+
+/*
+ * This chip has some limitations: one is that the USB endpoint
+ * can only receive 64 bytes/transfer, that leaves 54 bytes for
+ * the I2C transfer. On top of that, EITHER read_len OR write_len
+ * may be zero, but not both. If both are non-zero, the adapter
+ * issues a write followed by a read. And the chip does not
+ * support repeated START between the write and read phases.
+ */
+static struct i2c_adapter_quirks cp2615_i2c_quirks = {
+       .max_write_len = MAX_I2C_SIZE,
+       .max_read_len = MAX_I2C_SIZE,
+       .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
+       .max_comb_1st_msg_len = MAX_I2C_SIZE,
+       .max_comb_2nd_msg_len = MAX_I2C_SIZE
+};
+
+static void
+cp2615_i2c_remove(struct usb_interface *usbif)
+{
+       struct i2c_adapter *adap = usb_get_intfdata(usbif);
+
+       usb_set_intfdata(usbif, NULL);
+       i2c_del_adapter(adap);
+}
+
+static int
+cp2615_i2c_probe(struct usb_interface *usbif, const struct usb_device_id *id)
+{
+       int ret = 0;
+       struct i2c_adapter *adap;
+       struct usb_device *usbdev = interface_to_usbdev(usbif);
+
+       ret = usb_set_interface(usbdev, IOP_IFN, IOP_ALTSETTING);
+       if (ret)
+               return ret;
+
+       ret = cp2615_check_iop(usbif);
+       if (ret)
+               return ret;
+
+       adap = devm_kzalloc(&usbif->dev, sizeof(struct i2c_adapter), GFP_KERNEL);
+       if (!adap)
+               return -ENOMEM;
+
+       strncpy(adap->name, usbdev->serial, sizeof(adap->name) - 1);
+       adap->owner = THIS_MODULE;
+       adap->dev.parent = &usbif->dev;
+       adap->dev.of_node = usbif->dev.of_node;
+       adap->timeout = HZ;
+       adap->algo = &cp2615_i2c_algo;
+       adap->quirks = &cp2615_i2c_quirks;
+       adap->algo_data = usbif;
+
+       ret = i2c_add_adapter(adap);
+       if (ret)
+               return ret;
+
+       usb_set_intfdata(usbif, adap);
+       return 0;
+}
+
+static const struct usb_device_id id_table[] = {
+       { USB_DEVICE_INTERFACE_NUMBER(CP2615_VID, CP2615_PID, IOP_IFN) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver cp2615_i2c_driver = {
+       .name = "i2c-cp2615",
+       .probe = cp2615_i2c_probe,
+       .disconnect = cp2615_i2c_remove,
+       .id_table = id_table,
+};
+
+module_usb_driver(cp2615_i2c_driver);
+
+MODULE_AUTHOR("Bence Csókás <bence98@sch.bme.hu>");
+MODULE_DESCRIPTION("CP2615 I2C bus driver");
+MODULE_LICENSE("GPL");
index 3c19aad..fdc34d9 100644 (file)
@@ -150,6 +150,9 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
        reg = readl(dev->base + DW_IC_COMP_TYPE);
        i2c_dw_release_lock(dev);
 
+       if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
+               map_cfg.max_register = AMD_UCSI_INTR_REG;
+
        if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
                map_cfg.reg_read = dw_reg_read_swab;
                map_cfg.reg_write = dw_reg_write_swab;
index 5392b82..6a53f75 100644 (file)
@@ -295,8 +295,16 @@ struct dw_i2c_dev {
 
 #define MODEL_MSCC_OCELOT      BIT(8)
 #define MODEL_BAIKAL_BT1       BIT(9)
+#define MODEL_AMD_NAVI_GPU     BIT(10)
 #define MODEL_MASK             GENMASK(11, 8)
 
+/*
+ * Enable UCSI interrupt by writing 0xd at register
+ * offset 0x474 specified in hardware specification.
+ */
+#define AMD_UCSI_INTR_REG      0x474
+#define AMD_UCSI_INTR_EN       0xd
+
 int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
 u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
 u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
index 873ef38..13be1d6 100644 (file)
 
 #include "i2c-designware-core.h"
 
+#define AMD_TIMEOUT_MIN_US     25
+#define AMD_TIMEOUT_MAX_US     250
+#define AMD_MASTERCFG_MASK     GENMASK(15, 0)
+
 static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
 {
        /* Configure Tx/Rx FIFO threshold levels */
@@ -35,10 +39,10 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
 
 static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
 {
-       const char *mode_str, *fp_str = "";
        u32 comp_param1;
        u32 sda_falling_time, scl_falling_time;
        struct i2c_timings *t = &dev->timings;
+       const char *fp_str = "";
        u32 ic_clk;
        int ret;
 
@@ -78,7 +82,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
         * difference is the timing parameter values since the registers are
         * the same.
         */
-       if (t->bus_freq_hz == 1000000) {
+       if (t->bus_freq_hz == I2C_MAX_FAST_MODE_PLUS_FREQ) {
                /*
                 * Check are Fast Mode Plus parameters available. Calculate
                 * SCL timing parameters for Fast Mode Plus if not set.
@@ -154,22 +158,10 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
 
        ret = i2c_dw_set_sda_hold(dev);
        if (ret)
-               goto out;
-
-       switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) {
-       case DW_IC_CON_SPEED_STD:
-               mode_str = "Standard Mode";
-               break;
-       case DW_IC_CON_SPEED_HIGH:
-               mode_str = "High Speed Mode";
-               break;
-       default:
-               mode_str = "Fast Mode";
-       }
-       dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str);
+               return ret;
 
-out:
-       return ret;
+       dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz));
+       return 0;
 }
 
 /**
@@ -260,6 +252,108 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
        regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK);
 }
 
+static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev)
+{
+       u32 val;
+       int ret;
+
+       ret = regmap_read_poll_timeout(dev->map, DW_IC_INTR_STAT, val,
+                                      !(val & DW_IC_INTR_STOP_DET),
+                                       1100, 20000);
+       if (ret)
+               dev_err(dev->dev, "i2c timeout error %d\n", ret);
+
+       return ret;
+}
+
+static int i2c_dw_status(struct dw_i2c_dev *dev)
+{
+       int status;
+
+       status = i2c_dw_wait_bus_not_busy(dev);
+       if (status)
+               return status;
+
+       return i2c_dw_check_stopbit(dev);
+}
+
+/*
+ * Initiate and continue master read/write transaction with polling
+ * based transfer routine afterward write messages into the Tx buffer.
+ */
+static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs)
+{
+       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+       int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx;
+       int cmd = 0, status;
+       u8 *tx_buf;
+       u32 val;
+
+       /*
+        * In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
+        * it is mandatory to set the right value in specific register
+        * (offset:0x474) as per the hardware IP specification.
+        */
+       regmap_write(dev->map, AMD_UCSI_INTR_REG, AMD_UCSI_INTR_EN);
+
+       dev->msgs = msgs;
+       dev->msgs_num = num_msgs;
+       i2c_dw_xfer_init(dev);
+       i2c_dw_disable_int(dev);
+
+       /* Initiate messages read/write transaction */
+       for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) {
+               tx_buf = msgs[msg_wrt_idx].buf;
+               buf_len = msgs[msg_wrt_idx].len;
+
+               if (!(msgs[msg_wrt_idx].flags & I2C_M_RD))
+                       regmap_write(dev->map, DW_IC_TX_TL, buf_len - 1);
+               /*
+                * Initiate the i2c read/write transaction of buffer length,
+                * and poll for bus busy status. For the last message transfer,
+                * update the command with stopbit enable.
+                */
+               for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) {
+                       if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1)
+                               cmd |= BIT(9);
+
+                       if (msgs[msg_wrt_idx].flags & I2C_M_RD) {
+                               /* Due to hardware bug, need to write the same command twice. */
+                               regmap_write(dev->map, DW_IC_DATA_CMD, 0x100);
+                               regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | cmd);
+                               if (cmd) {
+                                       regmap_write(dev->map, DW_IC_TX_TL, 2 * (buf_len - 1));
+                                       regmap_write(dev->map, DW_IC_RX_TL, 2 * (buf_len - 1));
+                                       /*
+                                        * Need to check the stop bit. However, it cannot be
+                                        * detected from the registers so we check it always
+                                        * when read/write the last byte.
+                                        */
+                                       status = i2c_dw_status(dev);
+                                       if (status)
+                                               return status;
+
+                                       for (data_idx = 0; data_idx < buf_len; data_idx++) {
+                                               regmap_read(dev->map, DW_IC_DATA_CMD, &val);
+                                               tx_buf[data_idx] = val;
+                                       }
+                                       status = i2c_dw_check_stopbit(dev);
+                                       if (status)
+                                               return status;
+                               }
+                       } else {
+                               regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd);
+                               usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US);
+                       }
+               }
+               status = i2c_dw_check_stopbit(dev);
+               if (status)
+                       return status;
+       }
+
+       return 0;
+}
+
 /*
  * Initiate (and continue) low level master read/write transaction.
  * This function is only called from i2c_dw_isr, and pumping i2c_msg
@@ -463,6 +557,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 
        pm_runtime_get_sync(dev->dev);
 
+       /*
+        * Initiate I2C message transfer when AMD NAVI GPU card is enabled,
+        * As it is polling based transfer mechanism, which does not support
+        * interrupt based functionalities of existing DesignWare driver.
+        */
+       if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
+               ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
+               goto done_nolock;
+       }
+
        if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) {
                ret = -ESHUTDOWN;
                goto done_nolock;
@@ -739,6 +843,20 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
        return 0;
 }
 
+static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev)
+{
+       struct i2c_adapter *adap = &dev->adapter;
+       int ret;
+
+       pm_runtime_get_noresume(dev->dev);
+       ret = i2c_add_numbered_adapter(adap);
+       if (ret)
+               dev_err(dev->dev, "Failed to add adapter: %d\n", ret);
+       pm_runtime_put_noidle(dev->dev);
+
+       return ret;
+}
+
 int i2c_dw_probe_master(struct dw_i2c_dev *dev)
 {
        struct i2c_adapter *adap = &dev->adapter;
@@ -775,6 +893,9 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
        adap->dev.parent = dev->dev;
        i2c_set_adapdata(adap, dev);
 
+       if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
+               return amd_i2c_adap_quirk(dev);
+
        if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
                irq_flags = IRQF_NO_SUSPEND;
        } else {
index 55c83a7..0f409a4 100644 (file)
@@ -26,6 +26,7 @@
 #include "i2c-designware-core.h"
 
 #define DRIVER_NAME "i2c-designware-pci"
+#define AMD_CLK_RATE_HZ        100000
 
 enum dw_pci_ctl_id_t {
        medfield,
@@ -34,6 +35,7 @@ enum dw_pci_ctl_id_t {
        cherrytrail,
        haswell,
        elkhartlake,
+       navi_amd,
 };
 
 struct dw_scl_sda_cfg {
@@ -78,11 +80,23 @@ static struct dw_scl_sda_cfg hsw_config = {
        .sda_hold = 0x9,
 };
 
+/* NAVI-AMD HCNT/LCNT/SDA hold time */
+static struct dw_scl_sda_cfg navi_amd_config = {
+       .ss_hcnt = 0x1ae,
+       .ss_lcnt = 0x23a,
+       .sda_hold = 0x9,
+};
+
 static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev)
 {
        return 25000;
 }
 
+static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev)
+{
+       return AMD_CLK_RATE_HZ;
+}
+
 static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
 {
        struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
@@ -104,6 +118,35 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
        return -ENODEV;
 }
 
+ /*
+  * TODO find a better way how to deduplicate instantiation
+  * of USB PD slave device from nVidia GPU driver.
+  */
+static int navi_amd_register_client(struct dw_i2c_dev *dev)
+{
+       struct i2c_board_info   info;
+
+       memset(&info, 0, sizeof(struct i2c_board_info));
+       strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE);
+       info.addr = 0x08;
+       info.irq = dev->irq;
+
+       dev->slave = i2c_new_client_device(&dev->adapter, &info);
+       if (IS_ERR(dev->slave))
+               return PTR_ERR(dev->slave);
+
+       return 0;
+}
+
+static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
+{
+       struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
+
+       dev->flags |= MODEL_AMD_NAVI_GPU;
+       dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+       return 0;
+}
+
 static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
 {
        /*
@@ -155,6 +198,12 @@ static struct dw_pci_controller dw_pci_controllers[] = {
                .bus_num = -1,
                .get_clk_rate_khz = ehl_get_clk_rate_khz,
        },
+       [navi_amd] = {
+               .bus_num = -1,
+               .scl_sda_cfg = &navi_amd_config,
+               .setup =  navi_amd_setup,
+               .get_clk_rate_khz = navi_amd_get_clk_rate_khz,
+       },
 };
 
 #ifdef CONFIG_PM
@@ -274,6 +323,14 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
                return r;
        }
 
+       if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
+               r = navi_amd_register_client(dev);
+               if (r) {
+                       dev_err(dev->dev, "register client failed with %d\n", r);
+                       return r;
+               }
+       }
+
        pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_put_autosuspend(&pdev->dev);
@@ -337,6 +394,10 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
        { PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake },
        { PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake },
        { PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake },
+       { PCI_VDEVICE(ATI,  0x7314), navi_amd },
+       { PCI_VDEVICE(ATI,  0x73a4), navi_amd },
+       { PCI_VDEVICE(ATI,  0x73e4), navi_amd },
+       { PCI_VDEVICE(ATI,  0x73c4), navi_amd },
        { 0,}
 };
 MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
index a08554c..bdff0e6 100644 (file)
@@ -395,7 +395,10 @@ static int em_i2c_probe(struct platform_device *pdev)
 
        em_i2c_reset(&priv->adap);
 
-       priv->irq = platform_get_irq(pdev, 0);
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0)
+               goto err_clk;
+       priv->irq = ret;
        ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0,
                                "em_i2c", priv);
        if (ret)
diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c
new file mode 100644 (file)
index 0000000..acf3948
--- /dev/null
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HiSilicon I2C Controller Driver for Kunpeng SoC
+ *
+ * Copyright (c) 2021 HiSilicon Technologies Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define HISI_I2C_FRAME_CTRL            0x0000
+#define   HISI_I2C_FRAME_CTRL_SPEED_MODE       GENMASK(1, 0)
+#define   HISI_I2C_FRAME_CTRL_ADDR_TEN BIT(2)
+#define HISI_I2C_SLV_ADDR              0x0004
+#define   HISI_I2C_SLV_ADDR_VAL                GENMASK(9, 0)
+#define   HISI_I2C_SLV_ADDR_GC_S_MODE  BIT(10)
+#define   HISI_I2C_SLV_ADDR_GC_S_EN    BIT(11)
+#define HISI_I2C_CMD_TXDATA            0x0008
+#define   HISI_I2C_CMD_TXDATA_DATA     GENMASK(7, 0)
+#define   HISI_I2C_CMD_TXDATA_RW       BIT(8)
+#define   HISI_I2C_CMD_TXDATA_P_EN     BIT(9)
+#define   HISI_I2C_CMD_TXDATA_SR_EN    BIT(10)
+#define HISI_I2C_RXDATA                        0x000c
+#define   HISI_I2C_RXDATA_DATA         GENMASK(7, 0)
+#define HISI_I2C_SS_SCL_HCNT           0x0010
+#define HISI_I2C_SS_SCL_LCNT           0x0014
+#define HISI_I2C_FS_SCL_HCNT           0x0018
+#define HISI_I2C_FS_SCL_LCNT           0x001c
+#define HISI_I2C_HS_SCL_HCNT           0x0020
+#define HISI_I2C_HS_SCL_LCNT           0x0024
+#define HISI_I2C_FIFO_CTRL             0x0028
+#define   HISI_I2C_FIFO_RX_CLR         BIT(0)
+#define   HISI_I2C_FIFO_TX_CLR         BIT(1)
+#define   HISI_I2C_FIFO_RX_AF_THRESH   GENMASK(7, 2)
+#define   HISI_I2C_FIFO_TX_AE_THRESH   GENMASK(13, 8)
+#define HISI_I2C_FIFO_STATE            0x002c
+#define   HISI_I2C_FIFO_STATE_RX_RERR  BIT(0)
+#define   HISI_I2C_FIFO_STATE_RX_WERR  BIT(1)
+#define   HISI_I2C_FIFO_STATE_RX_EMPTY BIT(3)
+#define   HISI_I2C_FIFO_STATE_TX_RERR  BIT(6)
+#define   HISI_I2C_FIFO_STATE_TX_WERR  BIT(7)
+#define   HISI_I2C_FIFO_STATE_TX_FULL  BIT(11)
+#define HISI_I2C_SDA_HOLD              0x0030
+#define   HISI_I2C_SDA_HOLD_TX         GENMASK(15, 0)
+#define   HISI_I2C_SDA_HOLD_RX         GENMASK(23, 16)
+#define HISI_I2C_FS_SPK_LEN            0x0038
+#define   HISI_I2C_FS_SPK_LEN_CNT      GENMASK(7, 0)
+#define HISI_I2C_HS_SPK_LEN            0x003c
+#define   HISI_I2C_HS_SPK_LEN_CNT      GENMASK(7, 0)
+#define HISI_I2C_INT_MSTAT             0x0044
+#define HISI_I2C_INT_CLR               0x0048
+#define HISI_I2C_INT_MASK              0x004C
+#define HISI_I2C_TRANS_STATE           0x0050
+#define HISI_I2C_TRANS_ERR             0x0054
+#define HISI_I2C_VERSION               0x0058
+
+#define HISI_I2C_INT_ALL       GENMASK(4, 0)
+#define HISI_I2C_INT_TRANS_CPLT        BIT(0)
+#define HISI_I2C_INT_TRANS_ERR BIT(1)
+#define HISI_I2C_INT_FIFO_ERR  BIT(2)
+#define HISI_I2C_INT_RX_FULL   BIT(3)
+#define HISI_I2C_INT_TX_EMPTY  BIT(4)
+#define HISI_I2C_INT_ERR \
+       (HISI_I2C_INT_TRANS_ERR | HISI_I2C_INT_FIFO_ERR)
+
+#define HISI_I2C_STD_SPEED_MODE                0
+#define HISI_I2C_FAST_SPEED_MODE       1
+#define HISI_I2C_HIGH_SPEED_MODE       2
+
+#define HISI_I2C_TX_FIFO_DEPTH         64
+#define HISI_I2C_RX_FIFO_DEPTH         64
+#define HISI_I2C_TX_F_AE_THRESH                1
+#define HISI_I2C_RX_F_AF_THRESH                60
+
+#define HZ_PER_KHZ     1000
+
+#define NSEC_TO_CYCLES(ns, clk_rate_khz) \
+       DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC)
+
+struct hisi_i2c_controller {
+       struct i2c_adapter adapter;
+       void __iomem *iobase;
+       struct device *dev;
+       int irq;
+
+       /* Intermediates for recording the transfer process */
+       struct completion *completion;
+       struct i2c_msg *msgs;
+       int msg_num;
+       int msg_tx_idx;
+       int buf_tx_idx;
+       int msg_rx_idx;
+       int buf_rx_idx;
+       u16 tar_addr;
+       u32 xfer_err;
+
+       /* I2C bus configuration */
+       struct i2c_timings t;
+       u32 clk_rate_khz;
+       u32 spk_len;
+};
+
+static void hisi_i2c_enable_int(struct hisi_i2c_controller *ctlr, u32 mask)
+{
+       writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_MASK);
+}
+
+static void hisi_i2c_disable_int(struct hisi_i2c_controller *ctlr, u32 mask)
+{
+       writel_relaxed((~mask) & HISI_I2C_INT_ALL, ctlr->iobase + HISI_I2C_INT_MASK);
+}
+
+static void hisi_i2c_clear_int(struct hisi_i2c_controller *ctlr, u32 mask)
+{
+       writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_CLR);
+}
+
+static void hisi_i2c_handle_errors(struct hisi_i2c_controller *ctlr)
+{
+       u32 int_err = ctlr->xfer_err, reg;
+
+       if (int_err & HISI_I2C_INT_FIFO_ERR) {
+               reg = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+
+               if (reg & HISI_I2C_FIFO_STATE_RX_RERR)
+                       dev_err(ctlr->dev, "rx fifo error read\n");
+
+               if (reg & HISI_I2C_FIFO_STATE_RX_WERR)
+                       dev_err(ctlr->dev, "rx fifo error write\n");
+
+               if (reg & HISI_I2C_FIFO_STATE_TX_RERR)
+                       dev_err(ctlr->dev, "tx fifo error read\n");
+
+               if (reg & HISI_I2C_FIFO_STATE_TX_WERR)
+                       dev_err(ctlr->dev, "tx fifo error write\n");
+       }
+}
+
+static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr)
+{
+       struct i2c_msg *msg = ctlr->msgs;
+       u32 reg;
+
+       reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
+       reg &= ~HISI_I2C_FRAME_CTRL_ADDR_TEN;
+       if (msg->flags & I2C_M_TEN)
+               reg |= HISI_I2C_FRAME_CTRL_ADDR_TEN;
+       writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
+
+       reg = readl(ctlr->iobase + HISI_I2C_SLV_ADDR);
+       reg &= ~HISI_I2C_SLV_ADDR_VAL;
+       reg |= FIELD_PREP(HISI_I2C_SLV_ADDR_VAL, msg->addr);
+       writel(reg, ctlr->iobase + HISI_I2C_SLV_ADDR);
+
+       reg = readl(ctlr->iobase + HISI_I2C_FIFO_CTRL);
+       reg |= HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR;
+       writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
+       reg &= ~(HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR);
+       writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
+
+       hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
+       hisi_i2c_enable_int(ctlr, HISI_I2C_INT_ALL);
+
+       return 0;
+}
+
+static void hisi_i2c_reset_xfer(struct hisi_i2c_controller *ctlr)
+{
+       ctlr->msg_num = 0;
+       ctlr->xfer_err = 0;
+       ctlr->msg_tx_idx = 0;
+       ctlr->msg_rx_idx = 0;
+       ctlr->buf_tx_idx = 0;
+       ctlr->buf_rx_idx = 0;
+}
+
+/*
+ * Initialize the transfer information and start the I2C bus transfer.
+ * We only configure the transfer and do some pre/post works here, and
+ * wait for the transfer done. The major transfer process is performed
+ * in the IRQ handler.
+ */
+static int hisi_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+                               int num)
+{
+       struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap);
+       DECLARE_COMPLETION_ONSTACK(done);
+       int ret = num;
+
+       hisi_i2c_reset_xfer(ctlr);
+       ctlr->completion = &done;
+       ctlr->msg_num = num;
+       ctlr->msgs = msgs;
+
+       hisi_i2c_start_xfer(ctlr);
+
+       if (!wait_for_completion_timeout(ctlr->completion, adap->timeout)) {
+               hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
+               synchronize_irq(ctlr->irq);
+               i2c_recover_bus(&ctlr->adapter);
+               dev_err(ctlr->dev, "bus transfer timeout\n");
+               ret = -EIO;
+       }
+
+       if (ctlr->xfer_err) {
+               hisi_i2c_handle_errors(ctlr);
+               ret = -EIO;
+       }
+
+       hisi_i2c_reset_xfer(ctlr);
+       ctlr->completion = NULL;
+
+       return ret;
+}
+
+static u32 hisi_i2c_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm hisi_i2c_algo = {
+       .master_xfer    = hisi_i2c_master_xfer,
+       .functionality  = hisi_i2c_functionality,
+};
+
+static int hisi_i2c_read_rx_fifo(struct hisi_i2c_controller *ctlr)
+{
+       struct i2c_msg *cur_msg;
+       u32 fifo_state;
+
+       while (ctlr->msg_rx_idx < ctlr->msg_num) {
+               cur_msg = ctlr->msgs + ctlr->msg_rx_idx;
+
+               if (!(cur_msg->flags & I2C_M_RD)) {
+                       ctlr->msg_rx_idx++;
+                       continue;
+               }
+
+               fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+               while (!(fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) &&
+                      ctlr->buf_rx_idx < cur_msg->len) {
+                       cur_msg->buf[ctlr->buf_rx_idx++] = readl(ctlr->iobase + HISI_I2C_RXDATA);
+                       fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+               }
+
+               if (ctlr->buf_rx_idx == cur_msg->len) {
+                       ctlr->buf_rx_idx = 0;
+                       ctlr->msg_rx_idx++;
+               }
+
+               if (fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY)
+                       break;
+       }
+
+       return 0;
+}
+
+static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr)
+{
+       int max_write = HISI_I2C_TX_FIFO_DEPTH;
+       bool need_restart = false, last_msg;
+       struct i2c_msg *cur_msg;
+       u32 cmd, fifo_state;
+
+       while (ctlr->msg_tx_idx < ctlr->msg_num) {
+               cur_msg = ctlr->msgs + ctlr->msg_tx_idx;
+               last_msg = (ctlr->msg_tx_idx == ctlr->msg_num - 1);
+
+               /* Signal the SR bit when we start transferring a new message */
+               if (ctlr->msg_tx_idx && !ctlr->buf_tx_idx)
+                       need_restart = true;
+
+               fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+               while (!(fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) &&
+                      ctlr->buf_tx_idx < cur_msg->len && max_write) {
+                       cmd = 0;
+
+                       if (need_restart) {
+                               cmd |= HISI_I2C_CMD_TXDATA_SR_EN;
+                               need_restart = false;
+                       }
+
+                       /* Signal the STOP bit at the last frame of the last message */
+                       if (ctlr->buf_tx_idx == cur_msg->len - 1 && last_msg)
+                               cmd |= HISI_I2C_CMD_TXDATA_P_EN;
+
+                       if (cur_msg->flags & I2C_M_RD)
+                               cmd |= HISI_I2C_CMD_TXDATA_RW;
+                       else
+                               cmd |= FIELD_PREP(HISI_I2C_CMD_TXDATA_DATA,
+                                                 cur_msg->buf[ctlr->buf_tx_idx]);
+
+                       writel(cmd, ctlr->iobase + HISI_I2C_CMD_TXDATA);
+                       ctlr->buf_tx_idx++;
+                       max_write--;
+
+                       fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
+               }
+
+               /* Update the transfer index after per message transfer is done. */
+               if (ctlr->buf_tx_idx == cur_msg->len) {
+                       ctlr->buf_tx_idx = 0;
+                       ctlr->msg_tx_idx++;
+               }
+
+               if ((fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) ||
+                   max_write == 0)
+                       break;
+       }
+}
+
+static irqreturn_t hisi_i2c_irq(int irq, void *context)
+{
+       struct hisi_i2c_controller *ctlr = context;
+       u32 int_stat;
+
+       int_stat = readl(ctlr->iobase + HISI_I2C_INT_MSTAT);
+       hisi_i2c_clear_int(ctlr, int_stat);
+       if (!(int_stat & HISI_I2C_INT_ALL))
+               return IRQ_NONE;
+
+       if (int_stat & HISI_I2C_INT_TX_EMPTY)
+               hisi_i2c_xfer_msg(ctlr);
+
+       if (int_stat & HISI_I2C_INT_ERR) {
+               ctlr->xfer_err = int_stat;
+               goto out;
+       }
+
+       /* Drain the rx fifo before finish the transfer */
+       if (int_stat & (HISI_I2C_INT_TRANS_CPLT | HISI_I2C_INT_RX_FULL))
+               hisi_i2c_read_rx_fifo(ctlr);
+
+out:
+       if (int_stat & HISI_I2C_INT_TRANS_CPLT || ctlr->xfer_err) {
+               hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
+               hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
+               complete(ctlr->completion);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Helper function for calculating and configuring the HIGH and LOW
+ * periods of SCL clock. The caller will pass the ratio of the
+ * counts (divide / divisor) according to the target speed mode,
+ * and the target registers.
+ */
+static void hisi_i2c_set_scl(struct hisi_i2c_controller *ctlr,
+                            u32 divide, u32 divisor,
+                            u32 reg_hcnt, u32 reg_lcnt)
+{
+       u32 total_cnt, t_scl_hcnt, t_scl_lcnt, scl_fall_cnt, scl_rise_cnt;
+       u32 scl_hcnt, scl_lcnt;
+
+       /* Total SCL clock cycles per speed period */
+       total_cnt = DIV_ROUND_UP_ULL(ctlr->clk_rate_khz * HZ_PER_KHZ, ctlr->t.bus_freq_hz);
+       /* Total HIGH level SCL clock cycles including edges */
+       t_scl_hcnt = DIV_ROUND_UP_ULL(total_cnt * divide, divisor);
+       /* Total LOW level SCL clock cycles including edges */
+       t_scl_lcnt = total_cnt - t_scl_hcnt;
+       /* Fall edge SCL clock cycles */
+       scl_fall_cnt = NSEC_TO_CYCLES(ctlr->t.scl_fall_ns, ctlr->clk_rate_khz);
+       /* Rise edge SCL clock cycles */
+       scl_rise_cnt = NSEC_TO_CYCLES(ctlr->t.scl_rise_ns, ctlr->clk_rate_khz);
+
+       /* Calculated HIGH and LOW periods of SCL clock */
+       scl_hcnt = t_scl_hcnt - ctlr->spk_len - 7 - scl_fall_cnt;
+       scl_lcnt = t_scl_lcnt - 1 - scl_rise_cnt;
+
+       writel(scl_hcnt, ctlr->iobase + reg_hcnt);
+       writel(scl_lcnt, ctlr->iobase + reg_lcnt);
+}
+
+static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr)
+{
+       u32 reg, sda_hold_cnt, speed_mode;
+
+       i2c_parse_fw_timings(ctlr->dev, &ctlr->t, true);
+       ctlr->spk_len = NSEC_TO_CYCLES(ctlr->t.digital_filter_width_ns, ctlr->clk_rate_khz);
+
+       switch (ctlr->t.bus_freq_hz) {
+       case I2C_MAX_FAST_MODE_FREQ:
+               speed_mode = HISI_I2C_FAST_SPEED_MODE;
+               hisi_i2c_set_scl(ctlr, 26, 76, HISI_I2C_FS_SCL_HCNT, HISI_I2C_FS_SCL_LCNT);
+               break;
+       case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+               speed_mode = HISI_I2C_HIGH_SPEED_MODE;
+               hisi_i2c_set_scl(ctlr, 6, 22, HISI_I2C_HS_SCL_HCNT, HISI_I2C_HS_SCL_LCNT);
+               break;
+       case I2C_MAX_STANDARD_MODE_FREQ:
+       default:
+               speed_mode = HISI_I2C_STD_SPEED_MODE;
+
+               /* For default condition force the bus speed to standard mode. */
+               ctlr->t.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
+               hisi_i2c_set_scl(ctlr, 40, 87, HISI_I2C_SS_SCL_HCNT, HISI_I2C_SS_SCL_LCNT);
+               break;
+       }
+
+       reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
+       reg &= ~HISI_I2C_FRAME_CTRL_SPEED_MODE;
+       reg |= FIELD_PREP(HISI_I2C_FRAME_CTRL_SPEED_MODE, speed_mode);
+       writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
+
+       sda_hold_cnt = NSEC_TO_CYCLES(ctlr->t.sda_hold_ns, ctlr->clk_rate_khz);
+
+       reg = FIELD_PREP(HISI_I2C_SDA_HOLD_TX, sda_hold_cnt);
+       writel(reg, ctlr->iobase + HISI_I2C_SDA_HOLD);
+
+       writel(ctlr->spk_len, ctlr->iobase + HISI_I2C_FS_SPK_LEN);
+
+       reg = FIELD_PREP(HISI_I2C_FIFO_RX_AF_THRESH, HISI_I2C_RX_F_AF_THRESH);
+       reg |= FIELD_PREP(HISI_I2C_FIFO_TX_AE_THRESH, HISI_I2C_TX_F_AE_THRESH);
+       writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
+}
+
+static int hisi_i2c_probe(struct platform_device *pdev)
+{
+       struct hisi_i2c_controller *ctlr;
+       struct device *dev = &pdev->dev;
+       struct i2c_adapter *adapter;
+       u64 clk_rate_hz;
+       u32 hw_version;
+       int ret;
+
+       ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL);
+       if (!ctlr)
+               return -ENOMEM;
+
+       ctlr->iobase = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(ctlr->iobase))
+               return PTR_ERR(ctlr->iobase);
+
+       ctlr->irq = platform_get_irq(pdev, 0);
+       if (ctlr->irq < 0)
+               return ctlr->irq;
+
+       ctlr->dev = dev;
+
+       hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
+
+       ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq, 0, "hisi-i2c", ctlr);
+       if (ret) {
+               dev_err(dev, "failed to request irq handler, ret = %d\n", ret);
+               return ret;
+       }
+
+       ret = device_property_read_u64(dev, "clk_rate", &clk_rate_hz);
+       if (ret) {
+               dev_err(dev, "failed to get clock frequency, ret = %d\n", ret);
+               return ret;
+       }
+
+       ctlr->clk_rate_khz = DIV_ROUND_UP_ULL(clk_rate_hz, HZ_PER_KHZ);
+
+       hisi_i2c_configure_bus(ctlr);
+
+       adapter = &ctlr->adapter;
+       snprintf(adapter->name, sizeof(adapter->name),
+                "HiSilicon I2C Controller %s", dev_name(dev));
+       adapter->owner = THIS_MODULE;
+       adapter->algo = &hisi_i2c_algo;
+       adapter->dev.parent = dev;
+       i2c_set_adapdata(adapter, ctlr);
+
+       ret = devm_i2c_add_adapter(dev, adapter);
+       if (ret)
+               return ret;
+
+       hw_version = readl(ctlr->iobase + HISI_I2C_VERSION);
+       dev_info(ctlr->dev, "speed mode is %s. hw version 0x%x\n",
+                i2c_freq_mode_string(ctlr->t.bus_freq_hz), hw_version);
+
+       return 0;
+}
+
+static const struct acpi_device_id hisi_i2c_acpi_ids[] = {
+       { "HISI03D1", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids);
+
+static struct platform_driver hisi_i2c_driver = {
+       .probe          = hisi_i2c_probe,
+       .driver         = {
+               .name   = "hisi-i2c",
+               .acpi_match_table = hisi_i2c_acpi_ids,
+       },
+};
+module_platform_driver(hisi_i2c_driver);
+
+MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>");
+MODULE_DESCRIPTION("HiSilicon I2C Controller Driver");
+MODULE_LICENSE("GPL");
index 4acee6f..99d4467 100644 (file)
@@ -73,6 +73,7 @@
  * Comet Lake-V (PCH)          0xa3a3  32      hard    yes     yes     yes
  * Alder Lake-S (PCH)          0x7aa3  32      hard    yes     yes     yes
  * Alder Lake-P (PCH)          0x51a3  32      hard    yes     yes     yes
+ * Alder Lake-M (PCH)          0x54a3  32      hard    yes     yes     yes
  *
  * Features supported by this driver:
  * Software PEC                                no
 #define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS         0x4b23
 #define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS          0x4da3
 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS         0x51a3
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS         0x54a3
 #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS              0x5ad4
 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS         0x7aa3
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS            0x8c22
@@ -1087,6 +1089,7 @@ static const struct pci_device_id i801_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS) },
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS) },
        { 0, }
 };
 
@@ -1771,6 +1774,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
        case PCI_DEVICE_ID_INTEL_EBG_SMBUS:
        case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS:
        case PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS:
+       case PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS:
                priv->features |= FEATURE_BLOCK_PROC;
                priv->features |= FEATURE_I2C_BLOCK_READ;
                priv->features |= FEATURE_IRQ;
index 66c9923..c8c422e 100644 (file)
@@ -54,7 +54,6 @@ struct icy_i2c {
 
        void __iomem *reg_s0;
        void __iomem *reg_s1;
-       struct fwnode_handle *ltc2990_fwnode;
        struct i2c_client *ltc2990_client;
 };
 
@@ -115,6 +114,10 @@ static const struct property_entry icy_ltc2990_props[] = {
        { }
 };
 
+static const struct software_node icy_ltc2990_node = {
+       .properties = icy_ltc2990_props,
+};
+
 static int icy_probe(struct zorro_dev *z,
                     const struct zorro_device_id *ent)
 {
@@ -123,6 +126,7 @@ static int icy_probe(struct zorro_dev *z,
        struct fwnode_handle *new_fwnode;
        struct i2c_board_info ltc2990_info = {
                .type           = "ltc2990",
+               .swnode         = &icy_ltc2990_node,
        };
 
        i2c = devm_kzalloc(&z->dev, sizeof(*i2c), GFP_KERNEL);
@@ -174,26 +178,10 @@ static int icy_probe(struct zorro_dev *z,
         *
         * See property_entry above for in1, in2, temp3.
         */
-       new_fwnode = fwnode_create_software_node(icy_ltc2990_props, NULL);
-       if (IS_ERR(new_fwnode)) {
-               dev_info(&z->dev, "Failed to create fwnode for LTC2990, error: %ld\n",
-                        PTR_ERR(new_fwnode));
-       } else {
-               /*
-                * Store the fwnode so we can destroy it on .remove().
-                * Only store it on success, as fwnode_remove_software_node()
-                * is NULL safe, but not PTR_ERR safe.
-                */
-               i2c->ltc2990_fwnode = new_fwnode;
-               ltc2990_info.fwnode = new_fwnode;
-
-               i2c->ltc2990_client =
-                       i2c_new_scanned_device(&i2c->adapter,
-                                              &ltc2990_info,
-                                              icy_ltc2990_addresses,
-                                              NULL);
-       }
-
+       i2c->ltc2990_client = i2c_new_scanned_device(&i2c->adapter,
+                                                    &ltc2990_info,
+                                                    icy_ltc2990_addresses,
+                                                    NULL);
        return 0;
 }
 
@@ -202,8 +190,6 @@ static void icy_remove(struct zorro_dev *z)
        struct icy_i2c *i2c = dev_get_drvdata(&z->dev);
 
        i2c_unregister_device(i2c->ltc2990_client);
-       fwnode_remove_software_node(i2c->ltc2990_fwnode);
-
        i2c_del_adapter(&i2c->adapter);
 }
 
index 98a8930..8e98794 100644 (file)
@@ -1057,7 +1057,7 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
                        atomic = true;
        }
 
-       ret = pm_runtime_get_sync(adap->dev.parent);
+       ret = pm_runtime_resume_and_get(adap->dev.parent);
        if (ret < 0)
                return ret;
 
@@ -1158,7 +1158,7 @@ static int img_i2c_init(struct img_i2c *i2c)
        u32 rev;
        int ret;
 
-       ret = pm_runtime_get_sync(i2c->adap.dev.parent);
+       ret = pm_runtime_resume_and_get(i2c->adap.dev.parent);
        if (ret < 0)
                return ret;
 
index 9db6ccd..8b9ba05 100644 (file)
@@ -259,7 +259,7 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
        unsigned int temp;
        int ret;
 
-       ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent);
+       ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent);
        if (ret < 0)
                return ret;
 
index b80fdc1..dc5ca71 100644 (file)
@@ -801,7 +801,7 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
        i2c_imx->last_slave_event = I2C_SLAVE_STOP;
 
        /* Resume */
-       ret = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
+       ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
        if (ret < 0) {
                dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
                return ret;
@@ -1253,7 +1253,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
        struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
        int result;
 
-       result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
+       result = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
        if (result < 0)
                return result;
 
@@ -1496,7 +1496,7 @@ static int i2c_imx_remove(struct platform_device *pdev)
        struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
        int irq, ret;
 
-       ret = pm_runtime_get_sync(&pdev->dev);
+       ret = pm_runtime_resume_and_get(&pdev->dev);
        if (ret < 0)
                return ret;
 
index 2f8b805..cfecaf1 100644 (file)
@@ -125,10 +125,12 @@ iop3xx_i2c_error(u32 sr)
        int rc = 0;
 
        if ((sr & IOP3XX_ISR_BERRD)) {
-               if ( !rc ) rc = -I2C_ERR_BERR;
+               if (!rc)
+                       rc = -I2C_ERR_BERR;
        }
        if ((sr & IOP3XX_ISR_ALD)) {
-               if ( !rc ) rc = -I2C_ERR_ALD;
+               if (!rc)
+                       rc = -I2C_ERR_ALD;
        }
        return rc;
 }
@@ -151,12 +153,12 @@ iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap)
  * sleep until interrupted, then recover and analyse the SR
  * saved by handler
  */
-typedef int (* compare_func)(unsigned test, unsigned mask);
+typedef int (*compare_func)(unsigned test, unsigned mask);
 /* returns 1 on correct comparison */
 
 static int
 iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
-                         unsigned flags, unsignedstatus,
+                         unsigned flags, unsigned *status,
                          compare_func compare)
 {
        unsigned sr = 0;
@@ -167,7 +169,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
        do {
                interrupted = wait_event_interruptible_timeout (
                        iop3xx_adap->waitq,
-                       (done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )),
+                       (done = compare(sr = iop3xx_i2c_get_srstat(iop3xx_adap), flags)),
                        1 * HZ
                        );
                if ((rc = iop3xx_i2c_error(sr)) < 0) {
@@ -177,7 +179,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
                        *status = sr;
                        return -ETIMEDOUT;
                }
-       } while(!done);
+       } while (!done);
 
        *status = sr;
 
@@ -204,7 +206,7 @@ iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
 {
        return iop3xx_i2c_wait_event(
                iop3xx_adap,
-               IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
+               IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
                status, any_bits_set);
 }
 
@@ -226,7 +228,7 @@ iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
 
 static int
 iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap,
-                               struct i2c_msgmsg)
+                               struct i2c_msg *msg)
 {
        unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
        int status;
@@ -273,7 +275,7 @@ iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte,
 }
 
 static int
-iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, charbyte,
+iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char *byte,
                                int stop)
 {
        unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
@@ -305,7 +307,7 @@ iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count)
        int rc = 0;
 
        for (ii = 0; rc == 0 && ii != count; ++ii)
-               rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1);
+               rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii == count-1);
        return rc;
 }
 
@@ -317,7 +319,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
        int rc = 0;
 
        for (ii = 0; rc == 0 && ii != count; ++ii)
-               rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1);
+               rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii == count-1);
 
        return rc;
 }
@@ -330,7 +332,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
  * condition.
  */
 static int
-iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msgpmsg)
+iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg)
 {
        struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
        int rc;
@@ -369,7 +371,7 @@ iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
 
        iop3xx_i2c_transaction_cleanup(iop3xx_adap);
 
-       if(ret)
+       if (ret)
                return ret;
 
        return im;
index 55177eb..baa7319 100644 (file)
@@ -825,7 +825,10 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
 
        jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0);
 
-       i2c->irq = platform_get_irq(pdev, 0);
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0)
+               goto err;
+       i2c->irq = ret;
        ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0,
                               dev_name(&pdev->dev), i2c);
        if (ret)
index 2fb0532..8716032 100644 (file)
 #define MLXBF_I2C_SMBUS_THIGH_MAX_TBUF            0x14
 #define MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT           0x18
 
-enum {
-       MLXBF_I2C_TIMING_100KHZ = 100000,
-       MLXBF_I2C_TIMING_400KHZ = 400000,
-       MLXBF_I2C_TIMING_1000KHZ = 1000000,
-};
-
 /*
  * Defines SMBus operating frequency and core clock frequency.
  * According to ADB files, default values are compliant to 100KHz SMBus
@@ -1202,7 +1196,7 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev,
 
        ret = device_property_read_u32(dev, "clock-frequency", &config_khz);
        if (ret < 0)
-               config_khz = MLXBF_I2C_TIMING_100KHZ;
+               config_khz = I2C_MAX_STANDARD_MODE_FREQ;
 
        switch (config_khz) {
        default:
@@ -1210,15 +1204,15 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev,
                pr_warn("Illegal value %d: defaulting to 100 KHz\n",
                        config_khz);
                fallthrough;
-       case MLXBF_I2C_TIMING_100KHZ:
+       case I2C_MAX_STANDARD_MODE_FREQ:
                config_idx = MLXBF_I2C_TIMING_CONFIG_100KHZ;
                break;
 
-       case MLXBF_I2C_TIMING_400KHZ:
+       case I2C_MAX_FAST_MODE_FREQ:
                config_idx = MLXBF_I2C_TIMING_CONFIG_400KHZ;
                break;
 
-       case MLXBF_I2C_TIMING_1000KHZ:
+       case I2C_MAX_FAST_MODE_PLUS_FREQ:
                config_idx = MLXBF_I2C_TIMING_CONFIG_1000KHZ;
                break;
        }
@@ -2376,6 +2370,8 @@ static int mlxbf_i2c_probe(struct platform_device *pdev)
        mlxbf_i2c_init_slave(pdev, priv);
 
        irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
        ret = devm_request_irq(dev, irq, mlxbf_smbus_irq,
                               IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED,
                               dev_name(dev), priv);
index d94f05c..30d9e89 100644 (file)
@@ -1,16 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * (C) Copyright 2003-2004
- * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
-
  * This is a combined i2c adapter and algorithm driver for the
  * MPC107/Tsi107 PowerPC northbridge and processors that include
  * the same I2C unit (8240, 8245, 85xx).
  *
- * Release 0.8
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
+ * Copyright (C) 2003-2004 Humboldt Solutions Ltd, adrian@humboldt.co.uk
+ * Copyright (C) 2021 Allied Telesis Labs
  */
 
 #include <linux/kernel.h>
@@ -19,6 +14,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
+#include <linux/property.h>
 #include <linux/slab.h>
 
 #include <linux/clk.h>
 #define CSR_MIF  0x02
 #define CSR_RXAK 0x01
 
+enum mpc_i2c_action {
+       MPC_I2C_ACTION_START = 1,
+       MPC_I2C_ACTION_RESTART,
+       MPC_I2C_ACTION_READ_BEGIN,
+       MPC_I2C_ACTION_READ_BYTE,
+       MPC_I2C_ACTION_WRITE,
+       MPC_I2C_ACTION_STOP,
+
+       __MPC_I2C_ACTION_CNT
+};
+
+static const char * const action_str[] = {
+       "invalid",
+       "start",
+       "restart",
+       "read begin",
+       "read",
+       "write",
+       "stop",
+};
+
+static_assert(ARRAY_SIZE(action_str) == __MPC_I2C_ACTION_CNT);
+
 struct mpc_i2c {
        struct device *dev;
        void __iomem *base;
        u32 interrupt;
-       wait_queue_head_t queue;
+       wait_queue_head_t waitq;
+       spinlock_t lock;
        struct i2c_adapter adap;
        int irq;
        u32 real_clk;
-#ifdef CONFIG_PM_SLEEP
        u8 fdr, dfsrr;
-#endif
        struct clk *clk_per;
+       u32 cntl_bits;
+       enum mpc_i2c_action action;
+       struct i2c_msg *msgs;
+       int num_msgs;
+       int curr_msg;
+       u32 byte_posn;
+       u32 block;
+       int rc;
+       int expect_rxack;
+
 };
 
 struct mpc_i2c_divider {
@@ -86,19 +114,6 @@ static inline void writeccr(struct mpc_i2c *i2c, u32 x)
        writeb(x, i2c->base + MPC_I2C_CR);
 }
 
-static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
-{
-       struct mpc_i2c *i2c = dev_id;
-       if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) {
-               /* Read again to allow register to stabilise */
-               i2c->interrupt = readb(i2c->base + MPC_I2C_SR);
-               writeb(0, i2c->base + MPC_I2C_SR);
-               wake_up(&i2c->queue);
-               return IRQ_HANDLED;
-       }
-       return IRQ_NONE;
-}
-
 /* Sometimes 9th clock pulse isn't generated, and slave doesn't release
  * the bus, because it wants to send ACK.
  * Following sequence of enabling/disabling and sending start/stop generates
@@ -121,61 +136,6 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c)
        }
 }
 
-static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
-{
-       unsigned long orig_jiffies = jiffies;
-       u32 cmd_err;
-       int result = 0;
-
-       if (!i2c->irq) {
-               while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
-                       schedule();
-                       if (time_after(jiffies, orig_jiffies + timeout)) {
-                               dev_dbg(i2c->dev, "timeout\n");
-                               writeccr(i2c, 0);
-                               result = -ETIMEDOUT;
-                               break;
-                       }
-               }
-               cmd_err = readb(i2c->base + MPC_I2C_SR);
-               writeb(0, i2c->base + MPC_I2C_SR);
-       } else {
-               /* Interrupt mode */
-               result = wait_event_timeout(i2c->queue,
-                       (i2c->interrupt & CSR_MIF), timeout);
-
-               if (unlikely(!(i2c->interrupt & CSR_MIF))) {
-                       dev_dbg(i2c->dev, "wait timeout\n");
-                       writeccr(i2c, 0);
-                       result = -ETIMEDOUT;
-               }
-
-               cmd_err = i2c->interrupt;
-               i2c->interrupt = 0;
-       }
-
-       if (result < 0)
-               return result;
-
-       if (!(cmd_err & CSR_MCF)) {
-               dev_dbg(i2c->dev, "unfinished\n");
-               return -EIO;
-       }
-
-       if (cmd_err & CSR_MAL) {
-               dev_dbg(i2c->dev, "MAL\n");
-               return -EAGAIN;
-       }
-
-       if (writing && (cmd_err & CSR_RXAK)) {
-               dev_dbg(i2c->dev, "No RXAK\n");
-               /* generate stop */
-               writeccr(i2c, CCR_MEN);
-               return -ENXIO;
-       }
-       return 0;
-}
-
 #if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x)
 static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
        {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
@@ -415,7 +375,7 @@ static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock,
        }
 
        *real_clk = fsl_get_sys_freq() / prescaler / div->divider;
-       return div ? (int)div->fdr : -EINVAL;
+       return (int)div->fdr;
 }
 
 static void mpc_i2c_setup_8xxx(struct device_node *node,
@@ -450,168 +410,209 @@ static void mpc_i2c_setup_8xxx(struct device_node *node,
 }
 #endif /* CONFIG_FSL_SOC */
 
-static void mpc_i2c_start(struct mpc_i2c *i2c)
+static void mpc_i2c_finish(struct mpc_i2c *i2c, int rc)
 {
-       /* Clear arbitration */
-       writeb(0, i2c->base + MPC_I2C_SR);
-       /* Start with MEN */
-       writeccr(i2c, CCR_MEN);
+       i2c->rc = rc;
+       i2c->block = 0;
+       i2c->cntl_bits = CCR_MEN;
+       writeccr(i2c, i2c->cntl_bits);
+       wake_up(&i2c->waitq);
 }
 
-static void mpc_i2c_stop(struct mpc_i2c *i2c)
+static void mpc_i2c_do_action(struct mpc_i2c *i2c)
 {
-       writeccr(i2c, CCR_MEN);
-}
+       struct i2c_msg *msg = &i2c->msgs[i2c->curr_msg];
+       int dir = 0;
+       int recv_len = 0;
+       u8 byte;
+
+       dev_dbg(i2c->dev, "action = %s\n", action_str[i2c->action]);
+
+       i2c->cntl_bits &= ~(CCR_RSTA | CCR_MTX | CCR_TXAK);
+
+       if (msg->flags & I2C_M_RD)
+               dir = 1;
+       if (msg->flags & I2C_M_RECV_LEN)
+               recv_len = 1;
+
+       switch (i2c->action) {
+       case MPC_I2C_ACTION_RESTART:
+               i2c->cntl_bits |= CCR_RSTA;
+               fallthrough;
+
+       case MPC_I2C_ACTION_START:
+               i2c->cntl_bits |= CCR_MSTA | CCR_MTX;
+               writeccr(i2c, i2c->cntl_bits);
+               writeb((msg->addr << 1) | dir, i2c->base + MPC_I2C_DR);
+               i2c->expect_rxack = 1;
+               i2c->action = dir ? MPC_I2C_ACTION_READ_BEGIN : MPC_I2C_ACTION_WRITE;
+               break;
+
+       case MPC_I2C_ACTION_READ_BEGIN:
+               if (msg->len) {
+                       if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN))
+                               i2c->cntl_bits |= CCR_TXAK;
+
+                       writeccr(i2c, i2c->cntl_bits);
+                       /* Dummy read */
+                       readb(i2c->base + MPC_I2C_DR);
+               }
+               i2c->action = MPC_I2C_ACTION_READ_BYTE;
+               break;
+
+       case MPC_I2C_ACTION_READ_BYTE:
+               if (i2c->byte_posn || !recv_len) {
+                       /* Generate Tx ACK on next to last byte */
+                       if (i2c->byte_posn == msg->len - 2)
+                               i2c->cntl_bits |= CCR_TXAK;
+                       /* Do not generate stop on last byte */
+                       if (i2c->byte_posn == msg->len - 1)
+                               i2c->cntl_bits |= CCR_MTX;
 
-static int mpc_write(struct mpc_i2c *i2c, int target,
-                    const u8 *data, int length, int restart)
-{
-       int i, result;
-       unsigned timeout = i2c->adap.timeout;
-       u32 flags = restart ? CCR_RSTA : 0;
+                       writeccr(i2c, i2c->cntl_bits);
+               }
 
-       /* Start as master */
-       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
-       /* Write target byte */
-       writeb((target << 1), i2c->base + MPC_I2C_DR);
+               byte = readb(i2c->base + MPC_I2C_DR);
 
-       result = i2c_wait(i2c, timeout, 1);
-       if (result < 0)
-               return result;
+               if (i2c->byte_posn == 0 && recv_len) {
+                       if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) {
+                               mpc_i2c_finish(i2c, -EPROTO);
+                               return;
+                       }
+                       msg->len += byte;
+                       /*
+                        * For block reads, generate Tx ACK here if data length
+                        * is 1 byte (total length is 2 bytes).
+                        */
+                       if (msg->len == 2) {
+                               i2c->cntl_bits |= CCR_TXAK;
+                               writeccr(i2c, i2c->cntl_bits);
+                       }
+               }
+
+               dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], byte);
+               msg->buf[i2c->byte_posn++] = byte;
+               break;
+
+       case MPC_I2C_ACTION_WRITE:
+               dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action],
+                       msg->buf[i2c->byte_posn]);
+               writeb(msg->buf[i2c->byte_posn++], i2c->base + MPC_I2C_DR);
+               i2c->expect_rxack = 1;
+               break;
 
-       for (i = 0; i < length; i++) {
-               /* Write data byte */
-               writeb(data[i], i2c->base + MPC_I2C_DR);
+       case MPC_I2C_ACTION_STOP:
+               mpc_i2c_finish(i2c, 0);
+               break;
 
-               result = i2c_wait(i2c, timeout, 1);
-               if (result < 0)
-                       return result;
+       default:
+               WARN(1, "Unexpected action %d\n", i2c->action);
+               break;
        }
 
-       return 0;
+       if (msg->len == i2c->byte_posn) {
+               i2c->curr_msg++;
+               i2c->byte_posn = 0;
+
+               if (i2c->curr_msg == i2c->num_msgs) {
+                       i2c->action = MPC_I2C_ACTION_STOP;
+                       /*
+                        * We don't get another interrupt on read so
+                        * finish the transfer now
+                        */
+                       if (dir)
+                               mpc_i2c_finish(i2c, 0);
+               } else {
+                       i2c->action = MPC_I2C_ACTION_RESTART;
+               }
+       }
 }
 
-static int mpc_read(struct mpc_i2c *i2c, int target,
-                   u8 *data, int length, int restart, bool recv_len)
+static void mpc_i2c_do_intr(struct mpc_i2c *i2c, u8 status)
 {
-       unsigned timeout = i2c->adap.timeout;
-       int i, result;
-       u32 flags = restart ? CCR_RSTA : 0;
+       spin_lock(&i2c->lock);
 
-       /* Switch to read - restart */
-       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
-       /* Write target address byte - this time with the read flag set */
-       writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
+       if (!(status & CSR_MCF)) {
+               dev_dbg(i2c->dev, "unfinished\n");
+               mpc_i2c_finish(i2c, -EIO);
+               goto out;
+       }
 
-       result = i2c_wait(i2c, timeout, 1);
-       if (result < 0)
-               return result;
+       if (status & CSR_MAL) {
+               dev_dbg(i2c->dev, "arbitration lost\n");
+               mpc_i2c_finish(i2c, -EAGAIN);
+               goto out;
+       }
 
-       if (length) {
-               if (length == 1 && !recv_len)
-                       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
-               else
-                       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
-               /* Dummy read */
-               readb(i2c->base + MPC_I2C_DR);
+       if (i2c->expect_rxack && (status & CSR_RXAK)) {
+               dev_dbg(i2c->dev, "no Rx ACK\n");
+               mpc_i2c_finish(i2c, -ENXIO);
+               goto out;
        }
+       i2c->expect_rxack = 0;
 
-       for (i = 0; i < length; i++) {
-               u8 byte;
-
-               result = i2c_wait(i2c, timeout, 0);
-               if (result < 0)
-                       return result;
-
-               /*
-                * For block reads, we have to know the total length (1st byte)
-                * before we can determine if we are done.
-                */
-               if (i || !recv_len) {
-                       /* Generate txack on next to last byte */
-                       if (i == length - 2)
-                               writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
-                                        | CCR_TXAK);
-                       /* Do not generate stop on last byte */
-                       if (i == length - 1)
-                               writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
-                                        | CCR_MTX);
-               }
+       mpc_i2c_do_action(i2c);
 
-               byte = readb(i2c->base + MPC_I2C_DR);
+out:
+       spin_unlock(&i2c->lock);
+}
 
-               /*
-                * Adjust length if first received byte is length.
-                * The length is 1 length byte plus actually data length
-                */
-               if (i == 0 && recv_len) {
-                       if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX)
-                               return -EPROTO;
-                       length += byte;
-                       /*
-                        * For block reads, generate txack here if data length
-                        * is 1 byte (total length is 2 bytes).
-                        */
-                       if (length == 2)
-                               writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
-                                        | CCR_TXAK);
-               }
-               data[i] = byte;
+static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
+{
+       struct mpc_i2c *i2c = dev_id;
+       u8 status;
+
+       status = readb(i2c->base + MPC_I2C_SR);
+       if (status & CSR_MIF) {
+               writeb(0, i2c->base + MPC_I2C_SR);
+               mpc_i2c_do_intr(i2c, status);
+               return IRQ_HANDLED;
        }
+       return IRQ_NONE;
+}
 
-       return length;
+static int mpc_i2c_wait_for_completion(struct mpc_i2c *i2c)
+{
+       long time_left;
+
+       time_left = wait_event_timeout(i2c->waitq, !i2c->block, i2c->adap.timeout);
+       if (!time_left)
+               return -ETIMEDOUT;
+       if (time_left < 0)
+               return time_left;
+
+       return 0;
 }
 
-static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+static int mpc_i2c_execute_msg(struct mpc_i2c *i2c)
 {
-       struct i2c_msg *pmsg;
-       int i;
-       int ret = 0;
-       unsigned long orig_jiffies = jiffies;
-       struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+       unsigned long orig_jiffies;
+       unsigned long flags;
+       int ret;
 
-       mpc_i2c_start(i2c);
+       spin_lock_irqsave(&i2c->lock, flags);
 
-       /* Allow bus up to 1s to become not busy */
-       while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
-               if (signal_pending(current)) {
-                       dev_dbg(i2c->dev, "Interrupted\n");
-                       writeccr(i2c, 0);
-                       return -EINTR;
-               }
-               if (time_after(jiffies, orig_jiffies + HZ)) {
-                       u8 status = readb(i2c->base + MPC_I2C_SR);
+       i2c->curr_msg = 0;
+       i2c->rc = 0;
+       i2c->byte_posn = 0;
+       i2c->block = 1;
+       i2c->action = MPC_I2C_ACTION_START;
 
-                       dev_dbg(i2c->dev, "timeout\n");
-                       if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) {
-                               writeb(status & ~CSR_MAL,
-                                      i2c->base + MPC_I2C_SR);
-                               mpc_i2c_fixup(i2c);
-                       }
-                       return -EIO;
-               }
-               schedule();
-       }
+       i2c->cntl_bits = CCR_MEN | CCR_MIEN;
+       writeb(0, i2c->base + MPC_I2C_SR);
+       writeccr(i2c, i2c->cntl_bits);
+
+       mpc_i2c_do_action(i2c);
+
+       spin_unlock_irqrestore(&i2c->lock, flags);
+
+       ret = mpc_i2c_wait_for_completion(i2c);
+       if (ret)
+               i2c->rc = ret;
+
+       if (i2c->rc == -EIO || i2c->rc == -EAGAIN || i2c->rc == -ETIMEDOUT)
+               i2c_recover_bus(&i2c->adap);
 
-       for (i = 0; ret >= 0 && i < num; i++) {
-               pmsg = &msgs[i];
-               dev_dbg(i2c->dev,
-                       "Doing %s %d bytes to 0x%02x - %d of %d messages\n",
-                       pmsg->flags & I2C_M_RD ? "read" : "write",
-                       pmsg->len, pmsg->addr, i + 1, num);
-               if (pmsg->flags & I2C_M_RD) {
-                       bool recv_len = pmsg->flags & I2C_M_RECV_LEN;
-
-                       ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i,
-                                      recv_len);
-                       if (recv_len && ret > 0)
-                               pmsg->len = ret;
-               } else {
-                       ret =
-                           mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
-               }
-       }
-       mpc_i2c_stop(i2c); /* Initiate STOP */
        orig_jiffies = jiffies;
        /* Wait until STOP is seen, allow up to 1 s */
        while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
@@ -622,13 +623,41 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
                        if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) {
                                writeb(status & ~CSR_MAL,
                                       i2c->base + MPC_I2C_SR);
-                               mpc_i2c_fixup(i2c);
+                               i2c_recover_bus(&i2c->adap);
                        }
                        return -EIO;
                }
                cond_resched();
        }
-       return (ret < 0) ? ret : num;
+
+       return i2c->rc;
+}
+
+static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       int rc, ret = num;
+       struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+       int i;
+
+       dev_dbg(i2c->dev, "num = %d\n", num);
+       for (i = 0; i < num; i++)
+               dev_dbg(i2c->dev, "  addr = %02x, flags = %02x, len = %d, %*ph\n",
+                       msgs[i].addr, msgs[i].flags, msgs[i].len,
+                       msgs[i].flags & I2C_M_RD ? 0 : msgs[i].len,
+                       msgs[i].buf);
+
+       WARN_ON(i2c->msgs != NULL);
+       i2c->msgs = msgs;
+       i2c->num_msgs = num;
+
+       rc = mpc_i2c_execute_msg(i2c);
+       if (rc < 0)
+               ret = rc;
+
+       i2c->num_msgs = 0;
+       i2c->msgs = NULL;
+
+       return ret;
 }
 
 static u32 mpc_functionality(struct i2c_adapter *adap)
@@ -637,6 +666,15 @@ static u32 mpc_functionality(struct i2c_adapter *adap)
          | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
 }
 
+static int fsl_i2c_bus_recovery(struct i2c_adapter *adap)
+{
+       struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+
+       mpc_i2c_fixup(i2c);
+
+       return 0;
+}
+
 static const struct i2c_algorithm mpc_algo = {
        .master_xfer = mpc_xfer,
        .functionality = mpc_functionality,
@@ -648,63 +686,61 @@ static struct i2c_adapter mpc_ops = {
        .timeout = HZ,
 };
 
-static const struct of_device_id mpc_i2c_of_match[];
+static struct i2c_bus_recovery_info fsl_i2c_recovery_info = {
+       .recover_bus = fsl_i2c_bus_recovery,
+};
+
 static int fsl_i2c_probe(struct platform_device *op)
 {
-       const struct of_device_id *match;
+       const struct mpc_i2c_data *data;
        struct mpc_i2c *i2c;
        const u32 *prop;
        u32 clock = MPC_I2C_CLOCK_LEGACY;
        int result = 0;
        int plen;
-       struct resource res;
        struct clk *clk;
        int err;
 
-       match = of_match_device(mpc_i2c_of_match, &op->dev);
-       if (!match)
-               return -EINVAL;
-
-       i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+       i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL);
        if (!i2c)
                return -ENOMEM;
 
        i2c->dev = &op->dev; /* for debug and error output */
 
-       init_waitqueue_head(&i2c->queue);
+       init_waitqueue_head(&i2c->waitq);
+       spin_lock_init(&i2c->lock);
 
-       i2c->base = of_iomap(op->dev.of_node, 0);
-       if (!i2c->base) {
-               dev_err(i2c->dev, "failed to map controller\n");
-               result = -ENOMEM;
-               goto fail_map;
-       }
+       i2c->base = devm_platform_ioremap_resource(op, 0);
+       if (IS_ERR(i2c->base))
+               return PTR_ERR(i2c->base);
 
-       i2c->irq = irq_of_parse_and_map(op->dev.of_node, 0);
-       if (i2c->irq) { /* no i2c->irq implies polling */
-               result = request_irq(i2c->irq, mpc_i2c_isr,
-                                    IRQF_SHARED, "i2c-mpc", i2c);
-               if (result < 0) {
-                       dev_err(i2c->dev, "failed to attach interrupt\n");
-                       goto fail_request;
-               }
+       i2c->irq = platform_get_irq(op, 0);
+       if (i2c->irq < 0)
+               return i2c->irq;
+
+       result = devm_request_irq(&op->dev, i2c->irq, mpc_i2c_isr,
+                       IRQF_SHARED, "i2c-mpc", i2c);
+       if (result < 0) {
+               dev_err(i2c->dev, "failed to attach interrupt\n");
+               return result;
        }
 
        /*
         * enable clock for the I2C peripheral (non fatal),
         * keep a reference upon successful allocation
         */
-       clk = devm_clk_get(&op->dev, NULL);
-       if (!IS_ERR(clk)) {
-               err = clk_prepare_enable(clk);
-               if (err) {
-                       dev_err(&op->dev, "failed to enable clock\n");
-                       goto fail_request;
-               } else {
-                       i2c->clk_per = clk;
-               }
+       clk = devm_clk_get_optional(&op->dev, NULL);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       err = clk_prepare_enable(clk);
+       if (err) {
+               dev_err(&op->dev, "failed to enable clock\n");
+               return err;
        }
 
+       i2c->clk_per = clk;
+
        if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) {
                clock = MPC_I2C_CLOCK_PRESERVE;
        } else {
@@ -714,8 +750,8 @@ static int fsl_i2c_probe(struct platform_device *op)
                        clock = *prop;
        }
 
-       if (match->data) {
-               const struct mpc_i2c_data *data = match->data;
+       data = device_get_match_data(&op->dev);
+       if (data) {
                data->setup(op->dev.of_node, i2c, clock);
        } else {
                /* Backwards compatibility */
@@ -731,31 +767,25 @@ static int fsl_i2c_probe(struct platform_device *op)
        }
        dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ);
 
-       platform_set_drvdata(op, i2c);
-
        i2c->adap = mpc_ops;
-       of_address_to_resource(op->dev.of_node, 0, &res);
        scnprintf(i2c->adap.name, sizeof(i2c->adap.name),
-                 "MPC adapter at 0x%llx", (unsigned long long)res.start);
-       i2c_set_adapdata(&i2c->adap, i2c);
+                 "MPC adapter (%s)", of_node_full_name(op->dev.of_node));
        i2c->adap.dev.parent = &op->dev;
+       i2c->adap.nr = op->id;
        i2c->adap.dev.of_node = of_node_get(op->dev.of_node);
+       i2c->adap.bus_recovery_info = &fsl_i2c_recovery_info;
+       platform_set_drvdata(op, i2c);
+       i2c_set_adapdata(&i2c->adap, i2c);
 
-       result = i2c_add_adapter(&i2c->adap);
-       if (result < 0)
+       result = i2c_add_numbered_adapter(&i2c->adap);
+       if (result)
                goto fail_add;
 
-       return result;
+       return 0;
 
  fail_add:
-       if (i2c->clk_per)
-               clk_disable_unprepare(i2c->clk_per);
-       free_irq(i2c->irq, i2c);
- fail_request:
-       irq_dispose_mapping(i2c->irq);
-       iounmap(i2c->base);
- fail_map:
-       kfree(i2c);
+       clk_disable_unprepare(i2c->clk_per);
+
        return result;
 };
 
@@ -765,20 +795,12 @@ static int fsl_i2c_remove(struct platform_device *op)
 
        i2c_del_adapter(&i2c->adap);
 
-       if (i2c->clk_per)
-               clk_disable_unprepare(i2c->clk_per);
+       clk_disable_unprepare(i2c->clk_per);
 
-       if (i2c->irq)
-               free_irq(i2c->irq, i2c);
-
-       irq_dispose_mapping(i2c->irq);
-       iounmap(i2c->base);
-       kfree(i2c);
        return 0;
 };
 
-#ifdef CONFIG_PM_SLEEP
-static int mpc_i2c_suspend(struct device *dev)
+static int __maybe_unused mpc_i2c_suspend(struct device *dev)
 {
        struct mpc_i2c *i2c = dev_get_drvdata(dev);
 
@@ -788,7 +810,7 @@ static int mpc_i2c_suspend(struct device *dev)
        return 0;
 }
 
-static int mpc_i2c_resume(struct device *dev)
+static int __maybe_unused mpc_i2c_resume(struct device *dev)
 {
        struct mpc_i2c *i2c = dev_get_drvdata(dev);
 
@@ -797,12 +819,7 @@ static int mpc_i2c_resume(struct device *dev)
 
        return 0;
 }
-
 static SIMPLE_DEV_PM_OPS(mpc_i2c_pm_ops, mpc_i2c_suspend, mpc_i2c_resume);
-#define MPC_I2C_PM_OPS (&mpc_i2c_pm_ops)
-#else
-#define MPC_I2C_PM_OPS NULL
-#endif
 
 static const struct mpc_i2c_data mpc_i2c_data_512x = {
        .setup = mpc_i2c_setup_512x,
@@ -845,7 +862,7 @@ static struct platform_driver mpc_i2c_driver = {
        .driver = {
                .name = DRV_NAME,
                .of_match_table = mpc_i2c_of_match,
-               .pm = MPC_I2C_PM_OPS,
+               .pm = &mpc_i2c_pm_ops,
        },
 };
 
index 2ffd2f3..5ddfa4e 100644 (file)
@@ -231,6 +231,7 @@ struct mtk_i2c {
        struct i2c_adapter adap;        /* i2c host adapter */
        struct device *dev;
        struct completion msg_complete;
+       struct i2c_timings timing_info;
 
        /* set in i2c probe */
        void __iomem *base;             /* i2c base addr */
@@ -479,7 +480,7 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
 {
        u16 control_reg;
 
-       if (i2c->dev_comp->dma_sync) {
+       if (i2c->dev_comp->apdma_sync) {
                writel(I2C_DMA_WARM_RST, i2c->pdmabase + OFFSET_RST);
                udelay(10);
                writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
@@ -564,7 +565,7 @@ static const struct i2c_spec_values *mtk_i2c_get_spec(unsigned int speed)
 
 static int mtk_i2c_max_step_cnt(unsigned int target_speed)
 {
-       if (target_speed > I2C_MAX_FAST_MODE_FREQ)
+       if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ)
                return MAX_HS_STEP_CNT_DIV;
        else
                return MAX_STEP_CNT_DIV;
@@ -607,7 +608,8 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c,
        else
                clk_ns = sample_ns / 2;
 
-       su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns, clk_ns);
+       su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns +
+                                 i2c->timing_info.scl_int_delay_ns, clk_ns);
        if (su_sta_cnt > max_sta_cnt)
                return -1;
 
@@ -635,7 +637,7 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c,
        if (sda_min > sda_max)
                return -3;
 
-       if (check_speed > I2C_MAX_FAST_MODE_FREQ) {
+       if (check_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) {
                if (i2c->dev_comp->ltiming_adjust) {
                        i2c->ac_timing.hs = I2C_TIME_DEFAULT_VALUE |
                                (sample_cnt << 12) | (high_cnt << 8);
@@ -850,7 +852,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
 
        control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) &
                        ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
-       if ((i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ) || (left_num >= 1))
+       if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1))
                control_reg |= I2C_CONTROL_RS;
 
        if (i2c->op == I2C_MASTER_WRRD)
@@ -1067,7 +1069,8 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
                }
        }
 
-       if (i2c->auto_restart && num >= 2 && i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ)
+       if (i2c->auto_restart && num >= 2 &&
+               i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ)
                /* ignore the first restart irq after the master code,
                 * otherwise the first transfer will be discarded.
                 */
@@ -1175,6 +1178,8 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c)
        i2c->use_push_pull =
                of_property_read_bool(np, "mediatek,use-push-pull");
 
+       i2c_parse_fw_timings(i2c->dev, &i2c->timing_info, true);
+
        return 0;
 }
 
index a3363b2..dc77e1c 100644 (file)
@@ -277,7 +277,7 @@ static int init_hw(struct nmk_i2c_dev *dev)
                goto exit;
 
        /* disable the controller */
-       i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
+       i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
 
        disable_all_interrupts(dev);
 
@@ -525,7 +525,7 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
                        dev->virtbase + I2C_CR);
 
        /* enable the controller */
-       i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
+       i2c_set_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
 
        init_completion(&dev->xfer_complete);
 
index 6b20601..b5055a3 100644 (file)
@@ -262,6 +262,10 @@ static const struct property_entry ccgx_props[] = {
        { }
 };
 
+static const struct software_node ccgx_node = {
+       .properties = ccgx_props,
+};
+
 static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
 {
        i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
@@ -274,7 +278,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
                sizeof(i2cd->gpu_ccgx_ucsi->type));
        i2cd->gpu_ccgx_ucsi->addr = 0x8;
        i2cd->gpu_ccgx_ucsi->irq = irq;
-       i2cd->gpu_ccgx_ucsi->properties = ccgx_props;
+       i2cd->gpu_ccgx_ucsi->swnode = &ccgx_node;
        i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
        return PTR_ERR_OR_ZERO(i2cd->ccgx_client);
 }
index 12ac421..d4f6c6d 100644 (file)
@@ -1404,9 +1404,9 @@ omap_i2c_probe(struct platform_device *pdev)
        pm_runtime_set_autosuspend_delay(omap->dev, OMAP_I2C_PM_TIMEOUT);
        pm_runtime_use_autosuspend(omap->dev);
 
-       r = pm_runtime_get_sync(omap->dev);
+       r = pm_runtime_resume_and_get(omap->dev);
        if (r < 0)
-               goto err_free_mem;
+               goto err_disable_pm;
 
        /*
         * Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2.
@@ -1513,8 +1513,8 @@ err_unuse_clocks:
        omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
        pm_runtime_dont_use_autosuspend(omap->dev);
        pm_runtime_put_sync(omap->dev);
+err_disable_pm:
        pm_runtime_disable(&pdev->dev);
-err_free_mem:
 
        return r;
 }
@@ -1525,7 +1525,7 @@ static int omap_i2c_remove(struct platform_device *pdev)
        int ret;
 
        i2c_del_adapter(&omap->adapter);
-       ret = pm_runtime_get_sync(&pdev->dev);
+       ret = pm_runtime_resume_and_get(&pdev->dev);
        if (ret < 0)
                return ret;
 
index 3e38e11..5241e6f 100644 (file)
@@ -76,11 +76,6 @@ static s32 i2c_powermac_smbus_xfer(  struct i2c_adapter*     adap,
         * but I think the current API makes no sense and I don't want
         * any driver that I haven't verified for correctness to go
         * anywhere near a pmac i2c bus anyway ...
-        *
-        * I'm also not completely sure what kind of phases to do between
-        * the actual command and the data (what I am _supposed_ to do that
-        * is). For now, I assume writes are a single stream and reads have
-        * a repeat start/addr phase (but not stop in between)
         */
         case I2C_SMBUS_BLOCK_DATA:
                buf = data->block;
index 1c259b5..c63d554 100644 (file)
@@ -569,9 +569,9 @@ static int cci_probe(struct platform_device *pdev)
                cci->master[idx].mode = I2C_MODE_STANDARD;
                ret = of_property_read_u32(child, "clock-frequency", &val);
                if (!ret) {
-                       if (val == 400000)
+                       if (val == I2C_MAX_FAST_MODE_FREQ)
                                cci->master[idx].mode = I2C_MODE_FAST;
-                       else if (val == 1000000)
+                       else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ)
                                cci->master[idx].mode = I2C_MODE_FAST_PLUS;
                }
 
index 12f6d45..327c092 100644 (file)
@@ -141,6 +141,7 @@ struct rcar_i2c_priv {
        enum dma_data_direction dma_direction;
 
        struct reset_control *rstc;
+       bool atomic_xfer;
        int irq;
 
        struct i2c_client *host_notify_client;
@@ -353,7 +354,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
                        rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
                rcar_i2c_write(priv, ICMSR, 0);
        }
-       rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
+
+       if (!priv->atomic_xfer)
+               rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
 }
 
 static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
@@ -418,7 +421,7 @@ static bool 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 < RCAR_MIN_DMA_LEN ||
+       if (priv->atomic_xfer || IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
            !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
                return false;
 
@@ -646,7 +649,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
        /* Nack */
        if (msr & MNR) {
                /* HW automatically sends STOP after received NACK */
-               rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
+               if (!priv->atomic_xfer)
+                       rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
                priv->flags |= ID_NACK;
                goto out;
        }
@@ -667,7 +671,8 @@ out:
        if (priv->flags & ID_DONE) {
                rcar_i2c_write(priv, ICMIER, 0);
                rcar_i2c_write(priv, ICMSR, 0);
-               wake_up(&priv->wait);
+               if (!priv->atomic_xfer)
+                       wake_up(&priv->wait);
        }
 
        return IRQ_HANDLED;
@@ -684,7 +689,8 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)
 
        /* Only handle interrupts that are currently enabled */
        msr = rcar_i2c_read(priv, ICMSR);
-       msr &= rcar_i2c_read(priv, ICMIER);
+       if (!priv->atomic_xfer)
+               msr &= rcar_i2c_read(priv, ICMIER);
 
        return rcar_i2c_irq(irq, priv, msr);
 }
@@ -696,7 +702,8 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)
 
        /* Only handle interrupts that are currently enabled */
        msr = rcar_i2c_read(priv, ICMSR);
-       msr &= rcar_i2c_read(priv, ICMIER);
+       if (!priv->atomic_xfer)
+               msr &= rcar_i2c_read(priv, ICMIER);
 
        /*
         * Clear START or STOP immediately, except for REPSTART after read or
@@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
        int i, ret;
        long time_left;
 
+       priv->atomic_xfer = false;
+
        pm_runtime_get_sync(dev);
 
        /* Check bus state before init otherwise bus busy info will be lost */
@@ -858,6 +867,68 @@ out:
        return ret;
 }
 
+static int rcar_i2c_master_xfer_atomic(struct i2c_adapter *adap,
+                               struct i2c_msg *msgs,
+                               int num)
+{
+       struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
+       struct device *dev = rcar_i2c_priv_to_dev(priv);
+       unsigned long j;
+       bool time_left;
+       int ret;
+
+       priv->atomic_xfer = true;
+
+       pm_runtime_get_sync(dev);
+
+       /* Check bus state before init otherwise bus busy info will be lost */
+       ret = rcar_i2c_bus_barrier(priv);
+       if (ret < 0)
+               goto out;
+
+       rcar_i2c_init(priv);
+
+       /* init first message */
+       priv->msg = msgs;
+       priv->msgs_left = num;
+       priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG;
+       rcar_i2c_prepare_msg(priv);
+
+       j = jiffies + num * adap->timeout;
+       do {
+               u32 msr = rcar_i2c_read(priv, ICMSR);
+
+               msr &= (rcar_i2c_is_recv(priv) ? RCAR_IRQ_RECV : RCAR_IRQ_SEND) | RCAR_IRQ_STOP;
+
+               if (msr) {
+                       if (priv->devtype < I2C_RCAR_GEN3)
+                               rcar_i2c_gen2_irq(0, priv);
+                       else
+                               rcar_i2c_gen3_irq(0, priv);
+               }
+
+               time_left = time_before_eq(jiffies, j);
+       } while (!(priv->flags & ID_DONE) && time_left);
+
+       if (!time_left) {
+               rcar_i2c_init(priv);
+               ret = -ETIMEDOUT;
+       } else if (priv->flags & ID_NACK) {
+               ret = -ENXIO;
+       } else if (priv->flags & ID_ARBLOST) {
+               ret = -EAGAIN;
+       } else {
+               ret = num - priv->msgs_left; /* The number of transfer */
+       }
+out:
+       pm_runtime_put(dev);
+
+       if (ret < 0 && ret != -ENXIO)
+               dev_err(dev, "error %d : %x\n", ret, priv->flags);
+
+       return ret;
+}
+
 static int rcar_reg_slave(struct i2c_client *slave)
 {
        struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
@@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap)
 
 static const struct i2c_algorithm rcar_i2c_algo = {
        .master_xfer    = rcar_i2c_master_xfer,
+       .master_xfer_atomic = rcar_i2c_master_xfer_atomic,
        .functionality  = rcar_i2c_func,
        .reg_slave      = rcar_reg_slave,
        .unreg_slave    = rcar_unreg_slave,
@@ -1027,7 +1099,10 @@ static int rcar_i2c_probe(struct platform_device *pdev)
        if (of_property_read_bool(dev->of_node, "smbus"))
                priv->flags |= ID_P_HOST_NOTIFY;
 
-       priv->irq = platform_get_irq(pdev, 0);
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0)
+               goto out_pm_disable;
+       priv->irq = ret;
        ret = devm_request_irq(dev, priv->irq, irqhandler, irqflags, dev_name(dev), priv);
        if (ret < 0) {
                dev_err(dev, "cannot get irq %d\n", priv->irq);
index 62a903f..ab92861 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/gpio/consumer.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/mfd/syscon.h>
@@ -156,12 +157,8 @@ MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
  */
 static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev)
 {
-       if (pdev->dev.of_node) {
-               const struct of_device_id *match;
-
-               match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
-               return (kernel_ulong_t)match->data;
-       }
+       if (pdev->dev.of_node)
+               return (kernel_ulong_t)of_device_get_match_data(&pdev->dev);
 
        return platform_get_device_id(pdev)->driver_data;
 }
index 1dc3873..6746aa4 100644 (file)
@@ -18,8 +18,6 @@
 /* SMBUS HID definition as supported by Microsoft Windows */
 #define ACPI_SMBUS_MS_HID              "SMB0001"
 
-ACPI_MODULE_NAME("smbus_cmi");
-
 struct smbus_methods_t {
        char *mt_info;
        char *mt_sbr;
index c2005c7..319d1fa 100644 (file)
@@ -471,7 +471,10 @@ static int sh7760_i2c_probe(struct platform_device *pdev)
                goto out2;
        }
 
-       id->irq = platform_get_irq(pdev, 0);
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0)
+               goto out3;
+       id->irq = ret;
 
        id->adap.nr = pdev->id;
        id->adap.algo = &sh7760_i2c_algo;
index 2917fec..4fe15cd 100644 (file)
@@ -290,7 +290,7 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
        struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
        int im, ret;
 
-       ret = pm_runtime_get_sync(i2c_dev->dev);
+       ret = pm_runtime_resume_and_get(i2c_dev->dev);
        if (ret < 0)
                return ret;
 
@@ -576,7 +576,7 @@ static int sprd_i2c_remove(struct platform_device *pdev)
        struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev);
        int ret;
 
-       ret = pm_runtime_get_sync(i2c_dev->dev);
+       ret = pm_runtime_resume_and_get(i2c_dev->dev);
        if (ret < 0)
                return ret;
 
@@ -640,6 +640,7 @@ static const struct of_device_id sprd_i2c_of_match[] = {
        { .compatible = "sprd,sc9860-i2c", },
        {},
 };
+MODULE_DEVICE_TABLE(of, sprd_i2c_of_match);
 
 static struct platform_driver sprd_i2c_driver = {
        .probe = sprd_i2c_probe,
index c62c815..0138317 100644 (file)
@@ -164,7 +164,6 @@ enum {
 #define STM32F7_I2C_DNF_DEFAULT                        0
 #define STM32F7_I2C_DNF_MAX                    15
 
-#define STM32F7_I2C_ANALOG_FILTER_ENABLE       1
 #define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN    50      /* ns */
 #define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX    260     /* ns */
 
@@ -223,8 +222,6 @@ struct stm32f7_i2c_spec {
  * @clock_src: I2C clock source frequency (Hz)
  * @rise_time: Rise time (ns)
  * @fall_time: Fall time (ns)
- * @dnf: Digital filter coefficient (0-16)
- * @analog_filter: Analog filter delay (On/Off)
  * @fmp_clr_offset: Fast Mode Plus clear register offset from set register
  */
 struct stm32f7_i2c_setup {
@@ -232,8 +229,6 @@ struct stm32f7_i2c_setup {
        u32 clock_src;
        u32 rise_time;
        u32 fall_time;
-       u8 dnf;
-       bool analog_filter;
        u32 fmp_clr_offset;
 };
 
@@ -312,6 +307,9 @@ struct stm32f7_i2c_msg {
  * @wakeup_src: boolean to know if the device is a wakeup source
  * @smbus_mode: states that the controller is configured in SMBus mode
  * @host_notify_client: SMBus host-notify client
+ * @analog_filter: boolean to indicate enabling of the analog filter
+ * @dnf_dt: value of digital filter requested via dt
+ * @dnf: value of digital filter to apply
  */
 struct stm32f7_i2c_dev {
        struct i2c_adapter adap;
@@ -340,6 +338,9 @@ struct stm32f7_i2c_dev {
        bool wakeup_src;
        bool smbus_mode;
        struct i2c_client *host_notify_client;
+       bool analog_filter;
+       u32 dnf_dt;
+       u32 dnf;
 };
 
 /*
@@ -385,15 +386,11 @@ static struct stm32f7_i2c_spec stm32f7_i2c_specs[] = {
 static const struct stm32f7_i2c_setup stm32f7_setup = {
        .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
        .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
-       .dnf = STM32F7_I2C_DNF_DEFAULT,
-       .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
 };
 
 static const struct stm32f7_i2c_setup stm32mp15_setup = {
        .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
        .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
-       .dnf = STM32F7_I2C_DNF_DEFAULT,
-       .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
        .fmp_clr_offset = 0x40,
 };
 
@@ -462,27 +459,28 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
                return -EINVAL;
        }
 
-       if (setup->dnf > STM32F7_I2C_DNF_MAX) {
+       i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk);
+       if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) {
                dev_err(i2c_dev->dev,
                        "DNF out of bound %d/%d\n",
-                       setup->dnf, STM32F7_I2C_DNF_MAX);
+                       i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk);
                return -EINVAL;
        }
 
        /*  Analog and Digital Filters */
        af_delay_min =
-               (setup->analog_filter ?
+               (i2c_dev->analog_filter ?
                 STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0);
        af_delay_max =
-               (setup->analog_filter ?
+               (i2c_dev->analog_filter ?
                 STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
-       dnf_delay = setup->dnf * i2cclk;
+       dnf_delay = i2c_dev->dnf * i2cclk;
 
        sdadel_min = specs->hddat_min + setup->fall_time -
-               af_delay_min - (setup->dnf + 3) * i2cclk;
+               af_delay_min - (i2c_dev->dnf + 3) * i2cclk;
 
        sdadel_max = specs->vddat_max - setup->rise_time -
-               af_delay_max - (setup->dnf + 4) * i2cclk;
+               af_delay_max - (i2c_dev->dnf + 4) * i2cclk;
 
        scldel_min = setup->rise_time + specs->sudat_min;
 
@@ -648,6 +646,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
        setup->speed_freq = t->bus_freq_hz;
        i2c_dev->setup.rise_time = t->scl_rise_ns;
        i2c_dev->setup.fall_time = t->scl_fall_ns;
+       i2c_dev->dnf_dt = t->digital_filter_width_ns;
        setup->clock_src = clk_get_rate(i2c_dev->clk);
 
        if (!setup->clock_src) {
@@ -655,6 +654,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
                return -EINVAL;
        }
 
+       if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter"))
+               i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT;
+
        do {
                ret = stm32f7_i2c_compute_timing(i2c_dev, setup,
                                                 &i2c_dev->timing);
@@ -676,12 +678,15 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
                return ret;
        }
 
+       i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node,
+                                                      "i2c-analog-filter");
+
        dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n",
                setup->speed_freq, setup->clock_src);
        dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n",
                setup->rise_time, setup->fall_time);
        dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n",
-               (setup->analog_filter ? "On" : "Off"), setup->dnf);
+               (i2c_dev->analog_filter ? "On" : "Off"), i2c_dev->dnf);
 
        i2c_dev->bus_rate = setup->speed_freq;
 
@@ -720,8 +725,8 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
        timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll);
        writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR);
 
-       /* Enable I2C */
-       if (i2c_dev->setup.analog_filter)
+       /* Configure the Analog Filter */
+       if (i2c_dev->analog_filter)
                stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
                                     STM32F7_I2C_CR1_ANFOFF);
        else
@@ -732,7 +737,7 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
        stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
                             STM32F7_I2C_CR1_DNF_MASK);
        stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
-                            STM32F7_I2C_CR1_DNF(i2c_dev->setup.dnf));
+                            STM32F7_I2C_CR1_DNF(i2c_dev->dnf));
 
        stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
                             STM32F7_I2C_CR1_PE);
@@ -1597,7 +1602,8 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
 
        /* Bus error */
        if (status & STM32F7_I2C_ISR_BERR) {
-               dev_err(dev, "<%s>: Bus error\n", __func__);
+               dev_err(dev, "<%s>: Bus error accessing addr 0x%x\n",
+                       __func__, f7_msg->addr);
                writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
                stm32f7_i2c_release_bus(&i2c_dev->adap);
                f7_msg->result = -EIO;
@@ -1605,13 +1611,15 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
 
        /* Arbitration loss */
        if (status & STM32F7_I2C_ISR_ARLO) {
-               dev_dbg(dev, "<%s>: Arbitration loss\n", __func__);
+               dev_dbg(dev, "<%s>: Arbitration loss accessing addr 0x%x\n",
+                       __func__, f7_msg->addr);
                writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
                f7_msg->result = -EAGAIN;
        }
 
        if (status & STM32F7_I2C_ISR_PECERR) {
-               dev_err(dev, "<%s>: PEC error in reception\n", __func__);
+               dev_err(dev, "<%s>: PEC error in reception accessing addr 0x%x\n",
+                       __func__, f7_msg->addr);
                writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
                f7_msg->result = -EINVAL;
        }
@@ -1652,7 +1660,7 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
        i2c_dev->msg_id = 0;
        f7_msg->smbus = false;
 
-       ret = pm_runtime_get_sync(i2c_dev->dev);
+       ret = pm_runtime_resume_and_get(i2c_dev->dev);
        if (ret < 0)
                return ret;
 
@@ -1698,7 +1706,7 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
        f7_msg->read_write = read_write;
        f7_msg->smbus = true;
 
-       ret = pm_runtime_get_sync(dev);
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0)
                return ret;
 
@@ -1799,7 +1807,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
        if (ret)
                return ret;
 
-       ret = pm_runtime_get_sync(dev);
+       ret = pm_runtime_resume_and_get(dev);
        if (ret < 0)
                return ret;
 
@@ -1880,7 +1888,7 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
 
        WARN_ON(!i2c_dev->slave[id]);
 
-       ret = pm_runtime_get_sync(i2c_dev->dev);
+       ret = pm_runtime_resume_and_get(i2c_dev->dev);
        if (ret < 0)
                return ret;
 
@@ -2027,12 +2035,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
        phy_addr = (dma_addr_t)res->start;
 
        irq_event = platform_get_irq(pdev, 0);
-       if (irq_event <= 0) {
-               if (irq_event != -EPROBE_DEFER)
-                       dev_err(&pdev->dev, "Failed to get IRQ event: %d\n",
-                               irq_event);
+       if (irq_event <= 0)
                return irq_event ? : -ENOENT;
-       }
 
        irq_error = platform_get_irq(pdev, 1);
        if (irq_error <= 0)
@@ -2267,13 +2271,12 @@ static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
+static int __maybe_unused stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
 {
        int ret;
        struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
 
-       ret = pm_runtime_get_sync(i2c_dev->dev);
+       ret = pm_runtime_resume_and_get(i2c_dev->dev);
        if (ret < 0)
                return ret;
 
@@ -2289,13 +2292,13 @@ static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
        return ret;
 }
 
-static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
+static int __maybe_unused stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
 {
        u32 cr1;
        int ret;
        struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
 
-       ret = pm_runtime_get_sync(i2c_dev->dev);
+       ret = pm_runtime_resume_and_get(i2c_dev->dev);
        if (ret < 0)
                return ret;
 
@@ -2320,7 +2323,7 @@ static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
        return ret;
 }
 
-static int stm32f7_i2c_suspend(struct device *dev)
+static int __maybe_unused stm32f7_i2c_suspend(struct device *dev)
 {
        struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
        int ret;
@@ -2341,7 +2344,7 @@ static int stm32f7_i2c_suspend(struct device *dev)
        return 0;
 }
 
-static int stm32f7_i2c_resume(struct device *dev)
+static int __maybe_unused stm32f7_i2c_resume(struct device *dev)
 {
        struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
        int ret;
@@ -2361,7 +2364,6 @@ static int stm32f7_i2c_resume(struct device *dev)
 
        return 0;
 }
-#endif
 
 static const struct dev_pm_ops stm32f7_i2c_pm_ops = {
        SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend,
index c0c7d01..3680d60 100644 (file)
@@ -38,49 +38,31 @@ struct tegra_bpmp_i2c {
  * firmware I2C driver to avoid any issues in future if Linux I2C flags are
  * changed.
  */
-static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
+static void tegra_bpmp_xlate_flags(u16 flags, u16 *out)
 {
-       if (flags & I2C_M_TEN) {
+       if (flags & I2C_M_TEN)
                *out |= SERIALI2C_TEN;
-               flags &= ~I2C_M_TEN;
-       }
 
-       if (flags & I2C_M_RD) {
+       if (flags & I2C_M_RD)
                *out |= SERIALI2C_RD;
-               flags &= ~I2C_M_RD;
-       }
 
-       if (flags & I2C_M_STOP) {
+       if (flags & I2C_M_STOP)
                *out |= SERIALI2C_STOP;
-               flags &= ~I2C_M_STOP;
-       }
 
-       if (flags & I2C_M_NOSTART) {
+       if (flags & I2C_M_NOSTART)
                *out |= SERIALI2C_NOSTART;
-               flags &= ~I2C_M_NOSTART;
-       }
 
-       if (flags & I2C_M_REV_DIR_ADDR) {
+       if (flags & I2C_M_REV_DIR_ADDR)
                *out |= SERIALI2C_REV_DIR_ADDR;
-               flags &= ~I2C_M_REV_DIR_ADDR;
-       }
 
-       if (flags & I2C_M_IGNORE_NAK) {
+       if (flags & I2C_M_IGNORE_NAK)
                *out |= SERIALI2C_IGNORE_NAK;
-               flags &= ~I2C_M_IGNORE_NAK;
-       }
 
-       if (flags & I2C_M_NO_RD_ACK) {
+       if (flags & I2C_M_NO_RD_ACK)
                *out |= SERIALI2C_NO_RD_ACK;
-               flags &= ~I2C_M_NO_RD_ACK;
-       }
 
-       if (flags & I2C_M_RECV_LEN) {
+       if (flags & I2C_M_RECV_LEN)
                *out |= SERIALI2C_RECV_LEN;
-               flags &= ~I2C_M_RECV_LEN;
-       }
-
-       return 0;
 }
 
 /**
@@ -97,22 +79,19 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
  *
  * See deserialize_i2c documentation for the data format in the other direction.
  */
-static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
+static void tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
                                        struct mrq_i2c_request *request,
                                        struct i2c_msg *msgs,
                                        unsigned int num)
 {
        char *buf = request->xfer.data_buf;
        unsigned int i, j, pos = 0;
-       int err;
 
        for (i = 0; i < num; i++) {
                struct i2c_msg *msg = &msgs[i];
                u16 flags = 0;
 
-               err = tegra_bpmp_xlate_flags(msg->flags, &flags);
-               if (err < 0)
-                       return err;
+               tegra_bpmp_xlate_flags(msg->flags, &flags);
 
                buf[pos++] = msg->addr & 0xff;
                buf[pos++] = (msg->addr & 0xff00) >> 8;
@@ -128,8 +107,6 @@ static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
        }
 
        request->xfer.data_size = pos;
-
-       return 0;
 }
 
 /**
@@ -217,7 +194,32 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
        else
                err = tegra_bpmp_transfer(i2c->bpmp, &msg);
 
-       return err;
+       if (err < 0) {
+               dev_err(i2c->dev, "failed to transfer message: %d\n", err);
+               return err;
+       }
+
+       if (msg.rx.ret != 0) {
+               if (msg.rx.ret == -BPMP_EAGAIN) {
+                       dev_dbg(i2c->dev, "arbitration lost\n");
+                       return -EAGAIN;
+               }
+
+               if (msg.rx.ret == -BPMP_ETIMEDOUT) {
+                       dev_dbg(i2c->dev, "timeout\n");
+                       return -ETIMEDOUT;
+               }
+
+               if (msg.rx.ret == -BPMP_ENXIO) {
+                       dev_dbg(i2c->dev, "NAK\n");
+                       return -ENXIO;
+               }
+
+               dev_err(i2c->dev, "transaction failed: %d\n", msg.rx.ret);
+               return -EIO;
+       }
+
+       return 0;
 }
 
 static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
@@ -238,12 +240,7 @@ static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
        memset(&request, 0, sizeof(request));
        memset(&response, 0, sizeof(response));
 
-       err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
-       if (err < 0) {
-               dev_err(i2c->dev, "failed to serialize message: %d\n", err);
-               return err;
-       }
-
+       tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
        err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic);
        if (err < 0) {
                dev_err(i2c->dev, "failed to transfer message: %d\n", err);
index 63cbb9c..bba08cb 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
-#include <linux/version.h>
 
 #define MAILBOX_OP_TIMEOUT             1000    /* Operation time out in ms */
 #define MAILBOX_I2C_INDEX              0
index 087b295..2a8568b 100644 (file)
@@ -706,7 +706,7 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
        dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__,
                xiic_getreg8(i2c, XIIC_SR_REG_OFFSET));
 
-       err = pm_runtime_get_sync(i2c->dev);
+       err = pm_runtime_resume_and_get(i2c->dev);
        if (err < 0)
                return err;
 
@@ -873,7 +873,7 @@ static int xiic_i2c_remove(struct platform_device *pdev)
        /* remove adapter & data */
        i2c_del_adapter(&i2c->adap);
 
-       ret = pm_runtime_get_sync(i2c->dev);
+       ret = pm_runtime_resume_and_get(i2c->dev);
        if (ret < 0)
                return ret;
 
index 8bc51d4..4df8ad0 100644 (file)
@@ -47,7 +47,6 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
  *
  * The board info passed can safely be __initdata, but be careful of embedded
  * pointers (for platform_data, functions, etc) since that won't be copied.
- * Device properties are deep-copied though.
  */
 int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
 {
@@ -72,16 +71,6 @@ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsig
                devinfo->busnum = busnum;
                devinfo->board_info = *info;
 
-               if (info->properties) {
-                       devinfo->board_info.properties =
-                                       property_entries_dup(info->properties);
-                       if (IS_ERR(devinfo->board_info.properties)) {
-                               status = PTR_ERR(devinfo->board_info.properties);
-                               kfree(devinfo);
-                               break;
-                       }
-               }
-
                if (info->resources) {
                        devinfo->board_info.resources =
                                kmemdup(info->resources,
index f213623..5a97e4a 100644 (file)
@@ -76,6 +76,27 @@ void i2c_transfer_trace_unreg(void)
        static_branch_dec(&i2c_trace_msg_key);
 }
 
+const char *i2c_freq_mode_string(u32 bus_freq_hz)
+{
+       switch (bus_freq_hz) {
+       case I2C_MAX_STANDARD_MODE_FREQ:
+               return "Standard Mode (100 kHz)";
+       case I2C_MAX_FAST_MODE_FREQ:
+               return "Fast Mode (400 kHz)";
+       case I2C_MAX_FAST_MODE_PLUS_FREQ:
+               return "Fast Mode Plus (1.0 MHz)";
+       case I2C_MAX_TURBO_MODE_FREQ:
+               return "Turbo Mode (1.4 MHz)";
+       case I2C_MAX_HIGH_SPEED_MODE_FREQ:
+               return "High Speed Mode (3.4 MHz)";
+       case I2C_MAX_ULTRA_FAST_MODE_FREQ:
+               return "Ultra Fast Mode (5.0 MHz)";
+       default:
+               return "Unknown Mode";
+       }
+}
+EXPORT_SYMBOL_GPL(i2c_freq_mode_string);
+
 const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                                                const struct i2c_client *client)
 {
@@ -249,7 +270,7 @@ EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery);
 int i2c_recover_bus(struct i2c_adapter *adap)
 {
        if (!adap->bus_recovery_info)
-               return -EOPNOTSUPP;
+               return -EBUSY;
 
        dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
        return adap->bus_recovery_info->recover_bus(adap);
@@ -519,6 +540,13 @@ static int i2c_device_probe(struct device *dev)
        if (status)
                goto err_clear_wakeup_irq;
 
+       client->devres_group_id = devres_open_group(&client->dev, NULL,
+                                                   GFP_KERNEL);
+       if (!client->devres_group_id) {
+               status = -ENOMEM;
+               goto err_detach_pm_domain;
+       }
+
        /*
         * When there are no more users of probe(),
         * rename probe_new to probe.
@@ -531,11 +559,21 @@ static int i2c_device_probe(struct device *dev)
        else
                status = -EINVAL;
 
+       /*
+        * Note that we are not closing the devres group opened above so
+        * even resources that were attached to the device after probe is
+        * run are released when i2c_device_remove() is executed. This is
+        * needed as some drivers would allocate additional resources,
+        * for example when updating firmware.
+        */
+
        if (status)
-               goto err_detach_pm_domain;
+               goto err_release_driver_resources;
 
        return 0;
 
+err_release_driver_resources:
+       devres_release_group(&client->dev, client->devres_group_id);
 err_detach_pm_domain:
        dev_pm_domain_detach(&client->dev, true);
 err_clear_wakeup_irq:
@@ -564,6 +602,8 @@ static int i2c_device_remove(struct device *dev)
                        dev_warn(dev, "remove failed (%pe), will be ignored\n", ERR_PTR(status));
        }
 
+       devres_release_group(&client->dev, client->devres_group_id);
+
        dev_pm_domain_detach(&client->dev, true);
 
        dev_pm_clear_wake_irq(&client->dev);
@@ -612,7 +652,7 @@ modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
        if (len != -ENODEV)
                return len;
 
-       len = acpi_device_modalias(dev, buf, PAGE_SIZE -1);
+       len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1);
        if (len != -ENODEV)
                return len;
 
@@ -910,11 +950,11 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
 
        i2c_dev_set_name(adap, client, info);
 
-       if (info->properties) {
-               status = device_add_properties(&client->dev, info->properties);
+       if (info->swnode) {
+               status = device_add_software_node(&client->dev, info->swnode);
                if (status) {
                        dev_err(&adap->dev,
-                               "Failed to add properties to client %s: %d\n",
+                               "Failed to add software node to client %s: %d\n",
                                client->name, status);
                        goto out_err_put_of_node;
                }
@@ -922,16 +962,15 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
 
        status = device_register(&client->dev);
        if (status)
-               goto out_free_props;
+               goto out_remove_swnode;
 
        dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
                client->name, dev_name(&client->dev));
 
        return client;
 
-out_free_props:
-       if (info->properties)
-               device_remove_properties(&client->dev);
+out_remove_swnode:
+       device_remove_software_node(&client->dev);
 out_err_put_of_node:
        of_node_put(info->of_node);
 out_err:
@@ -961,6 +1000,7 @@ void i2c_unregister_device(struct i2c_client *client)
 
        if (ACPI_COMPANION(&client->dev))
                acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
+       device_remove_software_node(&client->dev);
        device_unregister(&client->dev);
 }
 EXPORT_SYMBOL_GPL(i2c_unregister_device);
@@ -1017,15 +1057,9 @@ struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address
 }
 EXPORT_SYMBOL_GPL(i2c_new_dummy_device);
 
-struct i2c_dummy_devres {
-       struct i2c_client *client;
-};
-
-static void devm_i2c_release_dummy(struct device *dev, void *res)
+static void devm_i2c_release_dummy(void *client)
 {
-       struct i2c_dummy_devres *this = res;
-
-       i2c_unregister_device(this->client);
+       i2c_unregister_device(client);
 }
 
 /**
@@ -1042,20 +1076,16 @@ struct i2c_client *devm_i2c_new_dummy_device(struct device *dev,
                                             struct i2c_adapter *adapter,
                                             u16 address)
 {
-       struct i2c_dummy_devres *dr;
        struct i2c_client *client;
-
-       dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL);
-       if (!dr)
-               return ERR_PTR(-ENOMEM);
+       int ret;
 
        client = i2c_new_dummy_device(adapter, address);
-       if (IS_ERR(client)) {
-               devres_free(dr);
-       } else {
-               dr->client = client;
-               devres_add(dev, dr);
-       }
+       if (IS_ERR(client))
+               return client;
+
+       ret = devm_add_action_or_reset(dev, devm_i2c_release_dummy, client);
+       if (ret)
+               return ERR_PTR(ret);
 
        return client;
 }
@@ -1704,6 +1734,32 @@ void i2c_del_adapter(struct i2c_adapter *adap)
 }
 EXPORT_SYMBOL(i2c_del_adapter);
 
+static void devm_i2c_del_adapter(void *adapter)
+{
+       i2c_del_adapter(adapter);
+}
+
+/**
+ * devm_i2c_add_adapter - device-managed variant of i2c_add_adapter()
+ * @dev: managing device for adding this I2C adapter
+ * @adapter: the adapter to add
+ * Context: can sleep
+ *
+ * Add adapter with dynamic bus number, same with i2c_add_adapter()
+ * but the adapter will be auto deleted on driver detach.
+ */
+int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter)
+{
+       int ret;
+
+       ret = i2c_add_adapter(adapter);
+       if (ret)
+               return ret;
+
+       return devm_add_action_or_reset(dev, devm_i2c_del_adapter, adapter);
+}
+EXPORT_SYMBOL_GPL(devm_i2c_add_adapter);
+
 static void i2c_parse_timing(struct device *dev, char *prop_name, u32 *cur_val_p,
                            u32 def_val, bool use_def)
 {
index 6ceb11c..6ef38a8 100644 (file)
@@ -440,8 +440,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                   sizeof(rdwr_arg)))
                        return -EFAULT;
 
-               /* Put an arbitrary limit on the number of messages that can
-                * be sent at once */
+               if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
+                       return -EINVAL;
+
+               /*
+                * Put an arbitrary limit on the number of messages that can
+                * be sent at once
+                */
                if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
                        return -EINVAL;
 
index 97381e2..2d0bc02 100644 (file)
@@ -1885,8 +1885,6 @@ static int elantech_create_smbus(struct psmouse *psmouse,
        };
        unsigned int idx = 0;
 
-       smbus_board.properties = i2c_props;
-
        i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x",
                                                   info->x_max + 1);
        i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y",
@@ -1918,6 +1916,10 @@ static int elantech_create_smbus(struct psmouse *psmouse,
        if (elantech_is_buttonpad(info))
                i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad");
 
+       smbus_board.fwnode = fwnode_create_software_node(i2c_props, NULL);
+       if (IS_ERR(smbus_board.fwnode))
+               return PTR_ERR(smbus_board.fwnode);
+
        return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
                                  leave_breadcrumbs);
 }
index 472a03d..4e14b4d 100644 (file)
@@ -52,12 +52,15 @@ struct i2c_peripheral {
        enum i2c_adapter_type type;
        u32 pci_devid;
 
+       const struct property_entry *properties;
+
        struct i2c_client *client;
 };
 
 struct acpi_peripheral {
        char hid[ACPI_ID_LEN];
-       const struct property_entry *properties;
+       struct software_node swnode;
+       struct i2c_client *client;
 };
 
 struct chromeos_laptop {
@@ -68,7 +71,7 @@ struct chromeos_laptop {
        struct i2c_peripheral *i2c_peripherals;
        unsigned int num_i2c_peripherals;
 
-       const struct acpi_peripheral *acpi_peripherals;
+       struct acpi_peripheral *acpi_peripherals;
        unsigned int num_acpi_peripherals;
 };
 
@@ -161,7 +164,7 @@ static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
 
 static bool chromeos_laptop_adjust_client(struct i2c_client *client)
 {
-       const struct acpi_peripheral *acpi_dev;
+       struct acpi_peripheral *acpi_dev;
        struct acpi_device_id acpi_ids[2] = { };
        int i;
        int error;
@@ -175,8 +178,7 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client)
                memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN);
 
                if (acpi_match_device(acpi_ids, &client->dev)) {
-                       error = device_add_properties(&client->dev,
-                                                     acpi_dev->properties);
+                       error = device_add_software_node(&client->dev, &acpi_dev->swnode);
                        if (error) {
                                dev_err(&client->dev,
                                        "failed to add properties: %d\n",
@@ -184,6 +186,8 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client)
                                break;
                        }
 
+                       acpi_dev->client = client;
+
                        return true;
                }
        }
@@ -193,15 +197,28 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client)
 
 static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
 {
+       struct acpi_peripheral *acpi_dev;
        struct i2c_peripheral *i2c_dev;
        int i;
 
-       for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
-               i2c_dev = &cros_laptop->i2c_peripherals[i];
+       if (has_acpi_companion(&client->dev))
+               for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {
+                       acpi_dev = &cros_laptop->acpi_peripherals[i];
 
-               if (i2c_dev->client == client)
-                       i2c_dev->client = NULL;
-       }
+                       if (acpi_dev->client == client) {
+                               acpi_dev->client = NULL;
+                               return;
+                       }
+               }
+       else
+               for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
+                       i2c_dev = &cros_laptop->i2c_peripherals[i];
+
+                       if (i2c_dev->client == client) {
+                               i2c_dev->client = NULL;
+                               return;
+                       }
+               }
 }
 
 static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
@@ -302,28 +319,26 @@ static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = {
                .board_info     = {
                        I2C_BOARD_INFO("atmel_mxt_ts",
                                        ATMEL_TS_I2C_ADDR),
-                       .properties     =
-                               chromebook_atmel_touchscreen_props,
                        .flags          = I2C_CLIENT_WAKE,
                },
                .dmi_name       = "touchscreen",
                .irqflags       = IRQF_TRIGGER_FALLING,
                .type           = I2C_ADAPTER_PANEL,
                .alt_addr       = ATMEL_TS_I2C_BL_ADDR,
+               .properties     = chromebook_atmel_touchscreen_props,
        },
        /* Touchpad. */
        {
                .board_info     = {
                        I2C_BOARD_INFO("atmel_mxt_tp",
                                        ATMEL_TP_I2C_ADDR),
-                       .properties     =
-                               chromebook_pixel_trackpad_props,
                        .flags          = I2C_CLIENT_WAKE,
                },
                .dmi_name       = "trackpad",
                .irqflags       = IRQF_TRIGGER_FALLING,
                .type           = I2C_ADAPTER_VGADDC,
                .alt_addr       = ATMEL_TP_I2C_BL_ADDR,
+               .properties     = chromebook_pixel_trackpad_props,
        },
        /* Light Sensor. */
        {
@@ -414,8 +429,6 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = {
                .board_info     = {
                        I2C_BOARD_INFO("atmel_mxt_ts",
                                        ATMEL_TS_I2C_ADDR),
-                       .properties     =
-                               chromebook_atmel_touchscreen_props,
                        .flags          = I2C_CLIENT_WAKE,
                },
                .dmi_name       = "touchscreen",
@@ -423,6 +436,7 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = {
                .type           = I2C_ADAPTER_DESIGNWARE,
                .pci_devid      = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
                .alt_addr       = ATMEL_TS_I2C_BL_ADDR,
+               .properties     = chromebook_atmel_touchscreen_props,
        },
        /* Touchpad. */
        {
@@ -498,12 +512,16 @@ static struct acpi_peripheral samus_peripherals[] __initdata = {
        /* Touchpad */
        {
                .hid            = "ATML0000",
-               .properties     = samus_trackpad_props,
+               .swnode         = {
+                       .properties = samus_trackpad_props,
+               },
        },
        /* Touchsceen */
        {
                .hid            = "ATML0001",
-               .properties     = chromebook_atmel_touchscreen_props,
+               .swnode         = {
+                       .properties = chromebook_atmel_touchscreen_props,
+               },
        },
 };
 DECLARE_ACPI_CROS_LAPTOP(samus);
@@ -512,12 +530,16 @@ static struct acpi_peripheral generic_atmel_peripherals[] __initdata = {
        /* Touchpad */
        {
                .hid            = "ATML0000",
-               .properties     = chromebook_pixel_trackpad_props,
+               .swnode         = {
+                       .properties = chromebook_pixel_trackpad_props,
+               },
        },
        /* Touchsceen */
        {
                .hid            = "ATML0001",
-               .properties     = chromebook_atmel_touchscreen_props,
+               .swnode         = {
+                       .properties = chromebook_atmel_touchscreen_props,
+               },
        },
 };
 DECLARE_ACPI_CROS_LAPTOP(generic_atmel);
@@ -743,12 +765,11 @@ chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop,
                if (error)
                        goto err_out;
 
-               /* We need to deep-copy properties */
-               if (info->properties) {
-                       info->properties =
-                               property_entries_dup(info->properties);
-                       if (IS_ERR(info->properties)) {
-                               error = PTR_ERR(info->properties);
+               /* Create primary fwnode for the device - copies everything */
+               if (i2c_dev->properties) {
+                       info->fwnode = fwnode_create_software_node(i2c_dev->properties, NULL);
+                       if (IS_ERR(info->fwnode)) {
+                               error = PTR_ERR(info->fwnode);
                                goto err_out;
                        }
                }
@@ -760,8 +781,8 @@ err_out:
        while (--i >= 0) {
                i2c_dev = &cros_laptop->i2c_peripherals[i];
                info = &i2c_dev->board_info;
-               if (info->properties)
-                       property_entries_free(info->properties);
+               if (!IS_ERR_OR_NULL(info->fwnode))
+                       fwnode_remove_software_node(info->fwnode);
        }
        kfree(cros_laptop->i2c_peripherals);
        return error;
@@ -801,11 +822,11 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop,
                *acpi_dev = *src_dev;
 
                /* We need to deep-copy properties */
-               if (src_dev->properties) {
-                       acpi_dev->properties =
-                               property_entries_dup(src_dev->properties);
-                       if (IS_ERR(acpi_dev->properties)) {
-                               error = PTR_ERR(acpi_dev->properties);
+               if (src_dev->swnode.properties) {
+                       acpi_dev->swnode.properties =
+                               property_entries_dup(src_dev->swnode.properties);
+                       if (IS_ERR(acpi_dev->swnode.properties)) {
+                               error = PTR_ERR(acpi_dev->swnode.properties);
                                goto err_out;
                        }
                }
@@ -821,8 +842,8 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop,
 err_out:
        while (--i >= 0) {
                acpi_dev = &acpi_peripherals[i];
-               if (acpi_dev->properties)
-                       property_entries_free(acpi_dev->properties);
+               if (!IS_ERR_OR_NULL(acpi_dev->swnode.properties))
+                       property_entries_free(acpi_dev->swnode.properties);
        }
 
        kfree(acpi_peripherals);
@@ -833,21 +854,20 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)
 {
        const struct acpi_peripheral *acpi_dev;
        struct i2c_peripheral *i2c_dev;
-       struct i2c_board_info *info;
        int i;
 
        for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
                i2c_dev = &cros_laptop->i2c_peripherals[i];
-               info = &i2c_dev->board_info;
-
                i2c_unregister_device(i2c_dev->client);
-               property_entries_free(info->properties);
        }
 
        for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {
                acpi_dev = &cros_laptop->acpi_peripherals[i];
 
-               property_entries_free(acpi_dev->properties);
+               if (acpi_dev->client)
+                       device_remove_software_node(&acpi_dev->client->dev);
+
+               property_entries_free(acpi_dev->swnode.properties);
        }
 
        kfree(cros_laptop->i2c_peripherals);
index 20b11e0..673f41c 100644 (file)
@@ -35,6 +35,10 @@ static const struct property_entry bq27xxx_props[] = {
        { }
 };
 
+static const struct software_node bq27xxx_node = {
+       .properties = bq27xxx_props,
+};
+
 int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
 {
        struct device *dev = data->dev;
@@ -43,7 +47,7 @@ int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
        memset(&board_info, 0, sizeof(board_info));
        strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type));
        board_info.dev_name = "bq27542";
-       board_info.properties = bq27xxx_props;
+       board_info.swnode = &bq27xxx_node;
        data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info);
 
        return PTR_ERR_OR_ZERO(data->battery_fg);
index 5662265..e8f2ac8 100644 (file)
@@ -51,6 +51,9 @@ struct module;
 struct property_entry;
 
 #if IS_ENABLED(CONFIG_I2C)
+/* Return the Frequency mode string based on the bus frequency */
+const char *i2c_freq_mode_string(u32 bus_freq_hz);
+
 /*
  * The master routines are the ones normally used to transmit data to devices
  * on a bus (or read from them). Apart from two basic transfer functions to
@@ -306,6 +309,8 @@ struct i2c_driver {
  *     userspace_devices list
  * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
  *     calls it to pass on slave events to the slave driver.
+ * @devres_group_id: id of the devres group that will be created for resources
+ *     acquired when probing this device.
  *
  * An i2c_client identifies a single device (i.e. chip) connected to an
  * i2c bus. The behaviour exposed to Linux is defined by the driver
@@ -334,6 +339,7 @@ struct i2c_client {
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
        i2c_slave_cb_t slave_cb;        /* callback for slave mode      */
 #endif
+       void *devres_group_id;          /* ID of probe devres group     */
 };
 #define to_i2c_client(d) container_of(d, struct i2c_client, dev)
 
@@ -391,7 +397,7 @@ static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
  * @platform_data: stored in i2c_client.dev.platform_data
  * @of_node: pointer to OpenFirmware device node
  * @fwnode: device node supplied by the platform firmware
- * @properties: additional device properties for the device
+ * @swnode: software node for the device
  * @resources: resources associated with the device
  * @num_resources: number of resources in the @resources array
  * @irq: stored in i2c_client.irq
@@ -415,7 +421,7 @@ struct i2c_board_info {
        void            *platform_data;
        struct device_node *of_node;
        struct fwnode_handle *fwnode;
-       const struct property_entry *properties;
+       const struct software_node *swnode;
        const struct resource *resources;
        unsigned int    num_resources;
        int             irq;
@@ -687,6 +693,8 @@ struct i2c_adapter_quirks {
 #define I2C_AQ_NO_ZERO_LEN_READ                BIT(5)
 #define I2C_AQ_NO_ZERO_LEN_WRITE       BIT(6)
 #define I2C_AQ_NO_ZERO_LEN             (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE)
+/* adapter cannot do repeated START */
+#define I2C_AQ_NO_REP_START            BIT(7)
 
 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along
@@ -844,6 +852,7 @@ static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap)
  */
 #if IS_ENABLED(CONFIG_I2C)
 int i2c_add_adapter(struct i2c_adapter *adap);
+int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter);
 void i2c_del_adapter(struct i2c_adapter *adap);
 int i2c_add_numbered_adapter(struct i2c_adapter *adap);