diff options
69 files changed, 1747 insertions, 605 deletions
diff --git a/Documentation/devicetree/bindings/eeprom/at24.yaml b/Documentation/devicetree/bindings/eeprom/at24.yaml index e396e47b2f13..b6239ec3512b 100644 --- a/Documentation/devicetree/bindings/eeprom/at24.yaml +++ b/Documentation/devicetree/bindings/eeprom/at24.yaml @@ -116,6 +116,7 @@ properties: - const: atmel,24c02 - items: - enum: + - giantec,gt24c04a - onnn,cat24c04 - onnn,cat24c05 - rohm,br24g04 diff --git a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml index 6df27b47b922..5b9bd2feda3b 100644 --- a/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/aspeed,i2c.yaml @@ -44,11 +44,6 @@ properties: description: frequency of the bus clock in Hz defaults to 100 kHz when not specified - multi-master: - type: boolean - description: - states that there is another master active on this bus - required: - reg - compatible diff --git a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml index 82b9d6682297..a9dae5b52f28 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml @@ -38,6 +38,7 @@ properties: - rockchip,rk3308-i2c - rockchip,rk3328-i2c - rockchip,rk3568-i2c + - rockchip,rk3576-i2c - rockchip,rk3588-i2c - rockchip,rv1126-i2c - const: rockchip,rk3399-i2c diff --git a/Documentation/devicetree/bindings/i2c/i2c-sprd.txt b/Documentation/devicetree/bindings/i2c/i2c-sprd.txt deleted file mode 100644 index 7b6b3b8d0d11..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-sprd.txt +++ /dev/null @@ -1,31 +0,0 @@ -I2C for Spreadtrum platforms - -Required properties: -- compatible: Should be "sprd,sc9860-i2c". -- reg: Specify the physical base address of the controller and length - of memory mapped region. -- interrupts: Should contain I2C interrupt. -- clock-names: Should contain following entries: - "i2c" for I2C clock, - "source" for I2C source (parent) clock, - "enable" for I2C module enable clock. -- clocks: Should contain a clock specifier for each entry in clock-names. -- clock-frequency: Contains desired I2C bus clock frequency in Hz. -- #address-cells: Should be 1 to describe address cells for I2C device address. -- #size-cells: Should be 0 means no size cell for I2C device address. - -Optional properties: -- Child nodes conforming to I2C bus binding - -Examples: -i2c0: i2c@70500000 { - compatible = "sprd,sc9860-i2c"; - reg = <0 0x70500000 0 0x1000>; - interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>; - clock-names = "i2c", "source", "enable"; - clocks = <&clk_i2c3>, <&ext_26m>, <&clk_ap_apb_gates 11>; - clock-frequency = <400000>; - #address-cells = <1>; - #size-cells = <0>; -}; - diff --git a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml index 92fbc1a2671a..b57ae6963e62 100644 --- a/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml +++ b/Documentation/devicetree/bindings/i2c/nvidia,tegra20-i2c.yaml @@ -103,6 +103,9 @@ properties: items: - const: i2c + power-domains: + maxItems: 1 + dmas: items: - description: DMA channel for the reception FIFO @@ -124,6 +127,8 @@ allOf: - nvidia,tegra30-i2c then: properties: + clocks: + minItems: 2 clock-names: items: - const: div-clk @@ -133,20 +138,13 @@ allOf: properties: compatible: contains: - const: nvidia,tegra114-i2c - then: - properties: - clock-names: - items: - - const: div-clk - - - if: - properties: - compatible: - contains: - const: nvidia,tegra210-i2c + enum: + - nvidia,tegra114-i2c + - nvidia,tegra210-i2c then: properties: + clocks: + maxItems: 1 clock-names: items: - const: div-clk @@ -158,6 +156,8 @@ allOf: const: nvidia,tegra210-i2c-vi then: properties: + clocks: + minItems: 2 clock-names: items: - const: div-clk @@ -165,6 +165,9 @@ allOf: power-domains: items: - description: phandle to the VENC power domain + else: + properties: + power-domains: false unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml index c33ae7b63b84..7dab3852c7f8 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml @@ -130,6 +130,7 @@ allOf: then: properties: clocks: + minItems: 4 maxItems: 4 clock-names: items: diff --git a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml index 7993fe463c4c..505a8ec92266 100644 --- a/Documentation/devicetree/bindings/i2c/renesas,riic.yaml +++ b/Documentation/devicetree/bindings/i2c/renesas,riic.yaml @@ -25,6 +25,10 @@ properties: - renesas,riic-r9a07g054 # RZ/V2L - const: renesas,riic-rz # RZ/A or RZ/G2L + - items: + - const: renesas,riic-r9a08g045 # RZ/G3S + - const: renesas,riic-r9a09g057 # RZ/V2H(P) + - const: renesas,riic-r9a09g057 # RZ/V2H(P) reg: diff --git a/Documentation/devicetree/bindings/i2c/sprd,sc9860-i2c.yaml b/Documentation/devicetree/bindings/i2c/sprd,sc9860-i2c.yaml new file mode 100644 index 000000000000..ec0d39e73d26 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/sprd,sc9860-i2c.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/sprd,sc9860-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spreadtrum SC9860 I2C controller + +maintainers: + - Orson Zhai <orsonzhai@gmail.com> + - Baolin Wang <baolin.wang7@gmail.com> + - Chunyan Zhang <zhang.lyra@gmail.com> + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +properties: + compatible: + const: sprd,sc9860-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: I2C clock + - description: I2C source (parent) clock + - description: I2C module enable clock + + clock-names: + items: + - const: i2c + - const: source + - const: enable + + clock-frequency: true + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - clock-frequency + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2c@70500000 { + compatible = "sprd,sc9860-i2c"; + reg = <0x70500000 0x1000>; + interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_i2c3>, <&ext_26m>, <&clk_ap_apb_gates 11>; + clock-names = "i2c", "source", "enable"; + clock-frequency = <400000>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml b/Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml new file mode 100644 index 000000000000..28139b676661 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/tsd,mule-i2c-mux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Theobroma Systems Mule I2C multiplexer + +maintainers: + - Farouk Bouabid <farouk.bouabid@cherry.de> + - Quentin Schulz <quentin.schulz@cherry.de> + +description: | + Theobroma Systems Mule is an MCU that emulates a set of I2C devices, among + which devices that are reachable through an I2C-mux. The devices on the mux + can be selected by writing the appropriate device number to an I2C config + register. + + + +--------------------------------------------------+ + | Mule | + 0x18| +---------------+ | + -------->|Config register|----+ | + | +---------------+ | | + | V_ | + | | \ +--------+ | + | | \-------->| dev #0 | | + | | | +--------+ | + 0x6f| | M |-------->| dev #1 | | + ---------------------------->| U | +--------+ | + | | X |-------->| dev #2 | | + | | | +--------+ | + | | /-------->| dev #3 | | + | |__/ +--------+ | + +--------------------------------------------------+ + + +allOf: + - $ref: /schemas/i2c/i2c-mux.yaml# + +properties: + compatible: + const: tsd,mule-i2c-mux + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + i2c-mux { + compatible = "tsd,mule-i2c-mux"; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + + rtc@6f { + compatible = "isil,isl1208"; + reg = <0x6f>; + }; + }; + }; +... + diff --git a/Documentation/i2c/slave-testunit-backend.rst b/Documentation/i2c/slave-testunit-backend.rst index 37142a48ab35..d752f433be07 100644 --- a/Documentation/i2c/slave-testunit-backend.rst +++ b/Documentation/i2c/slave-testunit-backend.rst @@ -20,11 +20,25 @@ Instantiating the device is regular. Example for bus 0, address 0x30:: # echo "slave-testunit 0x1030" > /sys/bus/i2c/devices/i2c-0/new_device -After that, you will have a write-only device listening. Reads will just return -an 8-bit version number of the testunit. When writing, the device consists of 4 -8-bit registers and, except for some "partial" commands, all registers must be -written to start a testcase, i.e. you usually write 4 bytes to the device. The -registers are: +Or using firmware nodes. Here is a devicetree example (note this is only a +debug device, so there are no official DT bindings):: + + &i2c0 { + ... + + testunit@30 { + compatible = "slave-testunit"; + reg = <(0x30 | I2C_OWN_SLAVE_ADDRESS)>; + }; + }; + +After that, you will have the device listening. Reading will return a single +byte. Its value is 0 if the testunit is idle, otherwise the command number of +the currently running command. + +When writing, the device consists of 4 8-bit registers and, except for some +"partial" commands, all registers must be written to start a testcase, i.e. you +usually write 4 bytes to the device. The registers are: .. csv-table:: :header: "Offset", "Name", "Description" @@ -75,7 +89,7 @@ from another device on the bus. If the bus master under test also wants to access the bus at the same time, the bus will be busy. Example to read 128 bytes from device 0x50 after 50ms of delay:: - # i2cset -y 0 0x30 0x01 0x50 0x80 0x05 i + # i2cset -y 0 0x30 1 0x50 0x80 5 i 0x02 SMBUS_HOST_NOTIFY ~~~~~~~~~~~~~~~~~~~~~~ @@ -95,9 +109,9 @@ bytes from device 0x50 after 50ms of delay:: Also needs master mode. This test will send an SMBUS_HOST_NOTIFY message to the host. Note that the status word is currently ignored in the Linux Kernel. -Example to send a notification after 10ms:: +Example to send a notification with status word 0x6442 after 10ms:: - # i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i + # i2cset -y 0 0x30 2 0x42 0x64 1 i If the host controller supports HostNotify, this message with debug level should appear (Linux 6.11 and later):: @@ -116,7 +130,7 @@ should appear (Linux 6.11 and later):: - DELAY * - 0x03 - - must be '1', i.e. one further byte will be written + - 0x01 (i.e. one further byte will be written) - number of bytes to be sent back - leave out, partial command! @@ -131,5 +145,91 @@ from length-1 to 0. Here is an example which emulates i2c_smbus_block_process_call() using i2ctransfer (you need i2c-tools v4.2 or later):: - # i2ctransfer -y 0 w3@0x30 0x03 0x01 0x10 r? + # i2ctransfer -y 0 w3@0x30 3 1 0x10 r? 0x10 0x0f 0x0e 0x0d 0x0c 0x0b 0x0a 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00 + +0x04 GET_VERSION_WITH_REP_START +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + + * - CMD + - DATAL + - DATAH + - DELAY + + * - 0x04 + - currently unused + - currently unused + - leave out, partial command! + +Partial command. After sending this command, the testunit will reply to a read +message with a NUL terminated version string based on UTS_RELEASE. The first +character is always a 'v' and the length of the version string is at maximum +128 bytes. However, it will only respond if the read message is connected to +the write message via repeated start. If your controller driver handles +repeated start correctly, this will work:: + + # i2ctransfer -y 0 w3@0x30 4 0 0 r128 + 0x76 0x36 0x2e 0x31 0x31 0x2e 0x30 0x2d 0x72 0x63 0x31 0x2d 0x30 0x30 0x30 0x30 ... + +If you have i2c-tools 4.4 or later, you can print out the data right away:: + + # i2ctransfer -y -b 0 w3@0x30 4 0 0 r128 + v6.11.0-rc1-00009-gd37a1b4d3fd0 + +STOP/START combinations between the two messages will *not* work because they +are not equivalent to a REPEATED START. As an example, this returns just the +default response:: + + # i2cset -y 0 0x30 4 0 0 i; i2cget -y 0 0x30 + 0x00 + +0x05 SMBUS_ALERT_REQUEST +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + + * - CMD + - DATAL + - DATAH + - DELAY + + * - 0x05 + - response value (7 MSBs interpreted as I2C address) + - currently unused + - n * 10ms + +This test raises an interrupt via the SMBAlert pin which the host controller +must handle. The pin must be connected to the testunit as a GPIO. GPIO access +is not allowed to sleep. Currently, this can only be described using firmware +nodes. So, for devicetree, you would add something like this to the testunit +node:: + + gpios = <&gpio1 24 GPIO_ACTIVE_LOW>; + +The following command will trigger the alert with a response of 0xc9 after 1 +second of delay:: + + # i2cset -y 0 0x30 5 0xc9 0x00 100 i + +If the host controller supports SMBusAlert, this message with debug level +should appear:: + + smbus_alert 0-000c: SMBALERT# from dev 0x64, flag 1 + +This message may appear more than once because the testunit is software not +hardware and, thus, may not be able to react to the response of the host fast +enough. The interrupt count should increase only by one, though:: + + # cat /proc/interrupts | grep smbus_alert + 93: 1 gpio-rcar 26 Edge smbus_alert + +If the host does not respond to the alert within 1 second, the test will be +aborted and the testunit will report an error. + +For this test, the testunit will shortly drop its assigned address and listen +on the SMBus Alert Response Address (0x0c). It will reassign its original +address afterwards. diff --git a/arch/arc/configs/axs101_defconfig b/arch/arc/configs/axs101_defconfig index 89720d6d7e0d..319bbe270322 100644 --- a/arch/arc/configs/axs101_defconfig +++ b/arch/arc/configs/axs101_defconfig @@ -66,6 +66,7 @@ CONFIG_SERIAL_OF_PLATFORM=y # CONFIG_HW_RANDOM is not set CONFIG_I2C=y CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y # CONFIG_HWMON is not set CONFIG_DRM=m diff --git a/arch/arc/configs/axs103_defconfig b/arch/arc/configs/axs103_defconfig index 73ec01ed0492..8c1f1a111a17 100644 --- a/arch/arc/configs/axs103_defconfig +++ b/arch/arc/configs/axs103_defconfig @@ -66,6 +66,7 @@ CONFIG_SERIAL_OF_PLATFORM=y # CONFIG_HW_RANDOM is not set CONFIG_I2C=y CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y # CONFIG_HWMON is not set CONFIG_FB=y diff --git a/arch/arc/configs/axs103_smp_defconfig b/arch/arc/configs/axs103_smp_defconfig index 4da0f626fa9d..75cab9f25b5b 100644 --- a/arch/arc/configs/axs103_smp_defconfig +++ b/arch/arc/configs/axs103_smp_defconfig @@ -66,6 +66,7 @@ CONFIG_SERIAL_OF_PLATFORM=y # CONFIG_HW_RANDOM is not set CONFIG_I2C=y CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y # CONFIG_HWMON is not set CONFIG_DRM=m diff --git a/arch/arc/configs/tb10x_defconfig b/arch/arc/configs/tb10x_defconfig index 1a68e4beebca..5aba3d850fa2 100644 --- a/arch/arc/configs/tb10x_defconfig +++ b/arch/arc/configs/tb10x_defconfig @@ -60,6 +60,7 @@ CONFIG_SERIAL_8250_DW=y # CONFIG_HW_RANDOM is not set CONFIG_I2C=y # CONFIG_I2C_COMPAT is not set +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_GPIO_SYSFS=y # CONFIG_HWMON is not set diff --git a/arch/arm/configs/hisi_defconfig b/arch/arm/configs/hisi_defconfig index 0376a65e8bc1..e19c1039fb93 100644 --- a/arch/arm/configs/hisi_defconfig +++ b/arch/arm/configs/hisi_defconfig @@ -43,6 +43,7 @@ CONFIG_SERIAL_8250_DW=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y CONFIG_SPI_PL022=y diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 62734530a3d6..9a5f5c439b87 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -413,6 +413,7 @@ CONFIG_I2C_AT91=m CONFIG_I2C_BCM2835=y CONFIG_I2C_CADENCE=y CONFIG_I2C_DAVINCI=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_DIGICOLOR=m CONFIG_I2C_EMEV2=m diff --git a/arch/arm/configs/pxa_defconfig b/arch/arm/configs/pxa_defconfig index f2ca5c9131b5..e1cb170c2bf0 100644 --- a/arch/arm/configs/pxa_defconfig +++ b/arch/arm/configs/pxa_defconfig @@ -277,6 +277,7 @@ CONFIG_HW_RANDOM=y CONFIG_I2C_CHARDEV=m CONFIG_I2C_MUX_PCA954x=m CONFIG_I2C_MUX_PINCTRL=m +CONFIG_I2C_DESIGNWARE_CORE=m CONFIG_I2C_DESIGNWARE_PLATFORM=m CONFIG_I2C_GPIO=y CONFIG_I2C_PXA_SLAVE=y diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index e82c3866b810..294906c8f16e 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -83,6 +83,7 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=2 CONFIG_SERIAL_8250_DW=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y CONFIG_SPI_CADENCE_QUADSPI=y diff --git a/arch/arm/configs/spear13xx_defconfig b/arch/arm/configs/spear13xx_defconfig index c8128a6180e7..a8f992fdb30d 100644 --- a/arch/arm/configs/spear13xx_defconfig +++ b/arch/arm/configs/spear13xx_defconfig @@ -62,6 +62,7 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y # CONFIG_HW_RANDOM is not set CONFIG_RAW_DRIVER=y CONFIG_I2C=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y CONFIG_SPI_PL022=y diff --git a/arch/arm/configs/spear3xx_defconfig b/arch/arm/configs/spear3xx_defconfig index 97ea2e9a6f07..8dc5a388759c 100644 --- a/arch/arm/configs/spear3xx_defconfig +++ b/arch/arm/configs/spear3xx_defconfig @@ -42,6 +42,7 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y # CONFIG_HW_RANDOM is not set CONFIG_RAW_DRIVER=y CONFIG_I2C=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y CONFIG_SPI_PL022=y diff --git a/arch/arm/configs/spear6xx_defconfig b/arch/arm/configs/spear6xx_defconfig index a7a3413ac968..4e9e1a6ff381 100644 --- a/arch/arm/configs/spear6xx_defconfig +++ b/arch/arm/configs/spear6xx_defconfig @@ -33,6 +33,7 @@ CONFIG_STMMAC_ETH=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_I2C=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y CONFIG_SPI_PL022=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 187e7cc477e0..5fdbfea7a5b2 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -518,6 +518,7 @@ CONFIG_I2C_MUX=y CONFIG_I2C_MUX_PCA954x=y CONFIG_I2C_BCM2835=m CONFIG_I2C_CADENCE=m +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_GPIO=m CONFIG_I2C_IMX=y diff --git a/arch/mips/configs/generic/board-ocelot.config b/arch/mips/configs/generic/board-ocelot.config index 8cfbafa532e0..a5b5b5102472 100644 --- a/arch/mips/configs/generic/board-ocelot.config +++ b/arch/mips/configs/generic/board-ocelot.config @@ -31,6 +31,7 @@ CONFIG_MICROSEMI_PHY=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MUX=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index ee978cc74673..572eac58f33b 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -157,6 +157,7 @@ CONFIG_HW_RANDOM_VIRTIO=y CONFIG_HW_RANDOM_JH7110=m CONFIG_I2C=y CONFIG_I2C_CHARDEV=m +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_MV64XXX=m CONFIG_I2C_RIIC=y diff --git a/arch/riscv/configs/nommu_k210_defconfig b/arch/riscv/configs/nommu_k210_defconfig index af9601da4643..87ff5a1233af 100644 --- a/arch/riscv/configs/nommu_k210_defconfig +++ b/arch/riscv/configs/nommu_k210_defconfig @@ -58,6 +58,7 @@ CONFIG_I2C=y # CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y # CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y # CONFIG_SPI_MEM is not set diff --git a/arch/riscv/configs/nommu_k210_sdcard_defconfig b/arch/riscv/configs/nommu_k210_sdcard_defconfig index dd460c649152..95cbd574f291 100644 --- a/arch/riscv/configs/nommu_k210_sdcard_defconfig +++ b/arch/riscv/configs/nommu_k210_sdcard_defconfig @@ -50,6 +50,7 @@ CONFIG_DEVTMPFS_MOUNT=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y # CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_SPI=y # CONFIG_SPI_MEM is not set diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 44710267d669..c232054fddd6 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -40,14 +40,6 @@ config I2C_BOARDINFO bool default y -config I2C_COMPAT - bool "Enable compatibility bits for old user-space" - default y - help - Say Y here if you intend to run lm-sensors 3.1.1 or older, or any - other user-space package which expects i2c adapters to be class - devices. If you don't know, say Y. - config I2C_CHARDEV tristate "I2C device interface" help diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index a22f9125322a..53f18b351f53 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -559,28 +559,33 @@ config I2C_DAVINCI For details please see http://www.ti.com/davinci config I2C_DESIGNWARE_CORE - tristate + tristate "Synopsys DesignWare I2C adapter" select REGMAP + help + This option enables support for the Synopsys DesignWare I2C adapter. + This driver includes support for the I2C host on the Synopsys + Designware I2C adapter. + + To compile the driver as a module, choose M here: the module will be + called i2c-designware-core. + +if I2C_DESIGNWARE_CORE config I2C_DESIGNWARE_SLAVE bool "Synopsys DesignWare Slave" - depends on I2C_DESIGNWARE_CORE select I2C_SLAVE help If you say yes to this option, support will be included for the Synopsys DesignWare I2C slave adapter. - This is not a standalone module, this module compiles together with - i2c-designware-core. - config I2C_DESIGNWARE_PLATFORM - tristate "Synopsys DesignWare Platform" + tristate "Synopsys DesignWare Platform driver" depends on (ACPI && COMMON_CLK) || !ACPI - select I2C_DESIGNWARE_CORE select MFD_SYSCON if MIPS_BAIKAL_T1 + default I2C_DESIGNWARE_CORE help If you say yes to this option, support will be included for the - Synopsys DesignWare I2C adapter. + Synopsys DesignWare I2C adapters on the platform bus. This driver can also be built as a module. If so, the module will be called i2c-designware-platform. @@ -613,17 +618,19 @@ config I2C_DESIGNWARE_BAYTRAIL a BayTrail system using the AXP288. config I2C_DESIGNWARE_PCI - tristate "Synopsys DesignWare PCI" + tristate "Synopsys DesignWare PCI driver" depends on PCI - select I2C_DESIGNWARE_CORE select I2C_CCGX_UCSI help If you say yes to this option, support will be included for the - Synopsys DesignWare I2C adapter. Only master mode is supported. + Synopsys DesignWare I2C adapters on the PCI bus. Only master mode is + supported. This driver can also be built as a module. If so, the module will be called i2c-designware-pci. +endif + config I2C_DIGICOLOR tristate "Conexant Digicolor I2C driver" depends on ARCH_DIGICOLOR || COMPILE_TEST @@ -772,6 +779,17 @@ config I2C_JZ4780 If you don't know what to do here, say N. +config I2C_KEBA + tristate "KEBA I2C controller support" + depends on HAS_IOMEM + select AUXILIARY_BUS + help + This driver supports the I2C controller found in KEBA system FPGA + devices. + + This driver can also be built as a module. If so, the module + will be called i2c-keba. + config I2C_KEMPLD tristate "Kontron COM I2C Controller" depends on MFD_KEMPLD diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d0561339e5..ecc07c50f2a0 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o +obj-$(CONFIG_I2C_KEBA) += i2c-keba.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index 9d7b4efe26ad..544c94e86b89 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -479,9 +479,8 @@ static struct i2c_adapter ali1535_adapter = { static const struct pci_device_id ali1535_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, - { }, + { } }; - MODULE_DEVICE_TABLE(pci, ali1535_ids); static int ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id) diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c index d3ac1c77a509..6f0ef587e76d 100644 --- a/drivers/i2c/busses/i2c-amd-mp2-plat.c +++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c @@ -340,7 +340,7 @@ static void i2c_amd_remove(struct platform_device *pdev) static const struct acpi_device_id i2c_amd_acpi_match[] = { { "AMDI0011" }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match); diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 2a03a221e2dd..cc5a26637fd5 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -991,7 +991,7 @@ static const struct of_device_id aspeed_i2c_bus_of_table[] = { .compatible = "aspeed,ast2600-i2c-bus", .data = aspeed_i2c_25xx_get_clk_reg_val, }, - { }, + { } }; MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table); diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index e8a688d04aee..080204182bb5 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -20,12 +20,17 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/pm.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/swab.h> #include <linux/types.h> #include <linux/units.h> +#define DEFAULT_SYMBOL_NAMESPACE I2C_DW_COMMON + #include "i2c-designware-core.h" static char *abort_sources[] = { @@ -188,7 +193,7 @@ static const u32 supported_speeds[] = { I2C_MAX_STANDARD_MODE_FREQ, }; -int i2c_dw_validate_speed(struct dw_i2c_dev *dev) +static int i2c_dw_validate_speed(struct dw_i2c_dev *dev) { struct i2c_timings *t = &dev->timings; unsigned int i; @@ -208,7 +213,44 @@ int i2c_dw_validate_speed(struct dw_i2c_dev *dev) return -EINVAL; } -EXPORT_SYMBOL_GPL(i2c_dw_validate_speed); + +#ifdef CONFIG_OF + +#include <linux/platform_device.h> + +#define MSCC_ICPU_CFG_TWI_DELAY 0x0 +#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0) +#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4 + +static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev) +{ + writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE, + dev->ext + MSCC_ICPU_CFG_TWI_DELAY); + + return 0; +} + +static void i2c_dw_of_configure(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct dw_i2c_dev *dev = dev_get_drvdata(device); + + switch (dev->flags & MODEL_MASK) { + case MODEL_MSCC_OCELOT: + dev->ext = devm_platform_ioremap_resource(pdev, 1); + if (!IS_ERR(dev->ext)) + dev->set_sda_hold_time = mscc_twi_set_sda_hold_time; + break; + default: + break; + } +} + +#else /* CONFIG_OF */ + +static inline void i2c_dw_of_configure(struct device *device) { } + +#endif /* CONFIG_OF */ #ifdef CONFIG_ACPI @@ -255,7 +297,7 @@ static void i2c_dw_acpi_params(struct device *device, char method[], kfree(buf.pointer); } -int i2c_dw_acpi_configure(struct device *device) +static void i2c_dw_acpi_configure(struct device *device) { struct dw_i2c_dev *dev = dev_get_drvdata(device); struct i2c_timings *t = &dev->timings; @@ -285,10 +327,7 @@ int i2c_dw_acpi_configure(struct device *device) dev->sda_hold_time = fs_ht; break; } - - return 0; } -EXPORT_SYMBOL_GPL(i2c_dw_acpi_configure); static u32 i2c_dw_acpi_round_bus_speed(struct device *device) { @@ -310,11 +349,13 @@ static u32 i2c_dw_acpi_round_bus_speed(struct device *device) #else /* CONFIG_ACPI */ +static inline void i2c_dw_acpi_configure(struct device *device) { } + static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0; } #endif /* CONFIG_ACPI */ -void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev) +static void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev) { u32 acpi_speed = i2c_dw_acpi_round_bus_speed(dev->dev); struct i2c_timings *t = &dev->timings; @@ -330,10 +371,47 @@ void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev) else t->bus_freq_hz = I2C_MAX_FAST_MODE_FREQ; } -EXPORT_SYMBOL_GPL(i2c_dw_adjust_bus_speed); -u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + struct device *device = dev->dev; + struct fwnode_handle *fwnode = dev_fwnode(device); + + i2c_parse_fw_timings(device, t, false); + + i2c_dw_adjust_bus_speed(dev); + + if (is_of_node(fwnode)) + i2c_dw_of_configure(device); + else if (is_acpi_node(fwnode)) + i2c_dw_acpi_configure(device); + + return i2c_dw_validate_speed(dev); +} +EXPORT_SYMBOL_GPL(i2c_dw_fw_parse_and_configure); + +static u32 i2c_dw_read_scl_reg(struct dw_i2c_dev *dev, u32 reg) +{ + u32 val; + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return 0; + + ret = regmap_read(dev->map, reg, &val); + i2c_dw_release_lock(dev); + + return ret ? 0 : val; +} + +u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, + u32 tSYMBOL, u32 tf, int cond, int offset) { + if (!ic_clk) + return i2c_dw_read_scl_reg(dev, reg); + /* * DesignWare I2C core doesn't seem to have solid strategy to meet * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec @@ -372,8 +450,12 @@ u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) 3 + offset; } -u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, + u32 tLOW, u32 tf, int offset) { + if (!ic_clk) + return i2c_dw_read_scl_reg(dev, reg); + /* * Conditional expression: * @@ -653,6 +735,84 @@ void i2c_dw_disable(struct dw_i2c_dev *dev) i2c_dw_release_lock(dev); } +EXPORT_SYMBOL_GPL(i2c_dw_disable); + +int i2c_dw_probe(struct dw_i2c_dev *dev) +{ + device_set_node(&dev->adapter.dev, dev_fwnode(dev->dev)); + + switch (dev->mode) { + case DW_IC_SLAVE: + return i2c_dw_probe_slave(dev); + case DW_IC_MASTER: + return i2c_dw_probe_master(dev); + default: + dev_err(dev->dev, "Wrong operation mode: %d\n", dev->mode); + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(i2c_dw_probe); + +static int i2c_dw_prepare(struct device *device) +{ + /* + * If the ACPI companion device object is present for this device, + * it may be accessed during suspend and resume of other devices via + * I2C operation regions, so tell the PM core and middle layers to + * avoid skipping system suspend/resume callbacks for it in that case. + */ + return !has_acpi_companion(device); +} + +static int i2c_dw_runtime_suspend(struct device *device) +{ + struct dw_i2c_dev *dev = dev_get_drvdata(device); + + if (dev->shared_with_punit) + return 0; + + i2c_dw_disable(dev); + i2c_dw_prepare_clk(dev, false); + + return 0; +} + +static int i2c_dw_suspend(struct device *device) +{ + struct dw_i2c_dev *dev = dev_get_drvdata(device); + + i2c_mark_adapter_suspended(&dev->adapter); + + return i2c_dw_runtime_suspend(device); +} + +static int i2c_dw_runtime_resume(struct device *device) +{ + struct dw_i2c_dev *dev = dev_get_drvdata(device); + + if (!dev->shared_with_punit) + i2c_dw_prepare_clk(dev, true); + + dev->init(dev); + + return 0; +} + +static int i2c_dw_resume(struct device *device) +{ + struct dw_i2c_dev *dev = dev_get_drvdata(device); + + i2c_dw_runtime_resume(device); + i2c_mark_adapter_resumed(&dev->adapter); + + return 0; +} + +EXPORT_GPL_DEV_PM_OPS(i2c_dw_dev_pm_ops) = { + .prepare = pm_sleep_ptr(i2c_dw_prepare), + LATE_SYSTEM_SLEEP_PM_OPS(i2c_dw_suspend, i2c_dw_resume) + RUNTIME_PM_OPS(i2c_dw_runtime_suspend, i2c_dw_runtime_resume, NULL) +}; MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index e9606c00b8d1..1ac2afd03a0a 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -10,11 +10,10 @@ */ #include <linux/bits.h> -#include <linux/compiler_types.h> #include <linux/completion.h> -#include <linux/dev_printk.h> #include <linux/errno.h> #include <linux/i2c.h> +#include <linux/pm.h> #include <linux/regmap.h> #include <linux/types.h> @@ -237,7 +236,6 @@ struct reset_control; * @semaphore_idx: Index of table with semaphore type attached to the bus. It's * -1 if there is no semaphore. * @shared_with_punit: true if this bus is shared with the SoCs PUNIT - * @disable: function to disable the controller * @init: function to initialize the I2C hardware * @set_sda_hold_time: callback to retrieve IP specific SDA hold timing * @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE @@ -295,7 +293,6 @@ struct dw_i2c_dev { void (*release_lock)(void); int semaphore_idx; bool shared_with_punit; - void (*disable)(struct dw_i2c_dev *dev); int (*init)(struct dw_i2c_dev *dev); int (*set_sda_hold_time)(struct dw_i2c_dev *dev); int mode; @@ -329,8 +326,10 @@ struct i2c_dw_semaphore_callbacks { }; 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); +u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, + u32 tSYMBOL, u32 tf, int cond, int offset); +u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, + u32 tLOW, u32 tf, int offset); int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev); u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev); int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare); @@ -340,7 +339,8 @@ int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev); int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev); int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev); u32 i2c_dw_func(struct i2c_adapter *adap); -void i2c_dw_disable(struct dw_i2c_dev *dev); + +extern const struct dev_pm_ops i2c_dw_dev_pm_ops; static inline void __i2c_dw_enable(struct dw_i2c_dev *dev) { @@ -373,6 +373,7 @@ static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev, } void __i2c_dw_disable(struct dw_i2c_dev *dev); +void i2c_dw_disable(struct dw_i2c_dev *dev); extern void i2c_dw_configure_master(struct dw_i2c_dev *dev); extern int i2c_dw_probe_master(struct dw_i2c_dev *dev); @@ -385,19 +386,6 @@ static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { } static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; } #endif -static inline int i2c_dw_probe(struct dw_i2c_dev *dev) -{ - switch (dev->mode) { - case DW_IC_SLAVE: - return i2c_dw_probe_slave(dev); - case DW_IC_MASTER: - return i2c_dw_probe_master(dev); - default: - dev_err(dev->dev, "Wrong operation mode: %d\n", dev->mode); - return -EINVAL; - } -} - static inline void i2c_dw_configure(struct dw_i2c_dev *dev) { if (i2c_detect_slave_mode(dev->dev)) @@ -406,6 +394,8 @@ static inline void i2c_dw_configure(struct dw_i2c_dev *dev) i2c_dw_configure_master(dev); } +int i2c_dw_probe(struct dw_i2c_dev *dev); + #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev); #endif @@ -414,11 +404,4 @@ int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev); int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev); #endif -int i2c_dw_validate_speed(struct dw_i2c_dev *dev); -void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev); - -#if IS_ENABLED(CONFIG_ACPI) -int i2c_dw_acpi_configure(struct device *device); -#else -static inline int i2c_dw_acpi_configure(struct device *device) { return -ENODEV; } -#endif +int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index c7e56002809a..e46f1b22c360 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -22,6 +22,8 @@ #include <linux/regmap.h> #include <linux/reset.h> +#define DEFAULT_SYMBOL_NAMESPACE I2C_DW + #include "i2c-designware-core.h" #define AMD_TIMEOUT_MIN_US 25 @@ -64,13 +66,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) if (!dev->ss_hcnt || !dev->ss_lcnt) { ic_clk = i2c_dw_clk_rate(dev); dev->ss_hcnt = - i2c_dw_scl_hcnt(ic_clk, + i2c_dw_scl_hcnt(dev, + DW_IC_SS_SCL_HCNT, + ic_clk, 4000, /* tHD;STA = tHIGH = 4.0 us */ sda_falling_time, 0, /* 0: DW default, 1: Ideal */ 0); /* No offset */ dev->ss_lcnt = - i2c_dw_scl_lcnt(ic_clk, + i2c_dw_scl_lcnt(dev, + DW_IC_SS_SCL_LCNT, + ic_clk, 4700, /* tLOW = 4.7 us */ scl_falling_time, 0); /* No offset */ @@ -94,13 +100,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) } else { ic_clk = i2c_dw_clk_rate(dev); dev->fs_hcnt = - i2c_dw_scl_hcnt(ic_clk, + i2c_dw_scl_hcnt(dev, + DW_IC_FS_SCL_HCNT, + ic_clk, 260, /* tHIGH = 260 ns */ sda_falling_time, 0, /* DW default */ 0); /* No offset */ dev->fs_lcnt = - i2c_dw_scl_lcnt(ic_clk, + i2c_dw_scl_lcnt(dev, + DW_IC_FS_SCL_LCNT, + ic_clk, 500, /* tLOW = 500 ns */ scl_falling_time, 0); /* No offset */ @@ -114,13 +124,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) if (!dev->fs_hcnt || !dev->fs_lcnt) { ic_clk = i2c_dw_clk_rate(dev); dev->fs_hcnt = - i2c_dw_scl_hcnt(ic_clk, + i2c_dw_scl_hcnt(dev, + DW_IC_FS_SCL_HCNT, + ic_clk, 600, /* tHD;STA = tHIGH = 0.6 us */ sda_falling_time, 0, /* 0: DW default, 1: Ideal */ 0); /* No offset */ dev->fs_lcnt = - i2c_dw_scl_lcnt(ic_clk, + i2c_dw_scl_lcnt(dev, + DW_IC_FS_SCL_LCNT, + ic_clk, 1300, /* tLOW = 1.3 us */ scl_falling_time, 0); /* No offset */ @@ -142,13 +156,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) } else if (!dev->hs_hcnt || !dev->hs_lcnt) { ic_clk = i2c_dw_clk_rate(dev); dev->hs_hcnt = - i2c_dw_scl_hcnt(ic_clk, + i2c_dw_scl_hcnt(dev, + DW_IC_HS_SCL_HCNT, + ic_clk, 160, /* tHIGH = 160 ns */ sda_falling_time, 0, /* DW default */ 0); /* No offset */ dev->hs_lcnt = - i2c_dw_scl_lcnt(ic_clk, + i2c_dw_scl_lcnt(dev, + DW_IC_HS_SCL_LCNT, + ic_clk, 320, /* tLOW = 320 ns */ scl_falling_time, 0); /* No offset */ @@ -931,7 +949,6 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) init_completion(&dev->cmd_complete); dev->init = i2c_dw_init_master; - dev->disable = i2c_dw_disable; ret = i2c_dw_init_regmap(dev); if (ret) @@ -1021,3 +1038,4 @@ EXPORT_SYMBOL_GPL(i2c_dw_probe_master); MODULE_DESCRIPTION("Synopsys DesignWare I2C bus master adapter"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(I2C_DW_COMMON); diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index a1b379a1e904..7b2c5d71a7fc 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -9,7 +9,6 @@ * Copyright (C) 2009 Provigent Ltd. * Copyright (C) 2011, 2015, 2016 Intel Corporation. */ -#include <linux/acpi.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/errno.h> @@ -19,6 +18,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/power_supply.h> #include <linux/sched.h> @@ -102,7 +102,7 @@ static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev) static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { - struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); + struct dw_i2c_dev *dev = pci_get_drvdata(pdev); switch (pdev->device) { case 0x0817: @@ -152,7 +152,7 @@ static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev) static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { - struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); + struct dw_i2c_dev *dev = pci_get_drvdata(pdev); dev->flags |= MODEL_AMD_NAVI_GPU | ACCESS_POLLING; dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ; @@ -194,47 +194,6 @@ static struct dw_pci_controller dw_pci_controllers[] = { }, }; -static int __maybe_unused i2c_dw_pci_runtime_suspend(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - - i_dev->disable(i_dev); - return 0; -} - -static int __maybe_unused i2c_dw_pci_suspend(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - - i2c_mark_adapter_suspended(&i_dev->adapter); - - return i2c_dw_pci_runtime_suspend(dev); -} - -static int __maybe_unused i2c_dw_pci_runtime_resume(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - - return i_dev->init(i_dev); -} - -static int __maybe_unused i2c_dw_pci_resume(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - int ret; - - ret = i2c_dw_pci_runtime_resume(dev); - - i2c_mark_adapter_resumed(&i_dev->adapter); - - return ret; -} - -static const struct dev_pm_ops i2c_dw_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume) - SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, i2c_dw_pci_runtime_resume, NULL) -}; - static const struct property_entry dgpu_properties[] = { /* USB-C doesn't power the system */ PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE), @@ -253,7 +212,6 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, int r; struct dw_pci_controller *controller; struct dw_scl_sda_cfg *cfg; - struct i2c_timings *t; if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) return dev_err_probe(&pdev->dev, -EINVAL, @@ -288,29 +246,17 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, dev->irq = pci_irq_vector(pdev, 0); dev->flags |= controller->flags; - t = &dev->timings; - i2c_parse_fw_timings(&pdev->dev, t, false); - pci_set_drvdata(pdev, dev); if (controller->setup) { r = controller->setup(pdev, controller); - if (r) { - pci_free_irq_vectors(pdev); + if (r) return r; - } } - i2c_dw_adjust_bus_speed(dev); - - if (has_acpi_companion(&pdev->dev)) - i2c_dw_acpi_configure(&pdev->dev); - - r = i2c_dw_validate_speed(dev); - if (r) { - pci_free_irq_vectors(pdev); + r = i2c_dw_fw_parse_and_configure(dev); + if (r) return r; - } i2c_dw_configure(dev); @@ -326,14 +272,11 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, adap = &dev->adapter; adap->owner = THIS_MODULE; adap->class = 0; - ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); adap->nr = controller->bus_num; r = i2c_dw_probe(dev); - if (r) { - pci_free_irq_vectors(pdev); + if (r) return r; - } if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, &dgpu_node); @@ -354,16 +297,15 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev) { struct dw_i2c_dev *dev = pci_get_drvdata(pdev); - dev->disable(dev); + i2c_dw_disable(dev); + pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); i2c_del_adapter(&dev->adapter); - devm_free_irq(&pdev->dev, dev->irq, dev); - pci_free_irq_vectors(pdev); } -static const struct pci_device_id i2_designware_pci_ids[] = { +static const struct pci_device_id i2c_designware_pci_ids[] = { /* Medfield */ { PCI_VDEVICE(INTEL, 0x0817), medfield }, { PCI_VDEVICE(INTEL, 0x0818), medfield }, @@ -409,21 +351,23 @@ static const struct pci_device_id i2_designware_pci_ids[] = { { PCI_VDEVICE(ATI, 0x73c4), navi_amd }, { PCI_VDEVICE(ATI, 0x7444), navi_amd }, { PCI_VDEVICE(ATI, 0x7464), navi_amd }, - { 0,} + {} }; -MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids); +MODULE_DEVICE_TABLE(pci, i2c_designware_pci_ids); static struct pci_driver dw_i2c_driver = { .name = DRIVER_NAME, - .id_table = i2_designware_pci_ids, .probe = i2c_dw_pci_probe, .remove = i2c_dw_pci_remove, .driver = { - .pm = &i2c_dw_pm_ops, + .pm = pm_ptr(&i2c_dw_dev_pm_ops), }, + .id_table = i2c_designware_pci_ids, }; module_pci_driver(dw_i2c_driver); MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(I2C_DW); +MODULE_IMPORT_NS(I2C_DW_COMMON); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index df3dc1e8093e..2d0c7348e491 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -8,7 +8,6 @@ * Copyright (C) 2007 MontaVista Software Inc. * Copyright (C) 2009 Provigent Ltd. */ -#include <linux/acpi.h> #include <linux/clk-provider.h> #include <linux/clk.h> #include <linux/delay.h> @@ -21,7 +20,6 @@ #include <linux/kernel.h> #include <linux/mfd/syscon.h> #include <linux/module.h> -#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_runtime.h> @@ -30,7 +28,6 @@ #include <linux/reset.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/suspend.h> #include <linux/units.h> #include "i2c-designware-core.h" @@ -40,29 +37,6 @@ static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) return clk_get_rate(dev->clk) / KILO; } -#ifdef CONFIG_ACPI -static const struct acpi_device_id dw_i2c_acpi_match[] = { - { "INT33C2", 0 }, - { "INT33C3", 0 }, - { "INT3432", 0 }, - { "INT3433", 0 }, - { "INTC10EF", 0 }, - { "80860F41", ACCESS_NO_IRQ_SUSPEND }, - { "808622C1", ACCESS_NO_IRQ_SUSPEND }, - { "AMD0010", ACCESS_INTR_MASK }, - { "AMDI0010", ACCESS_INTR_MASK }, - { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE }, - { "AMDI0510", 0 }, - { "APMC0D0F", 0 }, - { "HISI02A1", 0 }, - { "HISI02A2", 0 }, - { "HISI02A3", 0 }, - { "HYGO0010", ACCESS_INTR_MASK }, - { } -}; -MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); -#endif - #ifdef CONFIG_OF #define BT1_I2C_CTL 0x100 #define BT1_I2C_CTL_ADDR_MASK GENMASK(7, 0) @@ -120,53 +94,11 @@ static int bt1_i2c_request_regs(struct dw_i2c_dev *dev) dev->map = devm_regmap_init(dev->dev, NULL, dev, &bt1_i2c_cfg); return PTR_ERR_OR_ZERO(dev->map); } - -#define MSCC_ICPU_CFG_TWI_DELAY 0x0 -#define MSCC_ICPU_CFG_TWI_DELAY_ENABLE BIT(0) -#define MSCC_ICPU_CFG_TWI_SPIKE_FILTER 0x4 - -static int mscc_twi_set_sda_hold_time(struct dw_i2c_dev *dev) -{ - writel((dev->sda_hold_time << 1) | MSCC_ICPU_CFG_TWI_DELAY_ENABLE, - dev->ext + MSCC_ICPU_CFG_TWI_DELAY); - - return 0; -} - -static int dw_i2c_of_configure(struct platform_device *pdev) -{ - struct dw_i2c_dev *dev = platform_get_drvdata(pdev); - - switch (dev->flags & MODEL_MASK) { - case MODEL_MSCC_OCELOT: - dev->ext = devm_platform_ioremap_resource(pdev, 1); - if (!IS_ERR(dev->ext)) - dev->set_sda_hold_time = mscc_twi_set_sda_hold_time; - break; - default: - break; - } - - return 0; -} - -static const struct of_device_id dw_i2c_of_match[] = { - { .compatible = "snps,designware-i2c", }, - { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT }, - { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 }, - {}, -}; -MODULE_DEVICE_TABLE(of, dw_i2c_of_match); #else static int bt1_i2c_request_regs(struct dw_i2c_dev *dev) { return -ENODEV; } - -static inline int dw_i2c_of_configure(struct platform_device *pdev) -{ - return -ENODEV; -} #endif static int txgbe_i2c_request_regs(struct dw_i2c_dev *dev) @@ -238,11 +170,9 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) int i = 0; int ret; - ptr = i2c_dw_semaphore_cb_table; - dev->semaphore_idx = -1; - while (ptr->probe) { + for (ptr = i2c_dw_semaphore_cb_table; ptr->probe; ptr++) { ret = ptr->probe(dev); if (ret) { /* @@ -254,7 +184,6 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) return ret; i++; - ptr++; continue; } @@ -278,7 +207,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) { struct i2c_adapter *adap; struct dw_i2c_dev *dev; - struct i2c_timings *t; int irq, ret; irq = platform_get_irq(pdev, 0); @@ -307,18 +235,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) reset_control_deassert(dev->rst); - t = &dev->timings; - i2c_parse_fw_timings(&pdev->dev, t, false); - - i2c_dw_adjust_bus_speed(dev); - - if (pdev->dev.of_node) - dw_i2c_of_configure(pdev); - - if (has_acpi_companion(&pdev->dev)) - i2c_dw_acpi_configure(&pdev->dev); - - ret = i2c_dw_validate_speed(dev); + ret = i2c_dw_fw_parse_and_configure(dev); if (ret) goto exit_reset; @@ -346,6 +263,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) goto exit_reset; if (dev->clk) { + struct i2c_timings *t = &dev->timings; u64 clk_khz; dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz; @@ -360,8 +278,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) adap->owner = THIS_MODULE; adap->class = dmi_check_system(dw_i2c_hwmon_class_dmi) ? I2C_CLASS_HWMON : I2C_CLASS_DEPRECATED; - ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); - adap->dev.of_node = pdev->dev.of_node; adap->nr = -1; if (dev->flags & ACCESS_NO_IRQ_SUSPEND) { @@ -408,7 +324,7 @@ static void dw_i2c_plat_remove(struct platform_device *pdev) i2c_del_adapter(&dev->adapter); - dev->disable(dev); + i2c_dw_disable(dev); pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); @@ -419,66 +335,34 @@ static void dw_i2c_plat_remove(struct platform_device *pdev) reset_control_assert(dev->rst); } -static int dw_i2c_plat_prepare(struct device *dev) -{ - /* - * If the ACPI companion device object is present for this device, it - * may be accessed during suspend and resume of other devices via I2C - * operation regions, so tell the PM core and middle layers to avoid - * skipping system suspend/resume callbacks for it in that case. - */ - return !has_acpi_companion(dev); -} - -static int dw_i2c_plat_runtime_suspend(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - - if (i_dev->shared_with_punit) - return 0; - - i_dev->disable(i_dev); - i2c_dw_prepare_clk(i_dev, false); - - return 0; -} - -static int dw_i2c_plat_suspend(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - - i2c_mark_adapter_suspended(&i_dev->adapter); - - return dw_i2c_plat_runtime_suspend(dev); -} - -static int dw_i2c_plat_runtime_resume(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - - if (!i_dev->shared_with_punit) - i2c_dw_prepare_clk(i_dev, true); - - i_dev->init(i_dev); - - return 0; -} - -static int dw_i2c_plat_resume(struct device *dev) -{ - struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - - dw_i2c_plat_runtime_resume(dev); - i2c_mark_adapter_resumed(&i_dev->adapter); - - return 0; -} +static const struct of_device_id dw_i2c_of_match[] = { + { .compatible = "snps,designware-i2c", }, + { .compatible = "mscc,ocelot-i2c", .data = (void *)MODEL_MSCC_OCELOT }, + { .compatible = "baikal,bt1-sys-i2c", .data = (void *)MODEL_BAIKAL_BT1 }, + {} +}; +MODULE_DEVICE_TABLE(of, dw_i2c_of_match); -static const struct dev_pm_ops dw_i2c_dev_pm_ops = { - .prepare = pm_sleep_ptr(dw_i2c_plat_prepare), - LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) - RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL) +static const struct acpi_device_id dw_i2c_acpi_match[] = { + { "80860F41", ACCESS_NO_IRQ_SUSPEND }, + { "808622C1", ACCESS_NO_IRQ_SUSPEND }, + { "AMD0010", ACCESS_INTR_MASK }, + { "AMDI0010", ACCESS_INTR_MASK }, + { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE }, + { "AMDI0510", 0 }, + { "APMC0D0F", 0 }, + { "HISI02A1", 0 }, + { "HISI02A2", 0 }, + { "HISI02A3", 0 }, + { "HYGO0010", ACCESS_INTR_MASK }, + { "INT33C2", 0 }, + { "INT33C3", 0 }, + { "INT3432", 0 }, + { "INT3433", 0 }, + { "INTC10EF", 0 }, + {} }; +MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); static const struct platform_device_id dw_i2c_platform_ids[] = { { "i2c_designware" }, @@ -491,9 +375,9 @@ static struct platform_driver dw_i2c_driver = { .remove_new = dw_i2c_plat_remove, .driver = { .name = "i2c_designware", - .of_match_table = of_match_ptr(dw_i2c_of_match), - .acpi_match_table = ACPI_PTR(dw_i2c_acpi_match), - .pm = pm_ptr(&dw_i2c_dev_pm_ops), + .of_match_table = dw_i2c_of_match, + .acpi_match_table = dw_i2c_acpi_match, + .pm = pm_ptr(&i2c_dw_dev_pm_ops), }, .id_table = dw_i2c_platform_ids, }; @@ -513,3 +397,5 @@ module_exit(dw_i2c_exit_driver); MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(I2C_DW); +MODULE_IMPORT_NS(I2C_DW_COMMON); diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c index 78e2c47e3d7d..7035296aa24c 100644 --- a/drivers/i2c/busses/i2c-designware-slave.c +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -16,6 +16,8 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> +#define DEFAULT_SYMBOL_NAMESPACE I2C_DW + #include "i2c-designware-core.h" static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev) @@ -88,7 +90,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave) struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter); regmap_write(dev->map, DW_IC_INTR_MASK, 0); - dev->disable(dev); + i2c_dw_disable(dev); synchronize_irq(dev->irq); dev->slave = NULL; pm_runtime_put(dev->dev); @@ -235,7 +237,6 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev) int ret; dev->init = i2c_dw_init_slave; - dev->disable = i2c_dw_disable; ret = i2c_dw_init_regmap(dev); if (ret) @@ -279,3 +280,4 @@ EXPORT_SYMBOL_GPL(i2c_dw_probe_slave); MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>"); MODULE_DESCRIPTION("Synopsys DesignWare I2C bus slave adapter"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(I2C_DW_COMMON); diff --git a/drivers/i2c/busses/i2c-digicolor.c b/drivers/i2c/busses/i2c-digicolor.c index 3e6b80e59b90..3dc5a46698fc 100644 --- a/drivers/i2c/busses/i2c-digicolor.c +++ b/drivers/i2c/busses/i2c-digicolor.c @@ -357,7 +357,7 @@ static void dc_i2c_remove(struct platform_device *pdev) static const struct of_device_id dc_i2c_match[] = { { .compatible = "cnxt,cx92755-i2c" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, dc_i2c_match); diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index 557409410445..d08be3f3cede 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -67,7 +67,6 @@ struct em_i2c_device { void __iomem *base; struct i2c_adapter adap; struct completion msg_done; - struct clk *sclk; struct i2c_client *slave; int irq; }; @@ -361,6 +360,7 @@ static const struct i2c_algorithm em_i2c_algo = { static int em_i2c_probe(struct platform_device *pdev) { struct em_i2c_device *priv; + struct clk *sclk; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); @@ -373,13 +373,9 @@ static int em_i2c_probe(struct platform_device *pdev) strscpy(priv->adap.name, "EMEV2 I2C", sizeof(priv->adap.name)); - priv->sclk = devm_clk_get(&pdev->dev, "sclk"); - if (IS_ERR(priv->sclk)) - return PTR_ERR(priv->sclk); - - ret = clk_prepare_enable(priv->sclk); - if (ret) - return ret; + sclk = devm_clk_get_enabled(&pdev->dev, "sclk"); + if (IS_ERR(sclk)) + return PTR_ERR(sclk); priv->adap.timeout = msecs_to_jiffies(100); priv->adap.retries = 5; @@ -397,26 +393,22 @@ static int em_i2c_probe(struct platform_device *pdev) ret = platform_get_irq(pdev, 0); if (ret < 0) - goto err_clk; + return ret; priv->irq = ret; + ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0, "em_i2c", priv); if (ret) - goto err_clk; + return ret; ret = i2c_add_adapter(&priv->adap); - if (ret) - goto err_clk; + return ret; dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, priv->irq); return 0; - -err_clk: - clk_disable_unprepare(priv->sclk); - return ret; } static void em_i2c_remove(struct platform_device *dev) @@ -424,7 +416,6 @@ static void em_i2c_remove(struct platform_device *dev) struct em_i2c_device *priv = platform_get_drvdata(dev); i2c_del_adapter(&priv->adap); - clk_disable_unprepare(priv->sclk); } static const struct of_device_id em_i2c_ids[] = { diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 328c0dab6b14..299fe9d3afab 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1763,8 +1763,15 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) i801_add_tco(priv); + /* + * adapter.name is used by platform code to find the main I801 adapter + * to instantiante i2c_clients, do not change. + */ snprintf(priv->adapter.name, sizeof(priv->adapter.name), - "SMBus I801 adapter at %04lx", priv->smba); + "SMBus %s adapter at %04lx", + (priv->features & FEATURE_IDF) ? "I801 IDF" : "I801", + priv->smba); + err = i2c_add_adapter(&priv->adapter); if (err) { platform_device_unregister(priv->tco_pdev); diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 0197786892a2..976d43f73f38 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -559,7 +559,7 @@ static const struct i2c_algorithm lpi2c_imx_algo = { static const struct of_device_id lpi2c_imx_of_match[] = { { .compatible = "fsl,imx7ulp-lpi2c" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 3842e527116b..98539313cbc9 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -687,7 +687,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic) i2c_imx_bus_busy(i2c_imx, 0, atomic); /* Disable I2C controller */ - temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, + temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); } @@ -1549,7 +1549,7 @@ static void i2c_imx_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused i2c_imx_runtime_suspend(struct device *dev) +static int i2c_imx_runtime_suspend(struct device *dev) { struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); @@ -1558,7 +1558,7 @@ static int __maybe_unused i2c_imx_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused i2c_imx_runtime_resume(struct device *dev) +static int i2c_imx_runtime_resume(struct device *dev) { struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); int ret; @@ -1571,8 +1571,7 @@ static int __maybe_unused i2c_imx_runtime_resume(struct device *dev) } static const struct dev_pm_ops i2c_imx_pm_ops = { - SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend, - i2c_imx_runtime_resume, NULL) + RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL) }; static struct platform_driver i2c_imx_driver = { @@ -1580,7 +1579,7 @@ static struct platform_driver i2c_imx_driver = { .remove_new = i2c_imx_remove, .driver = { .name = DRIVER_NAME, - .pm = &i2c_imx_pm_ops, + .pm = pm_ptr(&i2c_imx_pm_ops), .of_match_table = i2c_imx_dt_ids, .acpi_match_table = i2c_imx_acpi_ids, }, diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 655b5d851c48..c93c02aa6ac8 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -382,6 +382,15 @@ static int ismt_process_desc(const struct ismt_desc *desc, } /** + * ismt_kill_transaction() - kill current transaction + * @priv: iSMT private data + */ +static void ismt_kill_transaction(struct ismt_priv *priv) +{ + writel(ISMT_GCTRL_KILL, priv->smba + ISMT_GR_GCTRL); +} + +/** * ismt_access() - process an SMBus command * @adap: the i2c host adapter * @addr: address of the i2c/SMBus target @@ -623,6 +632,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr, dma_unmap_single(dev, dma_addr, dma_size, dma_direction); if (unlikely(!time_left)) { + ismt_kill_transaction(priv); ret = -ETIMEDOUT; goto out; } diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 4aafdfab6305..92cc5b091137 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -792,26 +792,22 @@ static int jz4780_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2c); - i2c->clk = devm_clk_get(&pdev->dev, NULL); + i2c->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(i2c->clk)) return PTR_ERR(i2c->clk); - ret = clk_prepare_enable(i2c->clk); - if (ret) - return ret; - ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clk_freq); if (ret) { dev_err(&pdev->dev, "clock-frequency not specified in DT\n"); - goto err; + return ret; } i2c->speed = clk_freq / 1000; if (i2c->speed == 0) { ret = -EINVAL; dev_err(&pdev->dev, "clock-frequency minimum is 1000\n"); - goto err; + return ret; } jz4780_i2c_set_speed(i2c); @@ -827,29 +823,25 @@ static int jz4780_i2c_probe(struct platform_device *pdev) ret = platform_get_irq(pdev, 0); if (ret < 0) - goto err; + return ret; i2c->irq = ret; + ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0, dev_name(&pdev->dev), i2c); if (ret) - goto err; + return ret; ret = i2c_add_adapter(&i2c->adap); if (ret < 0) - goto err; + return ret; return 0; - -err: - clk_disable_unprepare(i2c->clk); - return ret; } static void jz4780_i2c_remove(struct platform_device *pdev) { struct jz4780_i2c *i2c = platform_get_drvdata(pdev); - clk_disable_unprepare(i2c->clk); i2c_del_adapter(&i2c->adap); } diff --git a/drivers/i2c/busses/i2c-keba.c b/drivers/i2c/busses/i2c-keba.c new file mode 100644 index 000000000000..759732a07ef0 --- /dev/null +++ b/drivers/i2c/busses/i2c-keba.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) KEBA Industrial Automation Gmbh 2024 + * + * Driver for KEBA I2C controller FPGA IP core + */ + +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/misc/keba.h> + +#define KI2C "i2c-keba" + +#define KI2C_CAPABILITY_REG 0x02 +#define KI2C_CAPABILITY_CRYPTO 0x01 +#define KI2C_CAPABILITY_DC 0x02 + +#define KI2C_CONTROL_REG 0x04 +#define KI2C_CONTROL_MEN 0x01 +#define KI2C_CONTROL_MSTA 0x02 +#define KI2C_CONTROL_RSTA 0x04 +#define KI2C_CONTROL_MTX 0x08 +#define KI2C_CONTROL_TXAK 0x10 +#define KI2C_CONTROL_DISABLE 0x00 + +#define KI2C_CONTROL_DC_REG 0x05 +#define KI2C_CONTROL_DC_SDA 0x01 +#define KI2C_CONTROL_DC_SCL 0x02 + +#define KI2C_STATUS_REG 0x08 +#define KI2C_STATUS_IN_USE 0x01 +#define KI2C_STATUS_ACK_CYC 0x02 +#define KI2C_STATUS_RXAK 0x04 +#define KI2C_STATUS_MCF 0x08 + +#define KI2C_STATUS_DC_REG 0x09 +#define KI2C_STATUS_DC_SDA 0x01 +#define KI2C_STATUS_DC_SCL 0x02 + +#define KI2C_DATA_REG 0x0c + +#define KI2C_INUSE_SLEEP_US (2 * USEC_PER_MSEC) +#define KI2C_INUSE_TIMEOUT_US (10 * USEC_PER_SEC) + +#define KI2C_POLL_DELAY_US 5 + +struct ki2c { + struct keba_i2c_auxdev *auxdev; + void __iomem *base; + struct i2c_adapter adapter; + + struct i2c_client **client; + int client_size; +}; + +static int ki2c_inuse_lock(struct ki2c *ki2c) +{ + u8 sts; + int ret; + + /* + * The I2C controller has an IN_USE bit for locking access to the + * controller. This enables the use of I2C controller by other none + * Linux processors. + * + * If the I2C controller is free, then the first read returns + * IN_USE == 0. After that the I2C controller is locked and further + * reads of IN_USE return 1. + * + * The I2C controller is unlocked by writing 1 into IN_USE. + * + * The IN_USE bit acts as a hardware semaphore for the I2C controller. + * Poll for semaphore, but sleep while polling to free the CPU. + */ + ret = readb_poll_timeout(ki2c->base + KI2C_STATUS_REG, + sts, (sts & KI2C_STATUS_IN_USE) == 0, + KI2C_INUSE_SLEEP_US, KI2C_INUSE_TIMEOUT_US); + if (ret) + dev_err(&ki2c->auxdev->auxdev.dev, "%s err!\n", __func__); + + return ret; +} + +static void ki2c_inuse_unlock(struct ki2c *ki2c) +{ + /* unlock the controller by writing 1 into IN_USE */ + iowrite8(KI2C_STATUS_IN_USE, ki2c->base + KI2C_STATUS_REG); +} + +static int ki2c_wait_for_bit(void __iomem *addr, u8 mask, unsigned long timeout) +{ + u8 val; + + return readb_poll_timeout(addr, val, (val & mask), KI2C_POLL_DELAY_US, + jiffies_to_usecs(timeout)); +} + +static int ki2c_wait_for_mcf(struct ki2c *ki2c) +{ + return ki2c_wait_for_bit(ki2c->base + KI2C_STATUS_REG, KI2C_STATUS_MCF, + ki2c->adapter.timeout); +} + +static int ki2c_wait_for_data(struct ki2c *ki2c) +{ + int ret; + + ret = ki2c_wait_for_mcf(ki2c); + if (ret < 0) + return ret; + + return ki2c_wait_for_bit(ki2c->base + KI2C_STATUS_REG, + KI2C_STATUS_ACK_CYC, + ki2c->adapter.timeout); +} + +static int ki2c_wait_for_data_ack(struct ki2c *ki2c) +{ + unsigned int reg; + int ret; + + ret = ki2c_wait_for_data(ki2c); + if (ret < 0) + return ret; + + /* RXAK == 0 means ACK reveived */ + reg = ioread8(ki2c->base + KI2C_STATUS_REG); + if (reg & KI2C_STATUS_RXAK) + return -EIO; + + return 0; +} + +static int ki2c_has_capability(struct ki2c *ki2c, unsigned int cap) +{ + unsigned int reg = ioread8(ki2c->base + KI2C_CAPABILITY_REG); + + return (reg & cap) != 0; +} + +static int ki2c_get_scl(struct ki2c *ki2c) +{ + unsigned int reg = ioread8(ki2c->base + KI2C_STATUS_DC_REG); + + /* capability KI2C_CAPABILITY_DC required */ + return (reg & KI2C_STATUS_DC_SCL) != 0; +} + +static int ki2c_get_sda(struct ki2c *ki2c) +{ + unsigned int reg = ioread8(ki2c->base + KI2C_STATUS_DC_REG); + + /* capability KI2C_CAPABILITY_DC required */ + return (reg & KI2C_STATUS_DC_SDA) != 0; +} + +static void ki2c_set_scl(struct ki2c *ki2c, int val) +{ + u8 control_dc; + + /* capability KI2C_CAPABILITY_DC and KI2C_CONTROL_MEN = 0 reqired */ + control_dc = ioread8(ki2c->base + KI2C_CONTROL_DC_REG); + if (val) + control_dc |= KI2C_CONTROL_DC_SCL; + else + control_dc &= ~KI2C_CONTROL_DC_SCL; + iowrite8(control_dc, ki2c->base + KI2C_CONTROL_DC_REG); +} + +/* + * Resetting bus bitwise is done by checking SDA and applying clock cycles as + * long as SDA is low. 9 clock cycles are applied at most. + * + * Clock cycles are generated and udelay() determines the duration of clock + * cycles. Generated clock rate is 100 KHz and so duration of both clock levels + * is: delay in ns = (10^6 / 100) / 2 + */ +#define KI2C_RECOVERY_CLK_CNT (9 * 2) +#define KI2C_RECOVERY_UDELAY 5 +static int ki2c_reset_bus_bitwise(struct ki2c *ki2c) +{ + int val = 1; + int ret = 0; + int i; + + /* disable I2C controller (MEN = 0) to get direct access to SCL/SDA */ + iowrite8(0, ki2c->base + KI2C_CONTROL_REG); + + /* generate clock cycles */ + ki2c_set_scl(ki2c, val); + udelay(KI2C_RECOVERY_UDELAY); + for (i = 0; i < KI2C_RECOVERY_CLK_CNT; i++) { + if (val) { + /* SCL shouldn't be low here */ + if (!ki2c_get_scl(ki2c)) { + dev_err(&ki2c->auxdev->auxdev.dev, + "SCL is stuck low!\n"); + ret = -EBUSY; + break; + } + + /* break if SDA is high */ + if (ki2c_get_sda(ki2c)) + break; + } + + val = !val; + ki2c_set_scl(ki2c, val); + udelay(KI2C_RECOVERY_UDELAY); + } + + if (!ki2c_get_sda(ki2c)) { + dev_err(&ki2c->auxdev->auxdev.dev, "SDA is still low!\n"); + ret = -EBUSY; + } + + /* reenable controller */ + iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG); + + return ret; +} + +/* + * Resetting bus bytewise is done by writing start bit, 9 data bits and stop + * bit. + * + * This is not 100% safe. If target is an EEPROM and a write access was + * interrupted during the ACK cycle, this approach might not be able to recover + * the bus. The reason is, that after the 9 clock cycles the EEPROM will be in + * ACK cycle again and will hold SDA low like it did before the start of the + * routine. Furthermore the EEPROM might get written one additional byte with + * 0xff into it. Thus, use bitwise approach whenever possible, especially when + * EEPROMs are on the bus. + */ +static int ki2c_reset_bus_bytewise(struct ki2c *ki2c) +{ + int ret; + + /* hold data line high for 9 clock cycles */ + iowrite8(0xFF, ki2c->base + KI2C_DATA_REG); + + /* create start condition */ + iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_MSTA | KI2C_CONTROL_TXAK, + ki2c->base + KI2C_CONTROL_REG); + ret = ki2c_wait_for_mcf(ki2c); + if (ret < 0) { + dev_err(&ki2c->auxdev->auxdev.dev, "Start condition failed\n"); + + return ret; + } + + /* create stop condition */ + iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_TXAK, + ki2c->base + KI2C_CONTROL_REG); + ret = ki2c_wait_for_mcf(ki2c); + if (ret < 0) + dev_err(&ki2c->auxdev->auxdev.dev, "Stop condition failed\n"); + + return ret; +} + +static int ki2c_reset_bus(struct ki2c *ki2c) +{ + int ret; + + ret = ki2c_inuse_lock(ki2c); + if (ret < 0) + return ret; + + /* + * If the I2C controller is capable of direct control of SCL/SDA, then a + * bitwise reset is used. Otherwise fall back to bytewise reset. + */ + if (ki2c_has_capability(ki2c, KI2C_CAPABILITY_DC)) + ret = ki2c_reset_bus_bitwise(ki2c); + else + ret = ki2c_reset_bus_bytewise(ki2c); + + ki2c_inuse_unlock(ki2c); + + return ret; +} + +static void ki2c_write_target_addr(struct ki2c *ki2c, struct i2c_msg *m) +{ + u8 addr; + + addr = m->addr << 1; + /* Bit 0 signals RD/WR */ + if (m->flags & I2C_M_RD) + addr |= 0x01; + + iowrite8(addr, ki2c->base + KI2C_DATA_REG); +} + +static int ki2c_start_addr(struct ki2c *ki2c, struct i2c_msg *m) +{ + int ret; + + /* + * Store target address byte in the controller. This has to be done + * before sending START condition. + */ + ki2c_write_target_addr(ki2c, m); + + /* enable controller for TX */ + iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX, + ki2c->base + KI2C_CONTROL_REG); + + /* send START condition and target address byte */ + iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MTX | KI2C_CONTROL_MSTA, + ki2c->base + KI2C_CONTROL_REG); + + ret = ki2c_wait_for_data_ack(ki2c); + if (ret < 0) + /* + * For EEPROMs this is normal behavior during internal write + * operation. + */ + dev_dbg(&ki2c->auxdev->auxdev.dev, + "%s wait for ACK err at 0x%02x!\n", __func__, m->addr); + + return ret; +} + +static int ki2c_repstart_addr(struct ki2c *ki2c, struct i2c_msg *m) +{ + int ret; + + /* repeated start and write is not supported */ + if ((m->flags & I2C_M_RD) == 0) { + dev_err(&ki2c->auxdev->auxdev.dev, + "Repeated start not supported for writes\n"); + return -EINVAL; + } + + /* send repeated start */ + iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_RSTA, + ki2c->base + KI2C_CONTROL_REG); + + ret = ki2c_wait_for_mcf(ki2c); + if (ret < 0) { + dev_err(&ki2c->auxdev->auxdev.dev, + "%s wait for MCF err at 0x%02x!\n", __func__, m->addr); + return ret; + } + + /* write target-address byte */ + ki2c_write_target_addr(ki2c, m); + + ret = ki2c_wait_for_data_ack(ki2c); + if (ret < 0) + dev_err(&ki2c->auxdev->auxdev.dev, + "%s wait for ACK err at 0x%02x!\n", __func__, m->addr); + + return ret; +} + +static void ki2c_stop(struct ki2c *ki2c) +{ + iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG); + ki2c_wait_for_mcf(ki2c); +} + +static int ki2c_write(struct ki2c *ki2c, const u8 *data, int len) +{ + int ret; + int i; + + for (i = 0; i < len; i++) { + /* write data byte */ + iowrite8(data[i], ki2c->base + KI2C_DATA_REG); + + ret = ki2c_wait_for_data_ack(ki2c); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ki2c_read(struct ki2c *ki2c, u8 *data, int len) +{ + u8 control; + int ret; + int i; + + if (len == 0) + return 0; /* nothing to do */ + + control = KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA; + + /* if just one byte => send tx-nack after transfer */ + if (len == 1) + control |= KI2C_CONTROL_TXAK; + + iowrite8(control, ki2c->base + KI2C_CONTROL_REG); + + /* dummy read to start transfer on bus */ + ioread8(ki2c->base + KI2C_DATA_REG); + + for (i = 0; i < len; i++) { + ret = ki2c_wait_for_data(ki2c); + if (ret < 0) + return ret; + + if (i == len - 2) + /* send tx-nack after transfer of last byte */ + iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_TXAK, + ki2c->base + KI2C_CONTROL_REG); + else if (i == len - 1) + /* + * switch to TX on last byte, so that reading DATA + * register does not trigger another read transfer + */ + iowrite8(KI2C_CONTROL_MEN | KI2C_CONTROL_MSTA | KI2C_CONTROL_MTX, + ki2c->base + KI2C_CONTROL_REG); + + /* read byte and start next transfer (if not last byte) */ + data[i] = ioread8(ki2c->base + KI2C_DATA_REG); + } + + return len; +} + +static int ki2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct ki2c *ki2c = i2c_get_adapdata(adap); + int ret; + int i; + + ret = ki2c_inuse_lock(ki2c); + if (ret < 0) + return ret; + + for (i = 0; i < num; i++) { + struct i2c_msg *m = &msgs[i]; + + if (i == 0) + ret = ki2c_start_addr(ki2c, m); + else + ret = ki2c_repstart_addr(ki2c, m); + if (ret < 0) + break; + + if (m->flags & I2C_M_RD) + ret = ki2c_read(ki2c, m->buf, m->len); + else + ret = ki2c_write(ki2c, m->buf, m->len); + if (ret < 0) + break; + } + + ki2c_stop(ki2c); + + ki2c_inuse_unlock(ki2c); + + return ret < 0 ? ret : num; +} + +static void ki2c_unregister_devices(struct ki2c *ki2c) +{ + int i; + + for (i = 0; i < ki2c->client_size; i++) { + struct i2c_client *client = ki2c->client[i]; + + if (client) + i2c_unregister_device(client); + } +} + +static int ki2c_register_devices(struct ki2c *ki2c) +{ + struct i2c_board_info *info = ki2c->auxdev->info; + int i; + + /* register all known I2C devices */ + for (i = 0; i < ki2c->client_size; i++) { + struct i2c_client *client; + unsigned short const addr_list[2] = { info[i].addr, + I2C_CLIENT_END }; + + client = i2c_new_scanned_device(&ki2c->adapter, &info[i], + addr_list, NULL); + if (!IS_ERR(client)) { + ki2c->client[i] = client; + } else if (PTR_ERR(client) != -ENODEV) { + ki2c->client_size = i; + ki2c_unregister_devices(ki2c); + + return PTR_ERR(client); + } + } + + return 0; +} + +static u32 ki2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm ki2c_algo = { + .master_xfer = ki2c_xfer, + .functionality = ki2c_func, +}; + +static int ki2c_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &auxdev->dev; + struct i2c_adapter *adap; + struct ki2c *ki2c; + int ret; + + ki2c = devm_kzalloc(dev, sizeof(*ki2c), GFP_KERNEL); + if (!ki2c) + return -ENOMEM; + ki2c->auxdev = container_of(auxdev, struct keba_i2c_auxdev, auxdev); + ki2c->client = devm_kcalloc(dev, ki2c->auxdev->info_size, + sizeof(*ki2c->client), GFP_KERNEL); + if (!ki2c->client) + return -ENOMEM; + ki2c->client_size = ki2c->auxdev->info_size; + auxiliary_set_drvdata(auxdev, ki2c); + + ki2c->base = devm_ioremap_resource(dev, &ki2c->auxdev->io); + if (IS_ERR(ki2c->base)) + return PTR_ERR(ki2c->base); + + adap = &ki2c->adapter; + strscpy(adap->name, "KEBA I2C adapter", sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + adap->algo = &ki2c_algo; + adap->dev.parent = dev; + + i2c_set_adapdata(adap, ki2c); + + /* enable controller */ + iowrite8(KI2C_CONTROL_MEN, ki2c->base + KI2C_CONTROL_REG); + + /* reset bus before probing I2C devices */ + ret = ki2c_reset_bus(ki2c); + if (ret) + goto out; + + ret = devm_i2c_add_adapter(dev, adap); + if (ret) { + dev_err(dev, "Failed to add adapter (%d)!\n", ret); + goto out; + } + + ret = ki2c_register_devices(ki2c); + if (ret) { + dev_err(dev, "Failed to register devices (%d)!\n", ret); + goto out; + } + + return 0; + +out: + iowrite8(KI2C_CONTROL_DISABLE, ki2c->base + KI2C_CONTROL_REG); + return ret; +} + +static void ki2c_remove(struct auxiliary_device *auxdev) +{ + struct ki2c *ki2c = auxiliary_get_drvdata(auxdev); + + ki2c_unregister_devices(ki2c); + + /* disable controller */ + iowrite8(KI2C_CONTROL_DISABLE, ki2c->base + KI2C_CONTROL_REG); + + auxiliary_set_drvdata(auxdev, NULL); +} + +static const struct auxiliary_device_id ki2c_devtype_aux[] = { + { .name = "keba.i2c" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, ki2c_devtype_aux); + +static struct auxiliary_driver ki2c_driver_aux = { + .name = KI2C, + .id_table = ki2c_devtype_aux, + .probe = ki2c_probe, + .remove = ki2c_remove, +}; +module_auxiliary_driver(ki2c_driver_aux); + +MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>"); +MODULE_DESCRIPTION("KEBA I2C bus controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ljca.c b/drivers/i2c/busses/i2c-ljca.c index 0b70621cf9d3..1dc516ef0fdd 100644 --- a/drivers/i2c/busses/i2c-ljca.c +++ b/drivers/i2c/busses/i2c-ljca.c @@ -107,7 +107,7 @@ static int ljca_i2c_start(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, return 0; } -static void ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c, u8 target_addr) +static void ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c) { struct ljca_i2c_rw_packet *w_packet = (struct ljca_i2c_rw_packet *)ljca_i2c->obuf; @@ -178,7 +178,7 @@ static int ljca_i2c_read(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, u8 *data if (!ret) ret = ljca_i2c_pure_read(ljca_i2c, data, len); - ljca_i2c_stop(ljca_i2c, target_addr); + ljca_i2c_stop(ljca_i2c); return ret; } @@ -222,7 +222,7 @@ static int ljca_i2c_write(struct ljca_i2c_dev *ljca_i2c, u8 target_addr, if (!ret) ret = ljca_i2c_pure_write(ljca_i2c, data, len); - ljca_i2c_stop(ljca_i2c, target_addr); + ljca_i2c_stop(ljca_i2c); return ret; } diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 41d6c8ed163a..236d6b8ba867 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -88,7 +88,6 @@ struct mpc_i2c { int irq; u32 real_clk; u8 fdr, dfsrr; - struct clk *clk_per; u32 cntl_bits; enum mpc_i2c_action action; struct i2c_msg *msgs; @@ -779,7 +778,6 @@ static int fsl_i2c_probe(struct platform_device *op) struct clk *clk; int result; u32 clock; - int err; i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) @@ -809,18 +807,12 @@ static int fsl_i2c_probe(struct platform_device *op) * enable clock for the I2C peripheral (non fatal), * keep a reference upon successful allocation */ - clk = devm_clk_get_optional(&op->dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - err = clk_prepare_enable(clk); - if (err) { + clk = devm_clk_get_optional_enabled(&op->dev, NULL); + if (IS_ERR(clk)) { dev_err(&op->dev, "failed to enable clock\n"); - return err; + return PTR_ERR(clk); } - i2c->clk_per = clk; - if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) { clock = MPC_I2C_CLOCK_PRESERVE; } else { @@ -876,14 +868,9 @@ static int fsl_i2c_probe(struct platform_device *op) result = i2c_add_numbered_adapter(&i2c->adap); if (result) - goto fail_add; + return result; return 0; - - fail_add: - clk_disable_unprepare(i2c->clk_per); - - return result; }; static void fsl_i2c_remove(struct platform_device *op) @@ -891,8 +878,6 @@ static void fsl_i2c_remove(struct platform_device *op) struct mpc_i2c *i2c = platform_get_drvdata(op); i2c_del_adapter(&i2c->adap); - - clk_disable_unprepare(i2c->clk_per); }; static int __maybe_unused mpc_i2c_suspend(struct device *dev) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index a8b5719c3372..e0ba653dec2d 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -1306,12 +1306,9 @@ err_exit: static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id) { struct mtk_i2c *i2c = dev_id; - u16 restart_flag = 0; + u16 restart_flag = i2c->auto_restart ? I2C_RS_TRANSFER : 0; u16 intr_stat; - if (i2c->auto_restart) - restart_flag = I2C_RS_TRANSFER; - intr_stat = mtk_i2c_readw(i2c, OFFSET_INTR_STAT); mtk_i2c_writew(i2c, intr_stat, OFFSET_INTR_STAT); diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 2fe68615942e..bbcb4d6668ce 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -136,11 +136,13 @@ enum i2c_addr { * Since the addr regs are sprinkled all over the address space, * use this array to get the address or each register. */ -#define I2C_NUM_OWN_ADDR 2 +#define I2C_NUM_OWN_ADDR 10 #define I2C_NUM_OWN_ADDR_SUPPORTED 2 static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = { - NPCM_I2CADDR1, NPCM_I2CADDR2, + NPCM_I2CADDR1, NPCM_I2CADDR2, NPCM_I2CADDR3, NPCM_I2CADDR4, + NPCM_I2CADDR5, NPCM_I2CADDR6, NPCM_I2CADDR7, NPCM_I2CADDR8, + NPCM_I2CADDR9, NPCM_I2CADDR10, }; #endif diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 35a3f0a64986..1d9ad25c89ae 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1261,7 +1261,7 @@ static const struct of_device_id omap_i2c_of_match[] = { .compatible = "ti,omap2420-i2c", .data = &omap2420_pdata, }, - { }, + { } }; MODULE_DEVICE_TABLE(of, omap_i2c_of_match); #endif diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 4e32d57ae0bf..febbd9950d8f 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -146,7 +146,7 @@ static const struct dmi_system_id piix4_dmi_ibm[] = { .ident = "IBM", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), }, }, - { }, + { } }; /* diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index f448505d5468..1dafadda73af 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -721,7 +721,7 @@ static void i2c_pnx_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id i2c_pnx_of_match[] = { { .compatible = "nxp,pnx-i2c" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, i2c_pnx_of_match); #endif diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c index 6b3c6a733368..af2094720a4d 100644 --- a/drivers/i2c/busses/i2c-pxa-pci.c +++ b/drivers/i2c/busses/i2c-pxa-pci.c @@ -135,7 +135,7 @@ err_dev_add: static const struct pci_device_id ce4100_i2c_devices[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)}, - { }, + { } }; static struct pci_driver ce4100_i2c_driver = { diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 031175113dd4..4d76e71cdd4b 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -218,7 +218,7 @@ static const struct platform_device_id i2c_pxa_id_table[] = { { "ce4100-i2c", REGS_CE4100 }, { "pxa910-i2c", REGS_PXA910 }, { "armada-3700-i2c", REGS_A3700 }, - { }, + { } }; MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 4c9050a4d58e..212336f724a6 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -721,7 +721,7 @@ static const struct i2c_algorithm geni_i2c_algo = { static const struct acpi_device_id geni_i2c_acpi_match[] = { { "QCOM0220"}, { "QCOM0411" }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match); #endif @@ -984,21 +984,24 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) return ret; ret = clk_prepare_enable(gi2c->core_clk); - if (ret) { - geni_icc_disable(&gi2c->se); - return ret; - } + if (ret) + goto out_icc_disable; ret = geni_se_resources_on(&gi2c->se); - if (ret) { - clk_disable_unprepare(gi2c->core_clk); - geni_icc_disable(&gi2c->se); - return ret; - } + if (ret) + goto out_clk_disable; enable_irq(gi2c->irq); gi2c->suspended = 0; + return 0; + +out_clk_disable: + clk_disable_unprepare(gi2c->core_clk); +out_icc_disable: + geni_icc_disable(&gi2c->se); + + return ret; } static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 4a2c745751a2..d480162a4d39 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1648,7 +1648,7 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup) static const struct acpi_device_id qup_i2c_acpi_match[] = { { "QCOM8010"}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index da4b07c0ed4c..9267df38c2d0 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -1164,11 +1164,6 @@ static int rcar_i2c_probe(struct platform_device *pdev) rcar_i2c_init(priv); rcar_i2c_reset_slave(priv); - if (priv->devtype < I2C_RCAR_GEN3) { - irqflags |= IRQF_NO_THREAD; - irqhandler = rcar_i2c_gen2_irq; - } - /* Stay always active when multi-master to keep arbitration working */ if (of_property_read_bool(dev->of_node, "multi-master")) priv->flags |= ID_P_PM_BLOCKED; @@ -1178,8 +1173,11 @@ static int rcar_i2c_probe(struct platform_device *pdev) if (of_property_read_bool(dev->of_node, "smbus")) priv->flags |= ID_P_HOST_NOTIFY; - /* R-Car Gen3+ needs a reset before every transfer */ - if (priv->devtype >= I2C_RCAR_GEN3) { + if (priv->devtype < I2C_RCAR_GEN3) { + irqflags |= IRQF_NO_THREAD; + irqhandler = rcar_i2c_gen2_irq; + } else { + /* R-Car Gen3+ needs a reset before every transfer */ priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(priv->rstc)) { ret = PTR_ERR(priv->rstc); diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index d6f585cdb7e5..c7f3a4c02470 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -63,6 +63,8 @@ #define ICMR3_ACKWP 0x10 #define ICMR3_ACKBT 0x08 +#define ICFER_FMPE 0x80 + #define ICIER_TIE 0x80 #define ICIER_TEIE 0x40 #define ICIER_RIE 0x20 @@ -80,6 +82,7 @@ enum riic_reg_list { RIIC_ICCR2, RIIC_ICMR1, RIIC_ICMR3, + RIIC_ICFER, RIIC_ICSER, RIIC_ICIER, RIIC_ICSR2, @@ -91,7 +94,8 @@ enum riic_reg_list { }; struct riic_of_data { - u8 regs[RIIC_REG_END]; + const u8 *regs; + bool fast_mode_plus; }; struct riic_dev { @@ -105,6 +109,8 @@ struct riic_dev { struct completion msg_done; struct i2c_adapter adapter; struct clk *clk; + struct reset_control *rstc; + struct i2c_timings i2c_t; }; struct riic_irq_desc { @@ -131,11 +137,14 @@ static inline void riic_clear_set_bit(struct riic_dev *riic, u8 clear, u8 set, u static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct riic_dev *riic = i2c_get_adapdata(adap); + struct device *dev = adap->dev.parent; unsigned long time_left; - int i; + int i, ret; u8 start_bit; - pm_runtime_get_sync(adap->dev.parent); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; if (riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) { riic->err = -EBUSY; @@ -168,7 +177,8 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) } out: - pm_runtime_put(adap->dev.parent); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return riic->err ?: num; } @@ -298,21 +308,21 @@ static const struct i2c_algorithm riic_algo = { .functionality = riic_func, }; -static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t) +static int riic_init_hw(struct riic_dev *riic) { - int ret = 0; + int ret; unsigned long rate; int total_ticks, cks, brl, brh; + struct i2c_timings *t = &riic->i2c_t; + struct device *dev = riic->adapter.dev.parent; + bool fast_mode_plus = riic->info->fast_mode_plus; + u32 max_freq = fast_mode_plus ? I2C_MAX_FAST_MODE_PLUS_FREQ + : I2C_MAX_FAST_MODE_FREQ; - pm_runtime_get_sync(riic->adapter.dev.parent); - - if (t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ) { - dev_err(&riic->adapter.dev, - "unsupported bus speed (%dHz). %d max\n", - t->bus_freq_hz, I2C_MAX_FAST_MODE_FREQ); - ret = -EINVAL; - goto out; - } + if (t->bus_freq_hz > max_freq) + return dev_err_probe(&riic->adapter.dev, -EINVAL, + "unsupported bus speed %uHz (%u max)\n", + t->bus_freq_hz, max_freq); rate = clk_get_rate(riic->clk); @@ -349,8 +359,7 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t) if (brl > (0x1F + 3)) { dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n", (unsigned long)t->bus_freq_hz); - ret = -EINVAL; - goto out; + return -EINVAL; } brh = total_ticks - brl; @@ -382,6 +391,10 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t) t->scl_fall_ns / (1000000000 / rate), t->scl_rise_ns / (1000000000 / rate), cks, brl, brh); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + /* Changing the order of accessing IICRST and ICE may break things! */ riic_writeb(riic, ICCR1_IICRST | ICCR1_SOWP, RIIC_ICCR1); riic_clear_set_bit(riic, 0, ICCR1_ICE, RIIC_ICCR1); @@ -393,11 +406,14 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t) riic_writeb(riic, 0, RIIC_ICSER); riic_writeb(riic, ICMR3_ACKWP | ICMR3_RDRFS, RIIC_ICMR3); + if (fast_mode_plus && t->bus_freq_hz > I2C_MAX_FAST_MODE_FREQ) + riic_clear_set_bit(riic, 0, ICFER_FMPE, RIIC_ICFER); + riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1); -out: - pm_runtime_put(riic->adapter.dev.parent); - return ret; + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; } static struct riic_irq_desc riic_irqs[] = { @@ -415,13 +431,12 @@ static void riic_reset_control_assert(void *data) static int riic_i2c_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct riic_dev *riic; struct i2c_adapter *adap; - struct i2c_timings i2c_t; - struct reset_control *rstc; int i, ret; - riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL); + riic = devm_kzalloc(dev, sizeof(*riic), GFP_KERNEL); if (!riic) return -ENOMEM; @@ -429,22 +444,22 @@ static int riic_i2c_probe(struct platform_device *pdev) if (IS_ERR(riic->base)) return PTR_ERR(riic->base); - riic->clk = devm_clk_get(&pdev->dev, NULL); + riic->clk = devm_clk_get(dev, NULL); if (IS_ERR(riic->clk)) { - dev_err(&pdev->dev, "missing controller clock"); + dev_err(dev, "missing controller clock"); return PTR_ERR(riic->clk); } - rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rstc)) - return dev_err_probe(&pdev->dev, PTR_ERR(rstc), + riic->rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(riic->rstc)) + return dev_err_probe(dev, PTR_ERR(riic->rstc), "Error: missing reset ctrl\n"); - ret = reset_control_deassert(rstc); + ret = reset_control_deassert(riic->rstc); if (ret) return ret; - ret = devm_add_action_or_reset(&pdev->dev, riic_reset_control_assert, rstc); + ret = devm_add_action_or_reset(dev, riic_reset_control_assert, riic->rstc); if (ret) return ret; @@ -453,31 +468,34 @@ static int riic_i2c_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = devm_request_irq(&pdev->dev, ret, riic_irqs[i].isr, + ret = devm_request_irq(dev, ret, riic_irqs[i].isr, 0, riic_irqs[i].name, riic); if (ret) { - dev_err(&pdev->dev, "failed to request irq %s\n", riic_irqs[i].name); + dev_err(dev, "failed to request irq %s\n", riic_irqs[i].name); return ret; } } - riic->info = of_device_get_match_data(&pdev->dev); + riic->info = of_device_get_match_data(dev); adap = &riic->adapter; i2c_set_adapdata(adap, riic); strscpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name)); adap->owner = THIS_MODULE; adap->algo = &riic_algo; - adap->dev.parent = &pdev->dev; - adap->dev.of_node = pdev->dev.of_node; + adap->dev.parent = dev; + adap->dev.of_node = dev->of_node; init_completion(&riic->msg_done); - i2c_parse_fw_timings(&pdev->dev, &i2c_t, true); + i2c_parse_fw_timings(dev, &riic->i2c_t, true); - pm_runtime_enable(&pdev->dev); + /* Default 0 to save power. Can be overridden via sysfs for lower latency. */ + pm_runtime_set_autosuspend_delay(dev, 0); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); - ret = riic_init_hw(riic, &i2c_t); + ret = riic_init_hw(riic); if (ret) goto out; @@ -487,60 +505,127 @@ static int riic_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, riic); - dev_info(&pdev->dev, "registered with %dHz bus speed\n", - i2c_t.bus_freq_hz); + dev_info(dev, "registered with %dHz bus speed\n", riic->i2c_t.bus_freq_hz); return 0; out: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); return ret; } static void riic_i2c_remove(struct platform_device *pdev) { struct riic_dev *riic = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; - pm_runtime_get_sync(&pdev->dev); - riic_writeb(riic, 0, RIIC_ICIER); - pm_runtime_put(&pdev->dev); + ret = pm_runtime_resume_and_get(dev); + if (!ret) { + riic_writeb(riic, 0, RIIC_ICIER); + pm_runtime_put(dev); + } i2c_del_adapter(&riic->adapter); - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); } +static const u8 riic_rz_a_regs[RIIC_REG_END] = { + [RIIC_ICCR1] = 0x00, + [RIIC_ICCR2] = 0x04, + [RIIC_ICMR1] = 0x08, + [RIIC_ICMR3] = 0x10, + [RIIC_ICFER] = 0x14, + [RIIC_ICSER] = 0x18, + [RIIC_ICIER] = 0x1c, + [RIIC_ICSR2] = 0x24, + [RIIC_ICBRL] = 0x34, + [RIIC_ICBRH] = 0x38, + [RIIC_ICDRT] = 0x3c, + [RIIC_ICDRR] = 0x40, +}; + static const struct riic_of_data riic_rz_a_info = { - .regs = { - [RIIC_ICCR1] = 0x00, - [RIIC_ICCR2] = 0x04, - [RIIC_ICMR1] = 0x08, - [RIIC_ICMR3] = 0x10, - [RIIC_ICSER] = 0x18, - [RIIC_ICIER] = 0x1c, - [RIIC_ICSR2] = 0x24, - [RIIC_ICBRL] = 0x34, - [RIIC_ICBRH] = 0x38, - [RIIC_ICDRT] = 0x3c, - [RIIC_ICDRR] = 0x40, - }, + .regs = riic_rz_a_regs, + .fast_mode_plus = true, +}; + +static const struct riic_of_data riic_rz_a1h_info = { + .regs = riic_rz_a_regs, +}; + +static const u8 riic_rz_v2h_regs[RIIC_REG_END] = { + [RIIC_ICCR1] = 0x00, + [RIIC_ICCR2] = 0x01, + [RIIC_ICMR1] = 0x02, + [RIIC_ICMR3] = 0x04, + [RIIC_ICFER] = 0x05, + [RIIC_ICSER] = 0x06, + [RIIC_ICIER] = 0x07, + [RIIC_ICSR2] = 0x09, + [RIIC_ICBRL] = 0x10, + [RIIC_ICBRH] = 0x11, + [RIIC_ICDRT] = 0x12, + [RIIC_ICDRR] = 0x13, }; static const struct riic_of_data riic_rz_v2h_info = { - .regs = { - [RIIC_ICCR1] = 0x00, - [RIIC_ICCR2] = 0x01, - [RIIC_ICMR1] = 0x02, - [RIIC_ICMR3] = 0x04, - [RIIC_ICSER] = 0x06, - [RIIC_ICIER] = 0x07, - [RIIC_ICSR2] = 0x09, - [RIIC_ICBRL] = 0x10, - [RIIC_ICBRH] = 0x11, - [RIIC_ICDRT] = 0x12, - [RIIC_ICDRR] = 0x13, - }, + .regs = riic_rz_v2h_regs, + .fast_mode_plus = true, +}; + +static int riic_i2c_suspend(struct device *dev) +{ + struct riic_dev *riic = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + i2c_mark_adapter_suspended(&riic->adapter); + + /* Disable output on SDA, SCL pins. */ + riic_clear_set_bit(riic, ICCR1_ICE, 0, RIIC_ICCR1); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync(dev); + + return reset_control_assert(riic->rstc); +} + +static int riic_i2c_resume(struct device *dev) +{ + struct riic_dev *riic = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(riic->rstc); + if (ret) + return ret; + + ret = riic_init_hw(riic); + if (ret) { + /* + * In case this happens there is no way to recover from this + * state. The driver will remain loaded. We want to avoid + * keeping the reset line de-asserted for no reason. + */ + reset_control_assert(riic->rstc); + return ret; + } + + i2c_mark_adapter_resumed(&riic->adapter); + + return 0; +} + +static const struct dev_pm_ops riic_i2c_pm_ops = { + SYSTEM_SLEEP_PM_OPS(riic_i2c_suspend, riic_i2c_resume) }; static const struct of_device_id riic_i2c_dt_ids[] = { { .compatible = "renesas,riic-rz", .data = &riic_rz_a_info }, + { .compatible = "renesas,riic-r7s72100", .data = &riic_rz_a1h_info, }, { .compatible = "renesas,riic-r9a09g057", .data = &riic_rz_v2h_info }, { /* Sentinel */ }, }; @@ -551,6 +636,7 @@ static struct platform_driver riic_i2c_driver = { .driver = { .name = "i2c-riic", .of_match_table = riic_i2c_dt_ids, + .pm = pm_ptr(&riic_i2c_pm_ops), }, }; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 01419c738cfc..7698d9d59744 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -130,7 +130,7 @@ static const struct platform_device_id s3c24xx_driver_ids[] = { }, { .name = "s3c2440-hdmiphy-i2c", .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO, - }, { }, + }, { } }; MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c index 52ba1e0845ca..2a351f961b89 100644 --- a/drivers/i2c/busses/i2c-virtio.c +++ b/drivers/i2c/busses/i2c-virtio.c @@ -182,7 +182,7 @@ static u32 virtio_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static struct i2c_algorithm virtio_algorithm = { +static const struct i2c_algorithm virtio_algorithm = { .xfer = virtio_i2c_xfer, .functionality = virtio_i2c_func, }; @@ -237,7 +237,7 @@ static void virtio_i2c_remove(struct virtio_device *vdev) virtio_i2c_del_vqs(vdev); } -static struct virtio_device_id id_table[] = { +static const struct virtio_device_id id_table[] = { { VIRTIO_ID_I2C_ADAPTER, VIRTIO_DEV_ANY_ID }, {} }; diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index b63f75e44296..7c810893bfa3 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -915,6 +915,27 @@ int i2c_dev_irq_from_resources(const struct resource *resources, return 0; } +/* + * Serialize device instantiation in case it can be instantiated explicitly + * and by auto-detection + */ +static int i2c_lock_addr(struct i2c_adapter *adap, unsigned short addr, + unsigned short flags) +{ + if (!(flags & I2C_CLIENT_TEN) && + test_and_set_bit(addr, adap->addrs_in_instantiation)) + return -EBUSY; + + return 0; +} + +static void i2c_unlock_addr(struct i2c_adapter *adap, unsigned short addr, + unsigned short flags) +{ + if (!(flags & I2C_CLIENT_TEN)) + clear_bit(addr, adap->addrs_in_instantiation); +} + /** * i2c_new_client_device - instantiate an i2c device * @adap: the adapter managing the device @@ -962,6 +983,10 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf goto out_err_silent; } + status = i2c_lock_addr(adap, client->addr, client->flags); + if (status) + goto out_err_silent; + /* Check for address business */ status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client)); if (status) @@ -993,6 +1018,8 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); + i2c_unlock_addr(adap, client->addr, client->flags); + return client; out_remove_swnode: @@ -1004,6 +1031,7 @@ out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x (%d)\n", client->name, client->addr, status); + i2c_unlock_addr(adap, client->addr, client->flags); out_err_silent: if (need_put) put_device(&client->dev); @@ -1068,7 +1096,7 @@ EXPORT_SYMBOL(i2c_find_device_by_fwnode); static const struct i2c_device_id dummy_id[] = { { "dummy", }, { "smbus_host_notify", }, - { }, + { } }; static int dummy_probe(struct i2c_client *client) @@ -1367,10 +1395,6 @@ struct i2c_adapter *i2c_verify_adapter(struct device *dev) } EXPORT_SYMBOL(i2c_verify_adapter); -#ifdef CONFIG_I2C_COMPAT -static struct class_compat *i2c_adapter_compat_class; -#endif - static void i2c_scan_static_board_info(struct i2c_adapter *adapter) { struct i2c_devinfo *devinfo; @@ -1524,7 +1548,18 @@ static int i2c_register_adapter(struct i2c_adapter *adap) dev_set_name(&adap->dev, "i2c-%d", adap->nr); adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; - res = device_register(&adap->dev); + device_initialize(&adap->dev); + + /* + * This adapter can be used as a parent immediately after device_add(), + * setup runtime-pm (especially ignore-children) before hand. + */ + device_enable_async_suspend(&adap->dev); + pm_runtime_no_callbacks(&adap->dev); + pm_suspend_ignore_children(&adap->dev, true); + pm_runtime_enable(&adap->dev); + + res = device_add(&adap->dev); if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); goto out_list; @@ -1536,25 +1571,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) if (res) goto out_reg; - device_enable_async_suspend(&adap->dev); - pm_runtime_no_callbacks(&adap->dev); - pm_suspend_ignore_children(&adap->dev, true); - pm_runtime_enable(&adap->dev); - res = i2c_init_recovery(adap); if (res == -EPROBE_DEFER) goto out_reg; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); -#ifdef CONFIG_I2C_COMPAT - res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, - adap->dev.parent); - if (res) - dev_warn(&adap->dev, - "Failed to create compatibility class link\n"); -#endif - /* create pre-declared device nodes */ of_i2c_register_devices(adap); i2c_acpi_install_space_handler(adap); @@ -1761,11 +1783,6 @@ void i2c_del_adapter(struct i2c_adapter *adap) device_for_each_child(&adap->dev, NULL, __unregister_client); device_for_each_child(&adap->dev, NULL, __unregister_dummy); -#ifdef CONFIG_I2C_COMPAT - class_compat_remove_link(i2c_adapter_compat_class, &adap->dev, - adap->dev.parent); -#endif - /* device name is gone after device_unregister */ dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name); @@ -2074,13 +2091,6 @@ static int __init i2c_init(void) i2c_debugfs_root = debugfs_create_dir("i2c", NULL); -#ifdef CONFIG_I2C_COMPAT - i2c_adapter_compat_class = class_compat_register("i2c-adapter"); - if (!i2c_adapter_compat_class) { - retval = -ENOMEM; - goto bus_err; - } -#endif retval = i2c_add_driver(&dummy_driver); if (retval) goto class_err; @@ -2093,10 +2103,6 @@ static int __init i2c_init(void) return 0; class_err: -#ifdef CONFIG_I2C_COMPAT - class_compat_unregister(i2c_adapter_compat_class); -bus_err: -#endif is_registered = false; bus_unregister(&i2c_bus_type); return retval; @@ -2109,9 +2115,6 @@ static void __exit i2c_exit(void) if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier)); i2c_del_driver(&dummy_driver); -#ifdef CONFIG_I2C_COMPAT - class_compat_unregister(i2c_adapter_compat_class); -#endif debugfs_remove_recursive(i2c_debugfs_root); bus_unregister(&i2c_bus_type); tracepoint_synchronize_unregister(); diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c index e3765e12f93b..faefe1dfa8e5 100644 --- a/drivers/i2c/i2c-core-slave.c +++ b/drivers/i2c/i2c-core-slave.c @@ -109,15 +109,12 @@ EXPORT_SYMBOL_GPL(i2c_slave_event); bool i2c_detect_slave_mode(struct device *dev) { if (IS_BUILTIN(CONFIG_OF) && dev->of_node) { - struct device_node *child; u32 reg; - for_each_child_of_node(dev->of_node, child) { + for_each_child_of_node_scoped(dev->of_node, child) { of_property_read_u32(child, "reg", ®); - if (reg & I2C_OWN_SLAVE_ADDRESS) { - of_node_put(child); + if (reg & I2C_OWN_SLAVE_ADDRESS) return true; - } } } else if (IS_BUILTIN(CONFIG_ACPI) && ACPI_HANDLE(dev)) { dev_dbg(dev, "ACPI slave is not supported yet\n"); diff --git a/drivers/i2c/i2c-slave-testunit.c b/drivers/i2c/i2c-slave-testunit.c index 4c550306f3ec..9fe3150378e8 100644 --- a/drivers/i2c/i2c-slave-testunit.c +++ b/drivers/i2c/i2c-slave-testunit.c @@ -6,7 +6,10 @@ * Copyright (C) 2020 by Renesas Electronics Corporation */ +#include <generated/utsrelease.h> #include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> @@ -14,12 +17,14 @@ #include <linux/slab.h> #include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */ -#define TU_CUR_VERSION 0x01 +#define TU_VERSION_MAX_LENGTH 128 enum testunit_cmds { TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */ TU_CMD_SMBUS_HOST_NOTIFY, TU_CMD_SMBUS_BLOCK_PROC_CALL, + TU_CMD_GET_VERSION_WITH_REP_START, + TU_CMD_SMBUS_ALERT_REQUEST, TU_NUM_CMDS }; @@ -39,50 +44,38 @@ struct testunit_data { unsigned long flags; u8 regs[TU_NUM_REGS]; u8 reg_idx; + u8 read_idx; struct i2c_client *client; struct delayed_work worker; + struct gpio_desc *gpio; + struct completion alert_done; }; -static void i2c_slave_testunit_work(struct work_struct *work) -{ - struct testunit_data *tu = container_of(work, struct testunit_data, worker.work); - struct i2c_msg msg; - u8 msgbuf[256]; - int ret = 0; - - msg.addr = I2C_CLIENT_END; - msg.buf = msgbuf; +static char tu_version_info[] = "v" UTS_RELEASE "\n\0"; - switch (tu->regs[TU_REG_CMD]) { - case TU_CMD_READ_BYTES: - msg.addr = tu->regs[TU_REG_DATAL]; - msg.flags = I2C_M_RD; - msg.len = tu->regs[TU_REG_DATAH]; - break; +static int i2c_slave_testunit_smbalert_cb(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + struct testunit_data *tu = i2c_get_clientdata(client); - case TU_CMD_SMBUS_HOST_NOTIFY: - msg.addr = 0x08; - msg.flags = 0; - msg.len = 3; - msgbuf[0] = tu->client->addr; - msgbuf[1] = tu->regs[TU_REG_DATAL]; - msgbuf[2] = tu->regs[TU_REG_DATAH]; + switch (event) { + case I2C_SLAVE_READ_PROCESSED: + gpiod_set_value(tu->gpio, 0); + fallthrough; + case I2C_SLAVE_READ_REQUESTED: + *val = tu->regs[TU_REG_DATAL]; break; - default: + case I2C_SLAVE_STOP: + complete(&tu->alert_done); break; - } - if (msg.addr != I2C_CLIENT_END) { - ret = i2c_transfer(tu->client->adapter, &msg, 1); - /* convert '0 msgs transferred' to errno */ - ret = (ret == 0) ? -EIO : ret; + case I2C_SLAVE_WRITE_REQUESTED: + case I2C_SLAVE_WRITE_RECEIVED: + return -EOPNOTSUPP; } - if (ret < 0) - dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret); - - clear_bit(TU_FLAG_IN_PROCESS, &tu->flags); + return 0; } static int i2c_slave_testunit_slave_cb(struct i2c_client *client, @@ -91,9 +84,20 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client, struct testunit_data *tu = i2c_get_clientdata(client); bool is_proc_call = tu->reg_idx == 3 && tu->regs[TU_REG_DATAL] == 1 && tu->regs[TU_REG_CMD] == TU_CMD_SMBUS_BLOCK_PROC_CALL; + bool is_get_version = tu->reg_idx == 3 && + tu->regs[TU_REG_CMD] == TU_CMD_GET_VERSION_WITH_REP_START; int ret = 0; switch (event) { + case I2C_SLAVE_WRITE_REQUESTED: + if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags)) + return -EBUSY; + + memset(tu->regs, 0, TU_NUM_REGS); + tu->reg_idx = 0; + tu->read_idx = 0; + break; + case I2C_SLAVE_WRITE_RECEIVED: if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags)) return -EBUSY; @@ -127,27 +131,93 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client, tu->reg_idx = 0; break; - case I2C_SLAVE_WRITE_REQUESTED: - if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags)) - return -EBUSY; - - memset(tu->regs, 0, TU_NUM_REGS); - tu->reg_idx = 0; - break; - case I2C_SLAVE_READ_PROCESSED: - if (is_proc_call && tu->regs[TU_REG_DATAH]) + /* Advance until we reach the NUL character */ + if (is_get_version && tu_version_info[tu->read_idx] != 0) + tu->read_idx++; + else if (is_proc_call && tu->regs[TU_REG_DATAH]) tu->regs[TU_REG_DATAH]--; + fallthrough; case I2C_SLAVE_READ_REQUESTED: - *val = is_proc_call ? tu->regs[TU_REG_DATAH] : TU_CUR_VERSION; + if (is_get_version) + *val = tu_version_info[tu->read_idx]; + else if (is_proc_call) + *val = tu->regs[TU_REG_DATAH]; + else + *val = test_bit(TU_FLAG_IN_PROCESS, &tu->flags) ? + tu->regs[TU_REG_CMD] : 0; break; } return ret; } +static void i2c_slave_testunit_work(struct work_struct *work) +{ + struct testunit_data *tu = container_of(work, struct testunit_data, worker.work); + unsigned long time_left; + struct i2c_msg msg; + u8 msgbuf[256]; + u16 orig_addr; + int ret = 0; + + msg.addr = I2C_CLIENT_END; + msg.buf = msgbuf; + + switch (tu->regs[TU_REG_CMD]) { + case TU_CMD_READ_BYTES: + msg.addr = tu->regs[TU_REG_DATAL]; + msg.flags = I2C_M_RD; + msg.len = tu->regs[TU_REG_DATAH]; + break; + + case TU_CMD_SMBUS_HOST_NOTIFY: + msg.addr = 0x08; + msg.flags = 0; + msg.len = 3; + msgbuf[0] = tu->client->addr; + msgbuf[1] = tu->regs[TU_REG_DATAL]; + msgbuf[2] = tu->regs[TU_REG_DATAH]; + break; + + case TU_CMD_SMBUS_ALERT_REQUEST: + i2c_slave_unregister(tu->client); + orig_addr = tu->client->addr; + tu->client->addr = 0x0c; + ret = i2c_slave_register(tu->client, i2c_slave_testunit_smbalert_cb); + if (ret) + goto out_smbalert; + + reinit_completion(&tu->alert_done); + gpiod_set_value(tu->gpio, 1); + time_left = wait_for_completion_timeout(&tu->alert_done, HZ); + if (!time_left) + ret = -ETIMEDOUT; + + i2c_slave_unregister(tu->client); +out_smbalert: + tu->client->addr = orig_addr; + i2c_slave_register(tu->client, i2c_slave_testunit_slave_cb); + break; + + default: + break; + } + + if (msg.addr != I2C_CLIENT_END) { + ret = i2c_transfer(tu->client->adapter, &msg, 1); + /* convert '0 msgs transferred' to errno */ + ret = (ret == 0) ? -EIO : ret; + } + + if (ret < 0) + dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret); + + clear_bit(TU_FLAG_IN_PROCESS, &tu->flags); +} + static int i2c_slave_testunit_probe(struct i2c_client *client) { struct testunit_data *tu; @@ -158,8 +228,18 @@ static int i2c_slave_testunit_probe(struct i2c_client *client) tu->client = client; i2c_set_clientdata(client, tu); + init_completion(&tu->alert_done); INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work); + tu->gpio = devm_gpiod_get_index_optional(&client->dev, NULL, 0, GPIOD_OUT_LOW); + if (gpiod_cansleep(tu->gpio)) { + dev_err(&client->dev, "GPIO access which may sleep is not allowed\n"); + return -EDEADLK; + } + + if (sizeof(tu_version_info) > TU_VERSION_MAX_LENGTH) + tu_version_info[TU_VERSION_MAX_LENGTH - 1] = 0; + return i2c_slave_register(client, i2c_slave_testunit_slave_cb); }; diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index db1b9057612a..6d2f66810cdc 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -119,4 +119,20 @@ config I2C_MUX_MLXCPLD This driver can also be built as a module. If so, the module will be called i2c-mux-mlxcpld. +config I2C_MUX_MULE + tristate "Theobroma Systems Mule I2C device multiplexer" + depends on OF && SENSORS_AMC6821 + help + Mule is an MCU that emulates a set of I2C devices, among which + devices that are reachable through an I2C-mux. The devices on the mux + can be selected by writing the appropriate device number to an I2C + configuration register. + + If you say yes to this option, support will be included for a + Theobroma Systems Mule I2C multiplexer. This driver provides access to + I2C devices connected on this mux. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-mule. + endmenu diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 6d9d865e8518..4b24f49515a7 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o obj-$(CONFIG_I2C_MUX_GPMUX) += i2c-mux-gpmux.o obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o +obj-$(CONFIG_I2C_MUX_MULE) += i2c-mux-mule.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o diff --git a/drivers/i2c/muxes/i2c-mux-mule.c b/drivers/i2c/muxes/i2c-mux-mule.c new file mode 100644 index 000000000000..8e942470b35f --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-mule.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Theobroma Systems Mule I2C device multiplexer + * + * Copyright (C) 2024 Theobroma Systems Design und Consulting GmbH + */ + +#include <linux/i2c-mux.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#define MULE_I2C_MUX_CONFIG_REG 0xff +#define MULE_I2C_MUX_DEFAULT_DEV 0x0 + +struct mule_i2c_reg_mux { + struct regmap *regmap; +}; + +static int mule_i2c_mux_select(struct i2c_mux_core *muxc, u32 dev) +{ + struct mule_i2c_reg_mux *mux = muxc->priv; + + return regmap_write(mux->regmap, MULE_I2C_MUX_CONFIG_REG, dev); +} + +static int mule_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 dev) +{ + return mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV); +} + +static void mule_i2c_mux_remove(void *data) +{ + struct i2c_mux_core *muxc = data; + + i2c_mux_del_adapters(muxc); + + mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV); +} + +static int mule_i2c_mux_probe(struct platform_device *pdev) +{ + struct device *mux_dev = &pdev->dev; + struct mule_i2c_reg_mux *priv; + struct i2c_client *client; + struct i2c_mux_core *muxc; + struct device_node *dev; + unsigned int readback; + int ndev, ret; + bool old_fw; + + /* Count devices on the mux */ + ndev = of_get_child_count(mux_dev->of_node); + dev_dbg(mux_dev, "%d devices on the mux\n", ndev); + + client = to_i2c_client(mux_dev->parent); + + muxc = i2c_mux_alloc(client->adapter, mux_dev, ndev, sizeof(*priv), + I2C_MUX_LOCKED, mule_i2c_mux_select, mule_i2c_mux_deselect); + if (!muxc) + return -ENOMEM; + + priv = i2c_mux_priv(muxc); + + priv->regmap = dev_get_regmap(mux_dev->parent, NULL); + if (IS_ERR(priv->regmap)) + return dev_err_probe(mux_dev, PTR_ERR(priv->regmap), + "No parent i2c register map\n"); + + platform_set_drvdata(pdev, muxc); + + /* + * MULE_I2C_MUX_DEFAULT_DEV is guaranteed to exist on all old and new + * mule fw. Mule fw without mux support will accept write ops to the + * config register, but readback returns 0xff (register not updated). + */ + ret = mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV); + if (ret) + return dev_err_probe(mux_dev, ret, + "Failed to write config register\n"); + + ret = regmap_read(priv->regmap, MULE_I2C_MUX_CONFIG_REG, &readback); + if (ret) + return dev_err_probe(mux_dev, ret, + "Failed to read config register\n"); + + old_fw = (readback != MULE_I2C_MUX_DEFAULT_DEV); + + ret = devm_add_action_or_reset(mux_dev, mule_i2c_mux_remove, muxc); + if (ret) + return dev_err_probe(mux_dev, ret, + "Failed to register mux remove\n"); + + /* Create device adapters */ + for_each_child_of_node(mux_dev->of_node, dev) { + u32 reg; + + ret = of_property_read_u32(dev, "reg", ®); + if (ret) + return dev_err_probe(mux_dev, ret, + "No reg property found for %s\n", + of_node_full_name(dev)); + + if (old_fw && reg != 0) { + dev_warn(mux_dev, + "Mux is not supported, please update Mule FW\n"); + continue; + } + + ret = mule_i2c_mux_select(muxc, reg); + if (ret) { + dev_warn(mux_dev, + "Device %d not supported, please update Mule FW\n", reg); + continue; + } + + ret = i2c_mux_add_adapter(muxc, 0, reg); + if (ret) + return ret; + } + + mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV); + + return 0; +} + +static const struct of_device_id mule_i2c_mux_of_match[] = { + { .compatible = "tsd,mule-i2c-mux", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mule_i2c_mux_of_match); + +static struct platform_driver mule_i2c_mux_driver = { + .driver = { + .name = "mule-i2c-mux", + .of_match_table = mule_i2c_mux_of_match, + }, + .probe = mule_i2c_mux_probe, +}; + +module_platform_driver(mule_i2c_mux_driver); + +MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>"); +MODULE_DESCRIPTION("I2C mux driver for Theobroma Systems Mule"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index 85cdbdd44fec..e46ccebcfd22 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -41,10 +41,9 @@ config TXGBE tristate "Wangxun(R) 10GbE PCI Express adapters support" depends on PCI depends on COMMON_CLK + depends on I2C_DESIGNWARE_PLATFORM select MARVELL_10G_PHY select REGMAP - select I2C - select I2C_DESIGNWARE_PLATFORM select PHYLINK select HWMON if TXGBE=y select SFP diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 377def497298..388ce71a29a9 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -761,6 +761,9 @@ struct i2c_adapter { struct regulator *bus_regulator; struct dentry *debugfs; + + /* 7bit address space */ + DECLARE_BITMAP(addrs_in_instantiation, 1 << 7); }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) |