mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-17 10:26:09 +00:00
I2C for 6.3:
* new drivers for HPE GXP and Loongson 2K/LS7A * bigger refactorings for i801 and xiic * gpio driver gained ACPI and SDA-write only support * the core converted some OF helpers to fwnode helpers * usual bunch of driver updates -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEOZGx6rniZ1Gk92RdFA3kzBSgKbYFAmP3q8IACgkQFA3kzBSg KbYdNQ/+P0nGukhAmPJVDHt/GBI7NJ2E0g9oYs1saQcgQFtBxm3CXfdQC4I83KdX xPF69m8Y2fEFOdAjZYH6U+T2G5yRPX8PkcsHMYZe0w7QZepMjfvyfWxqHuNJt9k6 AoYnBp8l2ZvDFDRiYwClUwV8qGyTOUgc2xjishx0EuhSRZni64wgiA4RCVI2Tw5S /uyIkSj9pBVo1xSuM0XNq5vT3w4unPAT8EnXKK/wMu7JL9nMDFKaKyU9KVwP4Fxv 5IRUOGCiXBY/wS4ytKaAdmtUIhHBwzYo6Tv9xEUdivW8gTcp9YROhziCX6WfrPcy 7/dP3XQgG8wf9YlY0p/2ovqb8Yf4pOlC3kZ7BFwvVnwMO9SQUil0npIuNZrxo2k/ nVFy/8cDwfF1i9f6oFsdtg2bWdYEApn70iUhkM/xLC7ogi5/EbRscWSvim24W5zX VTLhsig4qhiza7ha2czGynq8hgJuyD2HeFufAhqfxwYkBKm0+3fX63Cs5RLp5Q4g as6fpKws9iBNhX+0x/I2J6B8IEfqZPExThP7huzzgD25ks+swD1YJ79dlCpJcoUD UV08U3QiUNO4DdQrT3GtGKILYjQFZ64ceqtZkWIKBrgxbabQfBsXkO2ndLdBFSJW LdQIMydNPV1y4VGTfUmnACHzoAVdHpOSlmQXVI0GIfVZ8zZmC7c= =MPV0 -----END PGP SIGNATURE----- Merge tag 'i2c-for-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux Pull i2c updates from Wolfram Sang: - new drivers for HPE GXP and Loongson 2K/LS7A - bigger refactorings for i801 and xiic - gpio driver gained ACPI and SDA-write only support - the core converted some OF helpers to fwnode helpers - usual bunch of driver updates * tag 'i2c-for-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (52 commits) MAINTAINERS: Add HPE GXP I2C Support i2c: Add GXP SoC I2C Controller dt-bindings: i2c: Add hpe,gxp-i2c i2c: xiic: Remove some dead code i2c: xiic: Add SCL frequency configuration support i2c: xiic: Update compatible with new IP version dt-bindings: i2c: xiic: Add 'xlnx,axi-iic-2.1' to compatible i2c: i801: Call i801_check_post() from i801_access() i2c: i801: Call i801_check_pre() from i801_access() i2c: i801: Centralize configuring block commands in i801_block_transaction i2c: i801: Centralize configuring non-block commands in i801_simple_transaction i2c: i801: Handle SMBAUXCTL_E32B in i801_block_transaction_by_block only i2c: i801: Add i801_simple_transaction(), complementing i801_block_transaction() Documentation: i2c: correct spelling dt-bindings: i2c: i2c-st: convert to DT schema i2c: i801: add helper i801_set_hstadd() i2c: i801: make FEATURE_BLOCK_PROC dependent on FEATURE_BLOCK_BUFFER i2c: i801: make FEATURE_HOST_NOTIFY dependent on FEATURE_IRQ i2c: i801: improve interrupt handler i2c: st: use pm_sleep_ptr to avoid ifdef CONFIG_PM_SLEEP ...
This commit is contained in:
commit
008128cd59
59
Documentation/devicetree/bindings/i2c/hpe,gxp-i2c.yaml
Normal file
59
Documentation/devicetree/bindings/i2c/hpe,gxp-i2c.yaml
Normal file
@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/hpe,gxp-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: HPE GXP SoC I2C Controller
|
||||
|
||||
maintainers:
|
||||
- Nick Hawkins <nick.hawkins@hpe.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: hpe,gxp-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
default: 100000
|
||||
|
||||
hpe,sysreg:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to the global status and enable interrupt registers shared
|
||||
between each I2C engine controller instance. It enables the I2C
|
||||
engine controller to act as both a master or slave by being able to
|
||||
arm and respond to interrupts from its engine. Each bit in the
|
||||
registers represent the respective bit position.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c@2600 {
|
||||
compatible = "hpe,gxp-i2c";
|
||||
reg = <0x2500 0x70>;
|
||||
interrupts = <9>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
hpe,sysreg = <&sysreg_system_controller>;
|
||||
clock-frequency = <10000>;
|
||||
|
||||
eeprom@50 {
|
||||
compatible = "atmel,24c128";
|
||||
reg = <0x50>;
|
||||
};
|
||||
};
|
@ -33,6 +33,10 @@ properties:
|
||||
open drain.
|
||||
maxItems: 1
|
||||
|
||||
i2c-gpio,sda-output-only:
|
||||
description: sda as output only
|
||||
type: boolean
|
||||
|
||||
i2c-gpio,scl-output-only:
|
||||
description: scl as output only
|
||||
type: boolean
|
||||
@ -63,6 +67,28 @@ properties:
|
||||
GPIO line used for SCL into open drain mode, and that something is not
|
||||
the GPIO chip. It is essentially an inconsistency flag.
|
||||
|
||||
i2c-gpio,sda-has-no-pullup:
|
||||
type: boolean
|
||||
description: sda is used in a non-compliant way and has no pull-up.
|
||||
Therefore disable open-drain. This property is mutually-exclusive
|
||||
with i2c-gpio,sda-open-drain.
|
||||
|
||||
i2c-gpio,scl-has-no-pullup:
|
||||
type: boolean
|
||||
description: scl is used in a non-compliant way and has no pull-up.
|
||||
Therefore disable open-drain. This property is mutually-exclusive
|
||||
with i2c-gpio,scl-open-drain.
|
||||
|
||||
dependencies:
|
||||
i2c-gpio,sda-has-no-pullup:
|
||||
not:
|
||||
required:
|
||||
- i2c-gpio,sda-open-drain
|
||||
i2c-gpio,scl-has-no-pullup:
|
||||
not:
|
||||
required:
|
||||
- i2c-gpio,scl-open-drain
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- sda-gpios
|
||||
|
@ -41,6 +41,10 @@ properties:
|
||||
- mediatek,mt6797-i2c
|
||||
- mediatek,mt7623-i2c
|
||||
- const: mediatek,mt6577-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt8365-i2c
|
||||
- const: mediatek,mt8168-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt8195-i2c
|
||||
|
@ -1,41 +0,0 @@
|
||||
ST SSC binding, for I2C mode operation
|
||||
|
||||
Required properties :
|
||||
- compatible : Must be "st,comms-ssc-i2c" or "st,comms-ssc4-i2c"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : the interrupt specifier
|
||||
- clock-names: Must contain "ssc".
|
||||
- clocks: Must contain an entry for each name in clock-names. See the common
|
||||
clock bindings.
|
||||
- A pinctrl state named "default" must be defined to set pins in mode of
|
||||
operation for I2C transfer.
|
||||
|
||||
Optional properties :
|
||||
- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
|
||||
the default 100 kHz frequency will be used. As only Normal and Fast modes
|
||||
are supported, possible values are 100000 and 400000.
|
||||
- st,i2c-min-scl-pulse-width-us : The minimum valid SCL pulse width that is
|
||||
allowed through the deglitch circuit. In units of us.
|
||||
- st,i2c-min-sda-pulse-width-us : The minimum valid SDA pulse width that is
|
||||
allowed through the deglitch circuit. In units of us.
|
||||
- A pinctrl state named "idle" could be defined to set pins in idle state
|
||||
when I2C instance is not performing a transfer.
|
||||
- A pinctrl state named "sleep" could be defined to set pins in sleep state
|
||||
when driver enters in suspend.
|
||||
|
||||
|
||||
|
||||
Example :
|
||||
|
||||
i2c0: i2c@fed40000 {
|
||||
compatible = "st,comms-ssc4-i2c";
|
||||
reg = <0xfed40000 0x110>;
|
||||
interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk_s_a0_ls CLK_ICN_REG>;
|
||||
clock-names = "ssc";
|
||||
clock-frequency = <400000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2c0_default>;
|
||||
st,i2c-min-scl-pulse-width-us = <0>;
|
||||
st,i2c-min-sda-pulse-width-us = <5>;
|
||||
};
|
51
Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
Normal file
51
Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
Normal file
@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/loongson,ls2x-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Loongson LS2X I2C Controller
|
||||
|
||||
maintainers:
|
||||
- Binbin Zhou <zhoubinbin@loongson.cn>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- loongson,ls2k-i2c
|
||||
- loongson,ls7a-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c0: i2c@1fe21000 {
|
||||
compatible = "loongson,ls2k-i2c";
|
||||
reg = <0x1fe21000 0x8>;
|
||||
interrupt-parent = <&extioiic>;
|
||||
interrupts = <22 IRQ_TYPE_LEVEL_LOW>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
eeprom@57 {
|
||||
compatible = "atmel,24c16";
|
||||
reg = <0x57>;
|
||||
pagesize = <16>;
|
||||
};
|
||||
};
|
@ -12,14 +12,24 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,msm8226-cci
|
||||
- qcom,msm8916-cci
|
||||
- qcom,msm8974-cci
|
||||
- qcom,msm8996-cci
|
||||
- qcom,sdm845-cci
|
||||
- qcom,sm8250-cci
|
||||
- qcom,sm8450-cci
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,msm8226-cci
|
||||
- qcom,msm8974-cci
|
||||
- qcom,msm8996-cci
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,msm8916-cci
|
||||
- const: qcom,msm8226-cci # CCI v1
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sdm845-cci
|
||||
- qcom,sm6350-cci
|
||||
- qcom,sm8250-cci
|
||||
- qcom,sm8450-cci
|
||||
- const: qcom,msm8996-cci # CCI v2
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
@ -88,10 +98,12 @@ allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8226-cci
|
||||
- qcom,msm8974-cci
|
||||
oneOf:
|
||||
- contains:
|
||||
enum:
|
||||
- qcom,msm8974-cci
|
||||
|
||||
- const: qcom,msm8226-cci
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
@ -105,10 +117,12 @@ allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,msm8916-cci
|
||||
- qcom,msm8996-cci
|
||||
oneOf:
|
||||
- contains:
|
||||
enum:
|
||||
- qcom,msm8916-cci
|
||||
|
||||
- const: qcom,msm8996-cci
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
@ -126,6 +140,7 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sdm845-cci
|
||||
- qcom,sm6350-cci
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
@ -169,7 +184,7 @@ examples:
|
||||
|
||||
cci@ac4a000 {
|
||||
reg = <0x0ac4a000 0x4000>;
|
||||
compatible = "qcom,sdm845-cci";
|
||||
compatible = "qcom,sdm845-cci", "qcom,msm8996-cci";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
@ -29,6 +29,9 @@ properties:
|
||||
minimum: 100000
|
||||
maximum: 400000
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -29,6 +29,9 @@ properties:
|
||||
minimum: 100000
|
||||
maximum: 400000
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
71
Documentation/devicetree/bindings/i2c/st,sti-i2c.yaml
Normal file
71
Documentation/devicetree/bindings/i2c/st,sti-i2c.yaml
Normal file
@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/st,sti-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: I2C controller embedded in STMicroelectronics STi platform
|
||||
|
||||
maintainers:
|
||||
- Patrice Chotard <patrice.chotard@foss.st.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- st,comms-ssc-i2c
|
||||
- st,comms-ssc4-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
|
||||
clock-frequency:
|
||||
enum: [ 100000, 400000 ]
|
||||
default: 100000
|
||||
|
||||
st,i2c-min-scl-pulse-width-us:
|
||||
description:
|
||||
The minimum valid SCL pulse width that is allowed through the
|
||||
deglitch circuit. In units of us.
|
||||
|
||||
st,i2c-min-sda-pulse-width-us:
|
||||
description:
|
||||
The minimum valid SDA pulse width that is allowed through the
|
||||
deglitch circuit. In units of us.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/stih407-clks.h>
|
||||
i2c@fed40000 {
|
||||
compatible = "st,comms-ssc4-i2c";
|
||||
reg = <0xfed40000 0x110>;
|
||||
interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk_s_a0_ls CLK_ICN_REG>;
|
||||
clock-names = "ssc";
|
||||
clock-frequency = <400000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2c0_default>;
|
||||
st,i2c-min-scl-pulse-width-us = <0>;
|
||||
st,i2c-min-sda-pulse-width-us = <5>;
|
||||
};
|
@ -14,7 +14,9 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: xlnx,xps-iic-2.00.a
|
||||
enum:
|
||||
- xlnx,axi-iic-2.1
|
||||
- xlnx,xps-iic-2.00.a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -30,6 +32,13 @@ properties:
|
||||
description: |
|
||||
Input clock name.
|
||||
|
||||
clock-frequency:
|
||||
description:
|
||||
Optional I2C SCL clock frequency. If not specified, do not configure
|
||||
in software, rely only on hardware design value.
|
||||
default: 100000
|
||||
enum: [ 100000, 400000, 1000000 ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -93,7 +93,7 @@ bus arbitration against another master in a multi-master setup.
|
||||
------------------
|
||||
|
||||
This file is write only and you need to write the duration of the arbitration
|
||||
intereference (in µs, maximum is 100ms). The calling process will then sleep
|
||||
interference (in µs, maximum is 100ms). The calling process will then sleep
|
||||
and wait for the next bus clock. The process is interruptible, though.
|
||||
|
||||
Arbitration lost is achieved by waiting for SCL going down by the master under
|
||||
|
@ -238,7 +238,7 @@ This is implemented in the following way in the Linux kernel:
|
||||
* I2C bus drivers trigger SMBus Host Notify by a call to
|
||||
i2c_handle_smbus_host_notify().
|
||||
* I2C drivers for devices which can trigger SMBus Host Notify will have
|
||||
client->irq assigned to a Host Notify IRQ if noone else specified an other.
|
||||
client->irq assigned to a Host Notify IRQ if no one else specified another.
|
||||
|
||||
There is currently no way to retrieve the data parameter from the client.
|
||||
|
||||
|
11
MAINTAINERS
11
MAINTAINERS
@ -2240,6 +2240,7 @@ S: Maintained
|
||||
F: Documentation/hwmon/gxp-fan-ctrl.rst
|
||||
F: Documentation/devicetree/bindings/arm/hpe,gxp.yaml
|
||||
F: Documentation/devicetree/bindings/hwmon/hpe,gxp-fan-ctrl.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/hpe,gxp-i2c.yaml
|
||||
F: Documentation/devicetree/bindings/spi/hpe,gxp-spifi.yaml
|
||||
F: Documentation/devicetree/bindings/timer/hpe,gxp-timer.yaml
|
||||
F: arch/arm/boot/dts/hpe-bmc*
|
||||
@ -2247,6 +2248,7 @@ F: arch/arm/boot/dts/hpe-gxp*
|
||||
F: arch/arm/mach-hpe/
|
||||
F: drivers/clocksource/timer-gxp.c
|
||||
F: drivers/hwmon/gxp-fan-ctrl.c
|
||||
F: drivers/i2c/busses/i2c-gxp.c
|
||||
F: drivers/spi/spi-gxp.c
|
||||
F: drivers/watchdog/gxp-wdt.c
|
||||
|
||||
@ -2795,7 +2797,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
W: http://www.stlinux.com
|
||||
F: Documentation/devicetree/bindings/spi/st,ssc-spi.yaml
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-st.txt
|
||||
F: Documentation/devicetree/bindings/i2c/st,sti-i2c.yaml
|
||||
F: arch/arm/boot/dts/sti*
|
||||
F: arch/arm/mach-sti/
|
||||
F: drivers/ata/ahci_st.c
|
||||
@ -12097,6 +12099,13 @@ F: drivers/*/*loongarch*
|
||||
F: Documentation/loongarch/
|
||||
F: Documentation/translations/zh_CN/loongarch/
|
||||
|
||||
LOONGSON LS2X I2C DRIVER
|
||||
M: Binbin Zhou <zhoubinbin@loongson.cn>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
|
||||
F: drivers/i2c/busses/i2c-ls2x.c
|
||||
|
||||
LOONGSON-2 SOC SERIES GUTS DRIVER
|
||||
M: Yinbo Zhu <zhuyinbo@loongson.cn>
|
||||
L: loongarch@lists.linux.dev
|
||||
|
@ -184,8 +184,9 @@ static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
|
||||
|
||||
/* read ack: SDA should be pulled down by slave, or it may
|
||||
* NAK (usually to report problems with the data we wrote).
|
||||
* Always report ACK if SDA is write-only.
|
||||
*/
|
||||
ack = !getsda(adap); /* ack: sda is pulled low -> success */
|
||||
ack = !adap->getsda || !getsda(adap); /* ack: sda is pulled low -> success */
|
||||
bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,
|
||||
ack ? "A" : "NA");
|
||||
|
||||
@ -238,71 +239,55 @@ static int test_bus(struct i2c_adapter *i2c_adap)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (adap->getsda == NULL)
|
||||
pr_info("%s: SDA is write-only, testing not possible\n", name);
|
||||
if (adap->getscl == NULL)
|
||||
pr_info("%s: Testing SDA only, SCL is not readable\n", name);
|
||||
pr_info("%s: SCL is write-only, testing not possible\n", name);
|
||||
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
sda = adap->getsda ? getsda(adap) : 1;
|
||||
scl = adap->getscl ? getscl(adap) : 1;
|
||||
if (!scl || !sda) {
|
||||
printk(KERN_WARNING
|
||||
"%s: bus seems to be busy (scl=%d, sda=%d)\n",
|
||||
name, scl, sda);
|
||||
pr_warn("%s: bus seems to be busy (scl=%d, sda=%d)\n", name, scl, sda);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
sdalo(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
if (sda) {
|
||||
printk(KERN_WARNING "%s: SDA stuck high!\n", name);
|
||||
if (adap->getsda && getsda(adap)) {
|
||||
pr_warn("%s: SDA stuck high!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
if (!scl) {
|
||||
printk(KERN_WARNING
|
||||
"%s: SCL unexpected low while pulling SDA low!\n",
|
||||
name);
|
||||
if (adap->getscl && !getscl(adap)) {
|
||||
pr_warn("%s: SCL unexpected low while pulling SDA low!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
sdahi(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
if (!sda) {
|
||||
printk(KERN_WARNING "%s: SDA stuck low!\n", name);
|
||||
if (adap->getsda && !getsda(adap)) {
|
||||
pr_warn("%s: SDA stuck low!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
if (!scl) {
|
||||
printk(KERN_WARNING
|
||||
"%s: SCL unexpected low while pulling SDA high!\n",
|
||||
name);
|
||||
if (adap->getscl && !getscl(adap)) {
|
||||
pr_warn("%s: SCL unexpected low while pulling SDA high!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
scllo(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 0 : getscl(adap);
|
||||
if (scl) {
|
||||
printk(KERN_WARNING "%s: SCL stuck high!\n", name);
|
||||
if (adap->getscl && getscl(adap)) {
|
||||
pr_warn("%s: SCL stuck high!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
if (!sda) {
|
||||
printk(KERN_WARNING
|
||||
"%s: SDA unexpected low while pulling SCL low!\n",
|
||||
name);
|
||||
if (adap->getsda && !getsda(adap)) {
|
||||
pr_warn("%s: SDA unexpected low while pulling SCL low!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
sclhi(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
if (!scl) {
|
||||
printk(KERN_WARNING "%s: SCL stuck low!\n", name);
|
||||
if (adap->getscl && !getscl(adap)) {
|
||||
pr_warn("%s: SCL stuck low!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
if (!sda) {
|
||||
printk(KERN_WARNING
|
||||
"%s: SDA unexpected low while pulling SCL high!\n",
|
||||
name);
|
||||
if (adap->getsda && !getsda(adap)) {
|
||||
pr_warn("%s: SDA unexpected low while pulling SCL high!\n", name);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
@ -420,6 +405,10 @@ static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
|
||||
unsigned char *temp = msg->buf;
|
||||
int count = msg->len;
|
||||
const unsigned flags = msg->flags;
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
|
||||
if (!adap->getsda)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
while (count > 0) {
|
||||
inval = i2c_inb(i2c_adap);
|
||||
@ -670,11 +659,15 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Complain if SCL can't be read */
|
||||
if (bit_adap->getscl == NULL) {
|
||||
if (bit_adap->getsda == NULL)
|
||||
dev_warn(&adap->dev, "Not I2C compliant: can't read SDA\n");
|
||||
|
||||
if (bit_adap->getscl == NULL)
|
||||
dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n");
|
||||
|
||||
if (bit_adap->getsda == NULL || bit_adap->getscl == NULL)
|
||||
dev_warn(&adap->dev, "Bus may be unreliable\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -659,6 +659,13 @@ config I2C_GPIO_FAULT_INJECTOR
|
||||
faults to an I2C bus, so another bus master can be stress-tested.
|
||||
This is for debugging. If unsure, say 'no'.
|
||||
|
||||
config I2C_GXP
|
||||
tristate "GXP I2C Interface"
|
||||
depends on ARCH_HPE_GXP || COMPILE_TEST
|
||||
help
|
||||
This enables support for GXP I2C interface. The I2C engines can be
|
||||
either I2C master or I2C slaves.
|
||||
|
||||
config I2C_HIGHLANDER
|
||||
tristate "Highlander FPGA SMBus interface"
|
||||
depends on SH_HIGHLANDER || COMPILE_TEST
|
||||
@ -761,6 +768,17 @@ config I2C_LPC2K
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-lpc2k.
|
||||
|
||||
config I2C_LS2X
|
||||
tristate "Loongson LS2X I2C adapter"
|
||||
depends on MACH_LOONGSON64 || COMPILE_TEST
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
I2C interface on the Loongson-2K SoCs and Loongson LS7A bridge
|
||||
chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-ls2x.
|
||||
|
||||
config I2C_MLXBF
|
||||
tristate "Mellanox BlueField I2C controller"
|
||||
depends on MELLANOX_PLATFORM && ARM64
|
||||
|
@ -77,6 +77,7 @@ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
||||
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
|
||||
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
||||
obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
|
||||
obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o
|
||||
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
|
||||
obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o
|
||||
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
|
||||
@ -127,6 +128,7 @@ obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o
|
||||
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
|
||||
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
|
||||
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
|
||||
obj-$(CONFIG_I2C_GXP) += i2c-gxp.o
|
||||
|
||||
# External I2C/SMBus adapter drivers
|
||||
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
|
||||
|
@ -979,15 +979,13 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
|
||||
const struct of_device_id *match;
|
||||
struct aspeed_i2c_bus *bus;
|
||||
struct clk *parent_clk;
|
||||
struct resource *res;
|
||||
int irq, ret;
|
||||
|
||||
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
bus->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
bus->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(bus->base))
|
||||
return PTR_ERR(bus->base);
|
||||
|
||||
|
@ -302,7 +302,6 @@ static int
|
||||
i2c_au1550_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_au1550_data *priv;
|
||||
struct resource *r;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct i2c_au1550_data),
|
||||
@ -310,8 +309,7 @@ i2c_au1550_probe(struct platform_device *pdev)
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->psc_base = devm_ioremap_resource(&pdev->dev, r);
|
||||
priv->psc_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(priv->psc_base))
|
||||
return PTR_ERR(priv->psc_base);
|
||||
|
||||
|
@ -407,7 +407,6 @@ static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
|
||||
static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm2835_i2c_dev *i2c_dev;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
struct i2c_adapter *adap;
|
||||
struct clk *mclk;
|
||||
@ -420,8 +419,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||
i2c_dev->dev = &pdev->dev;
|
||||
init_completion(&i2c_dev->completion);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
i2c_dev->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
i2c_dev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(i2c_dev->regs))
|
||||
return PTR_ERR(i2c_dev->regs);
|
||||
|
||||
|
@ -115,8 +115,6 @@
|
||||
#define CNDS_I2C_PM_TIMEOUT 1000 /* ms */
|
||||
|
||||
#define CDNS_I2C_FIFO_DEPTH 16
|
||||
/* FIFO depth at which the DATA interrupt occurs */
|
||||
#define CDNS_I2C_DATA_INTR_DEPTH (CDNS_I2C_FIFO_DEPTH - 2)
|
||||
#define CDNS_I2C_MAX_TRANSFER_SIZE 255
|
||||
/* Transfer size in multiples of data interrupt depth */
|
||||
#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_MAX_TRANSFER_SIZE - 3)
|
||||
@ -175,7 +173,6 @@ enum cdns_i2c_slave_state {
|
||||
* @send_count: Number of bytes still expected to send
|
||||
* @recv_count: Number of bytes still expected to receive
|
||||
* @curr_recv_count: Number of bytes to be received in current transfer
|
||||
* @irq: IRQ number
|
||||
* @input_clk: Input clock to I2C controller
|
||||
* @i2c_clk: Maximum I2C clock speed
|
||||
* @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
|
||||
@ -200,7 +197,6 @@ struct cdns_i2c {
|
||||
unsigned int send_count;
|
||||
unsigned int recv_count;
|
||||
unsigned int curr_recv_count;
|
||||
int irq;
|
||||
unsigned long input_clk;
|
||||
unsigned int i2c_clk;
|
||||
unsigned int bus_hold_flag;
|
||||
@ -616,9 +612,7 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
|
||||
}
|
||||
|
||||
/* Determine hold_clear based on number of bytes to receive and hold flag */
|
||||
if (!id->bus_hold_flag &&
|
||||
((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
|
||||
(id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
|
||||
if (!id->bus_hold_flag && id->recv_count <= CDNS_I2C_FIFO_DEPTH) {
|
||||
if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
|
||||
hold_clear = true;
|
||||
if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
|
||||
@ -1246,7 +1240,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *r_mem;
|
||||
struct cdns_i2c *id;
|
||||
int ret;
|
||||
int ret, irq;
|
||||
const struct of_device_id *match;
|
||||
|
||||
id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
|
||||
@ -1277,10 +1271,9 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(id->membase))
|
||||
return PTR_ERR(id->membase);
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
id->irq = ret;
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
id->adap.owner = THIS_MODULE;
|
||||
id->adap.dev.of_node = pdev->dev.of_node;
|
||||
@ -1331,10 +1324,10 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,
|
||||
ret = devm_request_irq(&pdev->dev, irq, cdns_i2c_isr, 0,
|
||||
DRIVER_NAME, id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
|
||||
dev_err(&pdev->dev, "cannot get irq %d\n", irq);
|
||||
goto err_clk_dis;
|
||||
}
|
||||
cdns_i2c_init(id);
|
||||
@ -1344,7 +1337,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
goto err_clk_dis;
|
||||
|
||||
dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
|
||||
id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
|
||||
id->i2c_clk / 1000, (unsigned long)r_mem->start, irq);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -391,7 +391,7 @@ u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||
|
||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 reg;
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
ret = i2c_dw_acquire_lock(dev);
|
||||
@ -442,7 +442,7 @@ err_release_lock:
|
||||
void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
{
|
||||
int timeout = 100;
|
||||
u32 status;
|
||||
unsigned int status;
|
||||
|
||||
do {
|
||||
__i2c_dw_disable_nowait(dev);
|
||||
@ -465,7 +465,7 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
dev_warn(dev->dev, "timeout in disabling adapter\n");
|
||||
}
|
||||
|
||||
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
|
||||
u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev)
|
||||
{
|
||||
/*
|
||||
* Clock is not necessary if we got LCNT/HCNT values directly from
|
||||
@ -527,7 +527,7 @@ void i2c_dw_release_lock(struct dw_i2c_dev *dev)
|
||||
*/
|
||||
int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 status;
|
||||
unsigned int status;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status,
|
||||
@ -571,7 +571,8 @@ int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
||||
|
||||
int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 param, tx_fifo_depth, rx_fifo_depth;
|
||||
u32 tx_fifo_depth, rx_fifo_depth;
|
||||
unsigned int param;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@ -611,7 +612,7 @@ u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||
|
||||
void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 dummy;
|
||||
unsigned int dummy;
|
||||
int ret;
|
||||
|
||||
ret = i2c_dw_acquire_lock(dev);
|
||||
|
@ -37,6 +37,7 @@
|
||||
#define DW_IC_CON_STOP_DET_IFADDRESSED BIT(7)
|
||||
#define DW_IC_CON_TX_EMPTY_CTRL BIT(8)
|
||||
#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9)
|
||||
#define DW_IC_CON_BUS_CLEAR_CTRL BIT(11)
|
||||
|
||||
#define DW_IC_DATA_CMD_DAT GENMASK(7, 0)
|
||||
|
||||
@ -264,7 +265,7 @@ struct dw_i2c_dev {
|
||||
u8 *rx_buf;
|
||||
int msg_err;
|
||||
unsigned int status;
|
||||
u32 abort_source;
|
||||
unsigned int abort_source;
|
||||
int irq;
|
||||
u32 flags;
|
||||
struct i2c_adapter adapter;
|
||||
@ -320,7 +321,7 @@ 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);
|
||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
|
||||
unsigned long i2c_dw_clk_rate(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);
|
||||
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
|
||||
void i2c_dw_release_lock(struct dw_i2c_dev *dev);
|
||||
|
@ -39,7 +39,7 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
||||
|
||||
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 comp_param1;
|
||||
unsigned int comp_param1;
|
||||
u32 sda_falling_time, scl_falling_time;
|
||||
struct i2c_timings *t = &dev->timings;
|
||||
const char *fp_str = "";
|
||||
@ -211,7 +211,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_msg *msgs = dev->msgs;
|
||||
u32 ic_con = 0, ic_tar = 0;
|
||||
u32 dummy;
|
||||
unsigned int dummy;
|
||||
|
||||
/* Disable the adapter */
|
||||
__i2c_dw_disable(dev);
|
||||
@ -287,7 +287,7 @@ static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx;
|
||||
int cmd = 0, status;
|
||||
u8 *tx_buf;
|
||||
u32 val;
|
||||
unsigned int val;
|
||||
|
||||
/*
|
||||
* In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
|
||||
@ -505,7 +505,8 @@ i2c_dw_read(struct dw_i2c_dev *dev)
|
||||
unsigned int rx_valid;
|
||||
|
||||
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
|
||||
u32 len, tmp;
|
||||
unsigned int tmp;
|
||||
u32 len;
|
||||
u8 *buf;
|
||||
|
||||
if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
|
||||
@ -653,7 +654,7 @@ static const struct i2c_adapter_quirks i2c_dw_quirks = {
|
||||
|
||||
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 stat, dummy;
|
||||
unsigned int stat, dummy;
|
||||
|
||||
/*
|
||||
* The IC_INTR_STAT register just indicates "enabled" interrupts.
|
||||
@ -714,7 +715,7 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_id;
|
||||
u32 stat, enabled;
|
||||
unsigned int stat, enabled;
|
||||
|
||||
regmap_read(dev->map, DW_IC_ENABLE, &enabled);
|
||||
regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat);
|
||||
@ -865,6 +866,7 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_adapter *adap = &dev->adapter;
|
||||
unsigned long irq_flags;
|
||||
unsigned int ic_con;
|
||||
int ret;
|
||||
|
||||
init_completion(&dev->cmd_complete);
|
||||
@ -884,6 +886,25 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Lock the bus for accessing DW_IC_CON */
|
||||
ret = i2c_dw_acquire_lock(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* On AMD platforms BIOS advertises the bus clear feature
|
||||
* and enables the SCL/SDA stuck low. SMU FW does the
|
||||
* bus recovery process. Driver should not ignore this BIOS
|
||||
* advertisement of bus clear feature.
|
||||
*/
|
||||
ret = regmap_read(dev->map, DW_IC_CON, &ic_con);
|
||||
i2c_dw_release_lock(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ic_con & DW_IC_CON_BUS_CLEAR_CTRL)
|
||||
dev->master_cfg |= DW_IC_CON_BUS_CLEAR_CTRL;
|
||||
|
||||
ret = dev->init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -98,7 +98,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave)
|
||||
|
||||
static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 stat, dummy;
|
||||
unsigned int stat, dummy;
|
||||
|
||||
/*
|
||||
* The IC_INTR_STAT register just indicates "enabled" interrupts.
|
||||
@ -150,7 +150,7 @@ static u32 i2c_dw_read_clear_intrbits_slave(struct dw_i2c_dev *dev)
|
||||
static irqreturn_t i2c_dw_isr_slave(int this_irq, void *dev_id)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_id;
|
||||
u32 raw_stat, stat, enabled, tmp;
|
||||
unsigned int raw_stat, stat, enabled, tmp;
|
||||
u8 val = 0, slave_activity;
|
||||
|
||||
regmap_read(dev->map, DW_IC_ENABLE, &enabled);
|
||||
|
@ -13,9 +13,9 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/i2c-gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct i2c_gpio_private_data {
|
||||
@ -300,22 +300,29 @@ static inline void i2c_gpio_fault_injector_init(struct platform_device *pdev) {}
|
||||
static inline void i2c_gpio_fault_injector_exit(struct platform_device *pdev) {}
|
||||
#endif /* CONFIG_I2C_GPIO_FAULT_INJECTOR*/
|
||||
|
||||
static void of_i2c_gpio_get_props(struct device_node *np,
|
||||
struct i2c_gpio_platform_data *pdata)
|
||||
/* Get i2c-gpio properties from DT or ACPI table */
|
||||
static void i2c_gpio_get_properties(struct device *dev,
|
||||
struct i2c_gpio_platform_data *pdata)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay);
|
||||
device_property_read_u32(dev, "i2c-gpio,delay-us", &pdata->udelay);
|
||||
|
||||
if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", ®))
|
||||
if (!device_property_read_u32(dev, "i2c-gpio,timeout-ms", ®))
|
||||
pdata->timeout = msecs_to_jiffies(reg);
|
||||
|
||||
pdata->sda_is_open_drain =
|
||||
of_property_read_bool(np, "i2c-gpio,sda-open-drain");
|
||||
device_property_read_bool(dev, "i2c-gpio,sda-open-drain");
|
||||
pdata->scl_is_open_drain =
|
||||
of_property_read_bool(np, "i2c-gpio,scl-open-drain");
|
||||
device_property_read_bool(dev, "i2c-gpio,scl-open-drain");
|
||||
pdata->scl_is_output_only =
|
||||
of_property_read_bool(np, "i2c-gpio,scl-output-only");
|
||||
device_property_read_bool(dev, "i2c-gpio,scl-output-only");
|
||||
pdata->sda_is_output_only =
|
||||
device_property_read_bool(dev, "i2c-gpio,sda-output-only");
|
||||
pdata->sda_has_no_pullup =
|
||||
device_property_read_bool(dev, "i2c-gpio,sda-has-no-pullup");
|
||||
pdata->scl_has_no_pullup =
|
||||
device_property_read_bool(dev, "i2c-gpio,scl-has-no-pullup");
|
||||
}
|
||||
|
||||
static struct gpio_desc *i2c_gpio_get_desc(struct device *dev,
|
||||
@ -361,7 +368,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
struct i2c_algo_bit_data *bit_data;
|
||||
struct i2c_adapter *adap;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
enum gpiod_flags gflags;
|
||||
int ret;
|
||||
|
||||
@ -373,8 +380,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
bit_data = &priv->bit_data;
|
||||
pdata = &priv->pdata;
|
||||
|
||||
if (np) {
|
||||
of_i2c_gpio_get_props(np, pdata);
|
||||
if (fwnode) {
|
||||
i2c_gpio_get_properties(dev, pdata);
|
||||
} else {
|
||||
/*
|
||||
* If all platform data settings are zero it is OK
|
||||
@ -392,7 +399,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
* handle them as we handle any other output. Else we enforce open
|
||||
* drain as this is required for an I2C bus.
|
||||
*/
|
||||
if (pdata->sda_is_open_drain)
|
||||
if (pdata->sda_is_open_drain || pdata->sda_has_no_pullup)
|
||||
gflags = GPIOD_OUT_HIGH;
|
||||
else
|
||||
gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
|
||||
@ -400,7 +407,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->sda))
|
||||
return PTR_ERR(priv->sda);
|
||||
|
||||
if (pdata->scl_is_open_drain)
|
||||
if (pdata->scl_is_open_drain || pdata->scl_has_no_pullup)
|
||||
gflags = GPIOD_OUT_HIGH;
|
||||
else
|
||||
gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
|
||||
@ -418,7 +425,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
|
||||
if (!pdata->scl_is_output_only)
|
||||
bit_data->getscl = i2c_gpio_getscl;
|
||||
bit_data->getsda = i2c_gpio_getsda;
|
||||
if (!pdata->sda_is_output_only)
|
||||
bit_data->getsda = i2c_gpio_getsda;
|
||||
|
||||
if (pdata->udelay)
|
||||
bit_data->udelay = pdata->udelay;
|
||||
@ -435,7 +443,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
bit_data->data = priv;
|
||||
|
||||
adap->owner = THIS_MODULE;
|
||||
if (np)
|
||||
if (fwnode)
|
||||
strscpy(adap->name, dev_name(dev), sizeof(adap->name));
|
||||
else
|
||||
snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
|
||||
@ -443,7 +451,7 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||
adap->algo_data = bit_data;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
adap->dev.parent = dev;
|
||||
adap->dev.of_node = np;
|
||||
device_set_node(&adap->dev, fwnode);
|
||||
|
||||
adap->nr = pdev->id;
|
||||
ret = i2c_bit_add_numbered_bus(adap);
|
||||
@ -489,10 +497,17 @@ static const struct of_device_id i2c_gpio_dt_ids[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
|
||||
|
||||
static const struct acpi_device_id i2c_gpio_acpi_match[] = {
|
||||
{ "LOON0005" }, /* LoongArch */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, i2c_gpio_acpi_match);
|
||||
|
||||
static struct platform_driver i2c_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-gpio",
|
||||
.of_match_table = i2c_gpio_dt_ids,
|
||||
.acpi_match_table = i2c_gpio_acpi_match,
|
||||
},
|
||||
.probe = i2c_gpio_probe,
|
||||
.remove = i2c_gpio_remove,
|
||||
|
620
drivers/i2c/busses/i2c-gxp.c
Normal file
620
drivers/i2c/busses/i2c-gxp.c
Normal file
@ -0,0 +1,620 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#define GXP_MAX_I2C_ENGINE 10
|
||||
static const char * const gxp_i2c_name[] = {
|
||||
"gxp-i2c0", "gxp-i2c1", "gxp-i2c2", "gxp-i2c3",
|
||||
"gxp-i2c4", "gxp-i2c5", "gxp-i2c6", "gxp-i2c7",
|
||||
"gxp-i2c8", "gxp-i2c9" };
|
||||
|
||||
/* GXP I2C Global interrupt status/enable register*/
|
||||
#define GXP_I2CINTSTAT 0x00
|
||||
#define GXP_I2CINTEN 0x04
|
||||
|
||||
/* GXP I2C registers */
|
||||
#define GXP_I2CSTAT 0x00
|
||||
#define MASK_STOP_EVENT 0x20
|
||||
#define MASK_ACK 0x08
|
||||
#define MASK_RW 0x04
|
||||
#define GXP_I2CEVTERR 0x01
|
||||
#define MASK_SLAVE_CMD_EVENT 0x01
|
||||
#define MASK_SLAVE_DATA_EVENT 0x02
|
||||
#define MASK_MASTER_EVENT 0x10
|
||||
#define GXP_I2CSNPDAT 0x02
|
||||
#define GXP_I2CMCMD 0x04
|
||||
#define GXP_I2CSCMD 0x06
|
||||
#define GXP_I2CSNPAA 0x09
|
||||
#define GXP_I2CADVFEAT 0x0A
|
||||
#define GXP_I2COWNADR 0x0B
|
||||
#define GXP_I2CFREQDIV 0x0C
|
||||
#define GXP_I2CFLTFAIR 0x0D
|
||||
#define GXP_I2CTMOEDG 0x0E
|
||||
#define GXP_I2CCYCTIM 0x0F
|
||||
|
||||
/* I2CSCMD Bits */
|
||||
#define SNOOP_EVT_CLR 0x80
|
||||
#define SLAVE_EVT_CLR 0x40
|
||||
#define SNOOP_EVT_MASK 0x20
|
||||
#define SLAVE_EVT_MASK 0x10
|
||||
#define SLAVE_ACK_ENAB 0x08
|
||||
#define SLAVE_EVT_STALL 0x01
|
||||
|
||||
/* I2CMCMD Bits */
|
||||
#define MASTER_EVT_CLR 0x80
|
||||
#define MASTER_ACK_ENAB 0x08
|
||||
#define RW_CMD 0x04
|
||||
#define STOP_CMD 0x02
|
||||
#define START_CMD 0x01
|
||||
|
||||
/* I2CTMOEDG value */
|
||||
#define GXP_DATA_EDGE_RST_CTRL 0x0a /* 30ns */
|
||||
|
||||
/* I2CFLTFAIR Bits */
|
||||
#define FILTER_CNT 0x30
|
||||
#define FAIRNESS_CNT 0x02
|
||||
|
||||
enum {
|
||||
GXP_I2C_IDLE = 0,
|
||||
GXP_I2C_ADDR_PHASE,
|
||||
GXP_I2C_RDATA_PHASE,
|
||||
GXP_I2C_WDATA_PHASE,
|
||||
GXP_I2C_ADDR_NACK,
|
||||
GXP_I2C_DATA_NACK,
|
||||
GXP_I2C_ERROR,
|
||||
GXP_I2C_COMP
|
||||
};
|
||||
|
||||
struct gxp_i2c_drvdata {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct i2c_timings t;
|
||||
u32 engine;
|
||||
int irq;
|
||||
struct completion completion;
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_msg *curr_msg;
|
||||
int msgs_remaining;
|
||||
int msgs_num;
|
||||
u8 *buf;
|
||||
size_t buf_remaining;
|
||||
unsigned char state;
|
||||
struct i2c_client *slave;
|
||||
unsigned char stopped;
|
||||
};
|
||||
|
||||
static struct regmap *i2cg_map;
|
||||
|
||||
static void gxp_i2c_start(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
u16 value;
|
||||
|
||||
drvdata->buf = drvdata->curr_msg->buf;
|
||||
drvdata->buf_remaining = drvdata->curr_msg->len;
|
||||
|
||||
/* Note: Address in struct i2c_msg is 7 bits */
|
||||
value = drvdata->curr_msg->addr << 9;
|
||||
|
||||
/* Read or Write */
|
||||
value |= drvdata->curr_msg->flags & I2C_M_RD ? RW_CMD | START_CMD : START_CMD;
|
||||
|
||||
drvdata->state = GXP_I2C_ADDR_PHASE;
|
||||
writew(value, drvdata->base + GXP_I2CMCMD);
|
||||
}
|
||||
|
||||
static int gxp_i2c_master_xfer(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret;
|
||||
struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(adapter);
|
||||
unsigned long time_left;
|
||||
|
||||
drvdata->msgs_remaining = num;
|
||||
drvdata->curr_msg = msgs;
|
||||
drvdata->msgs_num = num;
|
||||
reinit_completion(&drvdata->completion);
|
||||
|
||||
gxp_i2c_start(drvdata);
|
||||
|
||||
time_left = wait_for_completion_timeout(&drvdata->completion,
|
||||
adapter->timeout);
|
||||
ret = num - drvdata->msgs_remaining;
|
||||
if (time_left == 0) {
|
||||
switch (drvdata->state) {
|
||||
case GXP_I2C_WDATA_PHASE:
|
||||
break;
|
||||
case GXP_I2C_RDATA_PHASE:
|
||||
break;
|
||||
case GXP_I2C_ADDR_PHASE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (drvdata->state == GXP_I2C_ADDR_NACK ||
|
||||
drvdata->state == GXP_I2C_DATA_NACK)
|
||||
return -EIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 gxp_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_I2C_SLAVE))
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE;
|
||||
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
static int gxp_i2c_reg_slave(struct i2c_client *slave)
|
||||
{
|
||||
struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(slave->adapter);
|
||||
|
||||
if (drvdata->slave)
|
||||
return -EBUSY;
|
||||
|
||||
if (slave->flags & I2C_CLIENT_TEN)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
drvdata->slave = slave;
|
||||
|
||||
writeb(slave->addr << 1, drvdata->base + GXP_I2COWNADR);
|
||||
writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK | SLAVE_ACK_ENAB |
|
||||
SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gxp_i2c_unreg_slave(struct i2c_client *slave)
|
||||
{
|
||||
struct gxp_i2c_drvdata *drvdata = i2c_get_adapdata(slave->adapter);
|
||||
|
||||
WARN_ON(!drvdata->slave);
|
||||
|
||||
writeb(0x00, drvdata->base + GXP_I2COWNADR);
|
||||
writeb(SNOOP_EVT_CLR | SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_EVT_MASK, drvdata->base + GXP_I2CSCMD);
|
||||
|
||||
drvdata->slave = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct i2c_algorithm gxp_i2c_algo = {
|
||||
.master_xfer = gxp_i2c_master_xfer,
|
||||
.functionality = gxp_i2c_func,
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
.reg_slave = gxp_i2c_reg_slave,
|
||||
.unreg_slave = gxp_i2c_unreg_slave,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void gxp_i2c_stop(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
/* Clear event and send stop */
|
||||
writeb(MASTER_EVT_CLR | STOP_CMD, drvdata->base + GXP_I2CMCMD);
|
||||
|
||||
complete(&drvdata->completion);
|
||||
}
|
||||
|
||||
static void gxp_i2c_restart(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
u16 value;
|
||||
|
||||
drvdata->buf = drvdata->curr_msg->buf;
|
||||
drvdata->buf_remaining = drvdata->curr_msg->len;
|
||||
|
||||
value = drvdata->curr_msg->addr << 9;
|
||||
|
||||
if (drvdata->curr_msg->flags & I2C_M_RD) {
|
||||
/* Read and clear master event */
|
||||
value |= MASTER_EVT_CLR | RW_CMD | START_CMD;
|
||||
} else {
|
||||
/* Write and clear master event */
|
||||
value |= MASTER_EVT_CLR | START_CMD;
|
||||
}
|
||||
|
||||
drvdata->state = GXP_I2C_ADDR_PHASE;
|
||||
|
||||
writew(value, drvdata->base + GXP_I2CMCMD);
|
||||
}
|
||||
|
||||
static void gxp_i2c_chk_addr_ack(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
u16 value;
|
||||
|
||||
value = readb(drvdata->base + GXP_I2CSTAT);
|
||||
if (!(value & MASK_ACK)) {
|
||||
/* Got no ack, stop */
|
||||
drvdata->state = GXP_I2C_ADDR_NACK;
|
||||
gxp_i2c_stop(drvdata);
|
||||
return;
|
||||
}
|
||||
|
||||
if (drvdata->curr_msg->flags & I2C_M_RD) {
|
||||
/* Start to read data from slave */
|
||||
if (drvdata->buf_remaining == 0) {
|
||||
/* No more data to read, stop */
|
||||
drvdata->msgs_remaining--;
|
||||
drvdata->state = GXP_I2C_COMP;
|
||||
gxp_i2c_stop(drvdata);
|
||||
return;
|
||||
}
|
||||
drvdata->state = GXP_I2C_RDATA_PHASE;
|
||||
|
||||
if (drvdata->buf_remaining == 1) {
|
||||
/* The last data, do not ack */
|
||||
writeb(MASTER_EVT_CLR | RW_CMD,
|
||||
drvdata->base + GXP_I2CMCMD);
|
||||
} else {
|
||||
/* Read data and ack it */
|
||||
writeb(MASTER_EVT_CLR | MASTER_ACK_ENAB |
|
||||
RW_CMD, drvdata->base + GXP_I2CMCMD);
|
||||
}
|
||||
} else {
|
||||
/* Start to write first data to slave */
|
||||
if (drvdata->buf_remaining == 0) {
|
||||
/* No more data to write, stop */
|
||||
drvdata->msgs_remaining--;
|
||||
drvdata->state = GXP_I2C_COMP;
|
||||
gxp_i2c_stop(drvdata);
|
||||
return;
|
||||
}
|
||||
value = *drvdata->buf;
|
||||
value = value << 8;
|
||||
/* Clear master event */
|
||||
value |= MASTER_EVT_CLR;
|
||||
drvdata->buf++;
|
||||
drvdata->buf_remaining--;
|
||||
drvdata->state = GXP_I2C_WDATA_PHASE;
|
||||
writew(value, drvdata->base + GXP_I2CMCMD);
|
||||
}
|
||||
}
|
||||
|
||||
static void gxp_i2c_ack_data(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
/* Store the data returned */
|
||||
value = readb(drvdata->base + GXP_I2CSNPDAT);
|
||||
*drvdata->buf = value;
|
||||
drvdata->buf++;
|
||||
drvdata->buf_remaining--;
|
||||
|
||||
if (drvdata->buf_remaining == 0) {
|
||||
/* No more data, this message is completed. */
|
||||
drvdata->msgs_remaining--;
|
||||
|
||||
if (drvdata->msgs_remaining == 0) {
|
||||
/* No more messages, stop */
|
||||
drvdata->state = GXP_I2C_COMP;
|
||||
gxp_i2c_stop(drvdata);
|
||||
return;
|
||||
}
|
||||
/* Move to next message and start transfer */
|
||||
drvdata->curr_msg++;
|
||||
gxp_i2c_restart(drvdata);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ack the slave to make it send next byte */
|
||||
drvdata->state = GXP_I2C_RDATA_PHASE;
|
||||
if (drvdata->buf_remaining == 1) {
|
||||
/* The last data, do not ack */
|
||||
writeb(MASTER_EVT_CLR | RW_CMD,
|
||||
drvdata->base + GXP_I2CMCMD);
|
||||
} else {
|
||||
/* Read data and ack it */
|
||||
writeb(MASTER_EVT_CLR | MASTER_ACK_ENAB |
|
||||
RW_CMD, drvdata->base + GXP_I2CMCMD);
|
||||
}
|
||||
}
|
||||
|
||||
static void gxp_i2c_chk_data_ack(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
u16 value;
|
||||
|
||||
value = readb(drvdata->base + GXP_I2CSTAT);
|
||||
if (!(value & MASK_ACK)) {
|
||||
/* Received No ack, stop */
|
||||
drvdata->state = GXP_I2C_DATA_NACK;
|
||||
gxp_i2c_stop(drvdata);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Got ack, check if there is more data to write */
|
||||
if (drvdata->buf_remaining == 0) {
|
||||
/* No more data, this message is completed */
|
||||
drvdata->msgs_remaining--;
|
||||
|
||||
if (drvdata->msgs_remaining == 0) {
|
||||
/* No more messages, stop */
|
||||
drvdata->state = GXP_I2C_COMP;
|
||||
gxp_i2c_stop(drvdata);
|
||||
return;
|
||||
}
|
||||
/* Move to next message and start transfer */
|
||||
drvdata->curr_msg++;
|
||||
gxp_i2c_restart(drvdata);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write data to slave */
|
||||
value = *drvdata->buf;
|
||||
value = value << 8;
|
||||
|
||||
/* Clear master event */
|
||||
value |= MASTER_EVT_CLR;
|
||||
drvdata->buf++;
|
||||
drvdata->buf_remaining--;
|
||||
drvdata->state = GXP_I2C_WDATA_PHASE;
|
||||
writew(value, drvdata->base + GXP_I2CMCMD);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
static bool gxp_i2c_slave_irq_handler(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
u8 value;
|
||||
u8 buf;
|
||||
int ret;
|
||||
|
||||
value = readb(drvdata->base + GXP_I2CEVTERR);
|
||||
|
||||
/* Received start or stop event */
|
||||
if (value & MASK_SLAVE_CMD_EVENT) {
|
||||
value = readb(drvdata->base + GXP_I2CSTAT);
|
||||
/* Master sent stop */
|
||||
if (value & MASK_STOP_EVENT) {
|
||||
if (drvdata->stopped == 0)
|
||||
i2c_slave_event(drvdata->slave, I2C_SLAVE_STOP, &buf);
|
||||
writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_ACK_ENAB | SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
|
||||
drvdata->stopped = 1;
|
||||
} else {
|
||||
/* Master sent start and wants to read */
|
||||
drvdata->stopped = 0;
|
||||
if (value & MASK_RW) {
|
||||
i2c_slave_event(drvdata->slave,
|
||||
I2C_SLAVE_READ_REQUESTED, &buf);
|
||||
value = buf << 8 | (SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_EVT_STALL);
|
||||
writew(value, drvdata->base + GXP_I2CSCMD);
|
||||
} else {
|
||||
/* Master wants to write to us */
|
||||
ret = i2c_slave_event(drvdata->slave,
|
||||
I2C_SLAVE_WRITE_REQUESTED, &buf);
|
||||
if (!ret) {
|
||||
/* Ack next byte from master */
|
||||
writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_ACK_ENAB | SLAVE_EVT_STALL,
|
||||
drvdata->base + GXP_I2CSCMD);
|
||||
} else {
|
||||
/* Nack next byte from master */
|
||||
writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value & MASK_SLAVE_DATA_EVENT) {
|
||||
value = readb(drvdata->base + GXP_I2CSTAT);
|
||||
/* Master wants to read */
|
||||
if (value & MASK_RW) {
|
||||
/* Master wants another byte */
|
||||
if (value & MASK_ACK) {
|
||||
i2c_slave_event(drvdata->slave,
|
||||
I2C_SLAVE_READ_PROCESSED, &buf);
|
||||
value = buf << 8 | (SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_EVT_STALL);
|
||||
writew(value, drvdata->base + GXP_I2CSCMD);
|
||||
} else {
|
||||
/* No more bytes needed */
|
||||
writew(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_ACK_ENAB | SLAVE_EVT_STALL,
|
||||
drvdata->base + GXP_I2CSCMD);
|
||||
}
|
||||
} else {
|
||||
/* Master wants to write to us */
|
||||
value = readb(drvdata->base + GXP_I2CSNPDAT);
|
||||
buf = (uint8_t)value;
|
||||
ret = i2c_slave_event(drvdata->slave,
|
||||
I2C_SLAVE_WRITE_RECEIVED, &buf);
|
||||
if (!ret) {
|
||||
/* Ack next byte from master */
|
||||
writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_ACK_ENAB | SLAVE_EVT_STALL,
|
||||
drvdata->base + GXP_I2CSCMD);
|
||||
} else {
|
||||
/* Nack next byte from master */
|
||||
writeb(SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_EVT_STALL, drvdata->base + GXP_I2CSCMD);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static irqreturn_t gxp_i2c_irq_handler(int irq, void *_drvdata)
|
||||
{
|
||||
struct gxp_i2c_drvdata *drvdata = (struct gxp_i2c_drvdata *)_drvdata;
|
||||
u32 value;
|
||||
|
||||
/* Check if the interrupt is for the current engine */
|
||||
regmap_read(i2cg_map, GXP_I2CINTSTAT, &value);
|
||||
if (!(value & BIT(drvdata->engine)))
|
||||
return IRQ_NONE;
|
||||
|
||||
value = readb(drvdata->base + GXP_I2CEVTERR);
|
||||
|
||||
/* Error */
|
||||
if (value & ~(MASK_MASTER_EVENT | MASK_SLAVE_CMD_EVENT |
|
||||
MASK_SLAVE_DATA_EVENT)) {
|
||||
/* Clear all events */
|
||||
writeb(0x00, drvdata->base + GXP_I2CEVTERR);
|
||||
drvdata->state = GXP_I2C_ERROR;
|
||||
gxp_i2c_stop(drvdata);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_I2C_SLAVE)) {
|
||||
/* Slave mode */
|
||||
if (value & (MASK_SLAVE_CMD_EVENT | MASK_SLAVE_DATA_EVENT)) {
|
||||
if (gxp_i2c_slave_irq_handler(drvdata))
|
||||
return IRQ_HANDLED;
|
||||
return IRQ_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Master mode */
|
||||
switch (drvdata->state) {
|
||||
case GXP_I2C_ADDR_PHASE:
|
||||
gxp_i2c_chk_addr_ack(drvdata);
|
||||
break;
|
||||
|
||||
case GXP_I2C_RDATA_PHASE:
|
||||
gxp_i2c_ack_data(drvdata);
|
||||
break;
|
||||
|
||||
case GXP_I2C_WDATA_PHASE:
|
||||
gxp_i2c_chk_data_ack(drvdata);
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void gxp_i2c_init(struct gxp_i2c_drvdata *drvdata)
|
||||
{
|
||||
drvdata->state = GXP_I2C_IDLE;
|
||||
writeb(2000000 / drvdata->t.bus_freq_hz,
|
||||
drvdata->base + GXP_I2CFREQDIV);
|
||||
writeb(FILTER_CNT | FAIRNESS_CNT,
|
||||
drvdata->base + GXP_I2CFLTFAIR);
|
||||
writeb(GXP_DATA_EDGE_RST_CTRL, drvdata->base + GXP_I2CTMOEDG);
|
||||
writeb(0x00, drvdata->base + GXP_I2CCYCTIM);
|
||||
writeb(0x00, drvdata->base + GXP_I2CSNPAA);
|
||||
writeb(0x00, drvdata->base + GXP_I2CADVFEAT);
|
||||
writeb(SNOOP_EVT_CLR | SLAVE_EVT_CLR | SNOOP_EVT_MASK |
|
||||
SLAVE_EVT_MASK, drvdata->base + GXP_I2CSCMD);
|
||||
writeb(MASTER_EVT_CLR, drvdata->base + GXP_I2CMCMD);
|
||||
writeb(0x00, drvdata->base + GXP_I2CEVTERR);
|
||||
writeb(0x00, drvdata->base + GXP_I2COWNADR);
|
||||
}
|
||||
|
||||
static int gxp_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gxp_i2c_drvdata *drvdata;
|
||||
int rc;
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
if (!i2cg_map) {
|
||||
i2cg_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"hpe,sysreg");
|
||||
if (IS_ERR(i2cg_map)) {
|
||||
return dev_err_probe(&pdev->dev, IS_ERR(i2cg_map),
|
||||
"failed to map i2cg_handle\n");
|
||||
}
|
||||
|
||||
/* Disable interrupt */
|
||||
regmap_update_bits(i2cg_map, GXP_I2CINTEN, 0x00000FFF, 0);
|
||||
}
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata),
|
||||
GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
drvdata->dev = &pdev->dev;
|
||||
init_completion(&drvdata->completion);
|
||||
|
||||
drvdata->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(drvdata->base))
|
||||
return PTR_ERR(drvdata->base);
|
||||
|
||||
/* Use physical memory address to determine which I2C engine this is. */
|
||||
drvdata->engine = ((size_t)drvdata->base & 0xf00) >> 8;
|
||||
|
||||
if (drvdata->engine >= GXP_MAX_I2C_ENGINE) {
|
||||
return dev_err_probe(&pdev->dev, -EINVAL, "i2c engine% is unsupported\n",
|
||||
drvdata->engine);
|
||||
}
|
||||
|
||||
rc = platform_get_irq(pdev, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
drvdata->irq = rc;
|
||||
rc = devm_request_irq(&pdev->dev, drvdata->irq, gxp_i2c_irq_handler,
|
||||
IRQF_SHARED, gxp_i2c_name[drvdata->engine], drvdata);
|
||||
if (rc < 0)
|
||||
return dev_err_probe(&pdev->dev, rc, "irq request failed\n");
|
||||
|
||||
i2c_parse_fw_timings(&pdev->dev, &drvdata->t, true);
|
||||
|
||||
gxp_i2c_init(drvdata);
|
||||
|
||||
/* Enable interrupt */
|
||||
regmap_update_bits(i2cg_map, GXP_I2CINTEN, BIT(drvdata->engine),
|
||||
BIT(drvdata->engine));
|
||||
|
||||
adapter = &drvdata->adapter;
|
||||
i2c_set_adapdata(adapter, drvdata);
|
||||
|
||||
adapter->owner = THIS_MODULE;
|
||||
strscpy(adapter->name, "HPE GXP I2C adapter", sizeof(adapter->name));
|
||||
adapter->algo = &gxp_i2c_algo;
|
||||
adapter->dev.parent = &pdev->dev;
|
||||
adapter->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
rc = i2c_add_adapter(adapter);
|
||||
if (rc)
|
||||
return dev_err_probe(&pdev->dev, rc, "i2c add adapter failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gxp_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gxp_i2c_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
/* Disable interrupt */
|
||||
regmap_update_bits(i2cg_map, GXP_I2CINTEN, BIT(drvdata->engine), 0);
|
||||
i2c_del_adapter(&drvdata->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id gxp_i2c_of_match[] = {
|
||||
{ .compatible = "hpe,gxp-i2c" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gxp_i2c_of_match);
|
||||
|
||||
static struct platform_driver gxp_i2c_driver = {
|
||||
.probe = gxp_i2c_probe,
|
||||
.remove = gxp_i2c_remove,
|
||||
.driver = {
|
||||
.name = "gxp-i2c",
|
||||
.of_match_table = gxp_i2c_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(gxp_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>");
|
||||
MODULE_DESCRIPTION("HPE GXP I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -434,7 +434,7 @@ static int i801_wait_intr(struct i801_priv *priv)
|
||||
busy = status & SMBHSTSTS_HOST_BUSY;
|
||||
status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
|
||||
if (!busy && status)
|
||||
return status;
|
||||
return status & STATUS_ERROR_FLAGS;
|
||||
} while (time_is_after_eq_jiffies(timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
@ -458,26 +458,20 @@ static int i801_wait_byte_done(struct i801_priv *priv)
|
||||
|
||||
static int i801_transaction(struct i801_priv *priv, int xact)
|
||||
{
|
||||
int status;
|
||||
unsigned long result;
|
||||
const struct i2c_adapter *adap = &priv->adapter;
|
||||
|
||||
status = i801_check_pre(priv);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (priv->features & FEATURE_IRQ) {
|
||||
reinit_completion(&priv->done);
|
||||
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
|
||||
SMBHSTCNT(priv));
|
||||
result = wait_for_completion_timeout(&priv->done, adap->timeout);
|
||||
return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
|
||||
return result ? priv->status : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
|
||||
|
||||
status = i801_wait_intr(priv);
|
||||
return i801_check_post(priv, status);
|
||||
return i801_wait_intr(priv);
|
||||
}
|
||||
|
||||
static int i801_block_transaction_by_block(struct i801_priv *priv,
|
||||
@ -511,19 +505,23 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
|
||||
|
||||
status = i801_transaction(priv, xact);
|
||||
if (status)
|
||||
return status;
|
||||
goto out;
|
||||
|
||||
if (read_write == I2C_SMBUS_READ ||
|
||||
command == I2C_SMBUS_BLOCK_PROC_CALL) {
|
||||
len = inb_p(SMBHSTDAT0(priv));
|
||||
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EPROTO;
|
||||
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
|
||||
status = -EPROTO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->block[0] = len;
|
||||
for (i = 0; i < len; i++)
|
||||
data->block[i + 1] = inb_p(SMBBLKDAT(priv));
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) & ~SMBAUXCTL_E32B, SMBAUXCTL(priv));
|
||||
return status;
|
||||
}
|
||||
|
||||
static void i801_isr_byte_done(struct i801_priv *priv)
|
||||
@ -558,9 +556,6 @@ static void i801_isr_byte_done(struct i801_priv *priv)
|
||||
/* Write next byte, except for IRQ after last byte */
|
||||
outb_p(priv->data[++priv->count], SMBBLKDAT(priv));
|
||||
}
|
||||
|
||||
/* Clear BYTE_DONE to continue with next byte */
|
||||
outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
|
||||
}
|
||||
|
||||
static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
|
||||
@ -590,7 +585,6 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
|
||||
* BUS_ERR - SMI# transaction collision
|
||||
* FAILED - transaction was canceled due to a KILL request
|
||||
* When any of these occur, update ->status and signal completion.
|
||||
* ->status must be cleared before kicking off the next transaction.
|
||||
*
|
||||
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
|
||||
* occurs for each byte of a byte-by-byte to prepare the next byte.
|
||||
@ -615,25 +609,20 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
status = inb_p(SMBHSTSTS(priv));
|
||||
if (status & SMBHSTSTS_BYTE_DONE)
|
||||
if ((status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) == SMBHSTSTS_BYTE_DONE)
|
||||
i801_isr_byte_done(priv);
|
||||
|
||||
/*
|
||||
* Clear remaining IRQ sources: Completion of last command, errors
|
||||
* and the SMB_ALERT signal. SMB_ALERT status is set after signal
|
||||
* assertion independently of the interrupt generation being blocked
|
||||
* or not so clear it always when the status is set.
|
||||
*/
|
||||
status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS | SMBHSTSTS_SMBALERT_STS;
|
||||
if (status)
|
||||
outb_p(status, SMBHSTSTS(priv));
|
||||
status &= ~SMBHSTSTS_SMBALERT_STS; /* SMB_ALERT not reported */
|
||||
/*
|
||||
* Report transaction result.
|
||||
* ->status must be cleared before the next transaction is started.
|
||||
* Clear IRQ sources: SMB_ALERT status is set after signal assertion
|
||||
* independently of the interrupt generation being blocked or not
|
||||
* so clear it always when the status is set.
|
||||
*/
|
||||
status &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS;
|
||||
outb_p(status, SMBHSTSTS(priv));
|
||||
|
||||
status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
|
||||
if (status) {
|
||||
priv->status = status;
|
||||
priv->status = status & STATUS_ERROR_FLAGS;
|
||||
complete(&priv->done);
|
||||
}
|
||||
|
||||
@ -658,10 +647,6 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
|
||||
if (command == I2C_SMBUS_BLOCK_PROC_CALL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
status = i801_check_pre(priv);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
len = data->block[0];
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
@ -687,7 +672,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
|
||||
reinit_completion(&priv->done);
|
||||
outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
|
||||
result = wait_for_completion_timeout(&priv->done, adap->timeout);
|
||||
return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
|
||||
return result ? priv->status : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
for (i = 1; i <= len; i++) {
|
||||
@ -701,7 +686,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
|
||||
|
||||
status = i801_wait_byte_done(priv);
|
||||
if (status)
|
||||
goto exit;
|
||||
return status;
|
||||
|
||||
if (i == 1 && read_write == I2C_SMBUS_READ
|
||||
&& command != I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||
@ -731,14 +716,82 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
|
||||
outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
|
||||
}
|
||||
|
||||
status = i801_wait_intr(priv);
|
||||
exit:
|
||||
return i801_check_post(priv, status);
|
||||
return i801_wait_intr(priv);
|
||||
}
|
||||
|
||||
static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
|
||||
{
|
||||
outb_p((addr << 1) | (read_write & 0x01), SMBHSTADD(priv));
|
||||
}
|
||||
|
||||
/* Single value transaction function */
|
||||
static int i801_simple_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
|
||||
u8 addr, u8 hstcmd, char read_write, int command)
|
||||
{
|
||||
int xact, ret;
|
||||
|
||||
switch (command) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
i801_set_hstadd(priv, addr, read_write);
|
||||
xact = I801_QUICK;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE:
|
||||
i801_set_hstadd(priv, addr, read_write);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
outb_p(hstcmd, SMBHSTCMD(priv));
|
||||
xact = I801_BYTE;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
i801_set_hstadd(priv, addr, read_write);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
outb_p(data->byte, SMBHSTDAT0(priv));
|
||||
outb_p(hstcmd, SMBHSTCMD(priv));
|
||||
xact = I801_BYTE_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
i801_set_hstadd(priv, addr, read_write);
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
outb_p(data->word & 0xff, SMBHSTDAT0(priv));
|
||||
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
|
||||
}
|
||||
outb_p(hstcmd, SMBHSTCMD(priv));
|
||||
xact = I801_WORD_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
|
||||
outb_p(data->word & 0xff, SMBHSTDAT0(priv));
|
||||
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
|
||||
outb_p(hstcmd, SMBHSTCMD(priv));
|
||||
read_write = I2C_SMBUS_READ;
|
||||
xact = I801_PROC_CALL;
|
||||
break;
|
||||
default:
|
||||
pci_err(priv->pci_dev, "Unsupported transaction %d\n", command);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ret = i801_transaction(priv, xact);
|
||||
if (ret || read_write == I2C_SMBUS_WRITE)
|
||||
return ret;
|
||||
|
||||
switch (command) {
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
data->byte = inb_p(SMBHSTDAT0(priv));
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
data->word = inb_p(SMBHSTDAT0(priv)) +
|
||||
(inb_p(SMBHSTDAT1(priv)) << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Block transaction function */
|
||||
static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
|
||||
char read_write, int command)
|
||||
u8 addr, u8 hstcmd, char read_write, int command)
|
||||
{
|
||||
int result = 0;
|
||||
unsigned char hostc;
|
||||
@ -748,7 +801,29 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
|
||||
else if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EPROTO;
|
||||
|
||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||
switch (command) {
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
i801_set_hstadd(priv, addr, read_write);
|
||||
outb_p(hstcmd, SMBHSTCMD(priv));
|
||||
break;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
/*
|
||||
* NB: page 240 of ICH5 datasheet shows that the R/#W
|
||||
* bit should be cleared here, even when reading.
|
||||
* However if SPD Write Disable is set (Lynx Point and later),
|
||||
* the read will fail if we don't set the R/#W bit.
|
||||
*/
|
||||
i801_set_hstadd(priv, addr,
|
||||
priv->original_hstcfg & SMBHSTCFG_SPD_WD ?
|
||||
read_write : I2C_SMBUS_WRITE);
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
/* NB: page 240 of ICH5 datasheet also shows
|
||||
* that DATA1 is the cmd field when reading
|
||||
*/
|
||||
outb_p(hstcmd, SMBHSTDAT1(priv));
|
||||
} else
|
||||
outb_p(hstcmd, SMBHSTCMD(priv));
|
||||
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
/* set I2C_EN bit in configuration register */
|
||||
pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc);
|
||||
@ -759,6 +834,12 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
|
||||
"I2C block read is unsupported!\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
/* Needs to be flagged as write transaction */
|
||||
i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
|
||||
outb_p(hstcmd, SMBHSTCMD(priv));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Experience has shown that the block buffer can only be used for
|
||||
@ -787,9 +868,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write, u8 command,
|
||||
int size, union i2c_smbus_data *data)
|
||||
{
|
||||
int hwpec;
|
||||
int block = 0;
|
||||
int ret, xact;
|
||||
int hwpec, ret;
|
||||
struct i801_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
mutex_lock(&priv->acpi_lock);
|
||||
@ -800,127 +879,34 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
||||
|
||||
pm_runtime_get_sync(&priv->pci_dev->dev);
|
||||
|
||||
ret = i801_check_pre(priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
|
||||
&& size != I2C_SMBUS_QUICK
|
||||
&& size != I2C_SMBUS_I2C_BLOCK_DATA;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||
SMBHSTADD(priv));
|
||||
xact = I801_QUICK;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||
SMBHSTADD(priv));
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
outb_p(command, SMBHSTCMD(priv));
|
||||
xact = I801_BYTE;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||
SMBHSTADD(priv));
|
||||
outb_p(command, SMBHSTCMD(priv));
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
outb_p(data->byte, SMBHSTDAT0(priv));
|
||||
xact = I801_BYTE_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||
SMBHSTADD(priv));
|
||||
outb_p(command, SMBHSTCMD(priv));
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
outb_p(data->word & 0xff, SMBHSTDAT0(priv));
|
||||
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
|
||||
}
|
||||
xact = I801_WORD_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
|
||||
outb_p(command, SMBHSTCMD(priv));
|
||||
outb_p(data->word & 0xff, SMBHSTDAT0(priv));
|
||||
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
|
||||
xact = I801_PROC_CALL;
|
||||
read_write = I2C_SMBUS_READ;
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||
SMBHSTADD(priv));
|
||||
outb_p(command, SMBHSTCMD(priv));
|
||||
block = 1;
|
||||
break;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
/*
|
||||
* NB: page 240 of ICH5 datasheet shows that the R/#W
|
||||
* bit should be cleared here, even when reading.
|
||||
* However if SPD Write Disable is set (Lynx Point and later),
|
||||
* the read will fail if we don't set the R/#W bit.
|
||||
*/
|
||||
outb_p(((addr & 0x7f) << 1) |
|
||||
((priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
|
||||
(read_write & 0x01) : 0),
|
||||
SMBHSTADD(priv));
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
/* NB: page 240 of ICH5 datasheet also shows
|
||||
* that DATA1 is the cmd field when reading */
|
||||
outb_p(command, SMBHSTDAT1(priv));
|
||||
} else
|
||||
outb_p(command, SMBHSTCMD(priv));
|
||||
block = 1;
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
/*
|
||||
* Bit 0 of the slave address register always indicate a write
|
||||
* command.
|
||||
*/
|
||||
outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
|
||||
outb_p(command, SMBHSTCMD(priv));
|
||||
block = 1;
|
||||
break;
|
||||
default:
|
||||
dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n",
|
||||
size);
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (hwpec) /* enable/disable hardware PEC */
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv));
|
||||
else
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC),
|
||||
SMBAUXCTL(priv));
|
||||
|
||||
if (block)
|
||||
ret = i801_block_transaction(priv, data, read_write, size);
|
||||
if (size == I2C_SMBUS_BLOCK_DATA ||
|
||||
size == I2C_SMBUS_I2C_BLOCK_DATA ||
|
||||
size == I2C_SMBUS_BLOCK_PROC_CALL)
|
||||
ret = i801_block_transaction(priv, data, addr, command, read_write, size);
|
||||
else
|
||||
ret = i801_transaction(priv, xact);
|
||||
ret = i801_simple_transaction(priv, data, addr, command, read_write, size);
|
||||
|
||||
ret = i801_check_post(priv, ret);
|
||||
|
||||
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
|
||||
time, so we forcibly disable it after every transaction. Turn off
|
||||
E32B for the same reason. */
|
||||
if (hwpec || block)
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) &
|
||||
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
|
||||
|
||||
if (block)
|
||||
goto out;
|
||||
if (ret)
|
||||
goto out;
|
||||
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
|
||||
goto out;
|
||||
|
||||
switch (xact) {
|
||||
case I801_BYTE: /* Result put in SMBHSTDAT0 */
|
||||
case I801_BYTE_DATA:
|
||||
data->byte = inb_p(SMBHSTDAT0(priv));
|
||||
break;
|
||||
case I801_WORD_DATA:
|
||||
case I801_PROC_CALL:
|
||||
data->word = inb_p(SMBHSTDAT0(priv)) +
|
||||
(inb_p(SMBHSTDAT1(priv)) << 8);
|
||||
break;
|
||||
}
|
||||
|
||||
* time, so we forcibly disable it after every transaction.
|
||||
*/
|
||||
if (hwpec)
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv));
|
||||
out:
|
||||
/*
|
||||
* Unlock the SMBus device for use by BIOS/ACPI,
|
||||
@ -1667,6 +1653,10 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
}
|
||||
priv->features &= ~disable_features;
|
||||
|
||||
/* The block process call uses block buffer mode */
|
||||
if (!(priv->features & FEATURE_BLOCK_BUFFER))
|
||||
priv->features &= ~FEATURE_BLOCK_PROC;
|
||||
|
||||
err = pcim_enable_device(dev);
|
||||
if (err) {
|
||||
dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n",
|
||||
@ -1714,11 +1704,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
outb_p(inb_p(SMBAUXCTL(priv)) &
|
||||
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
|
||||
|
||||
/* Remember original Interrupt and Host Notify settings */
|
||||
priv->original_hstcnt = inb_p(SMBHSTCNT(priv)) & ~SMBHSTCNT_KILL;
|
||||
if (priv->features & FEATURE_HOST_NOTIFY)
|
||||
priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
|
||||
|
||||
/* Default timeout in interrupt mode: 200 ms */
|
||||
priv->adapter.timeout = HZ / 5;
|
||||
|
||||
@ -1748,6 +1733,15 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
dev_info(&dev->dev, "SMBus using %s\n",
|
||||
priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
|
||||
|
||||
/* Host notification uses an interrupt */
|
||||
if (!(priv->features & FEATURE_IRQ))
|
||||
priv->features &= ~FEATURE_HOST_NOTIFY;
|
||||
|
||||
/* Remember original Interrupt and Host Notify settings */
|
||||
priv->original_hstcnt = inb_p(SMBHSTCNT(priv)) & ~SMBHSTCNT_KILL;
|
||||
if (priv->features & FEATURE_HOST_NOTIFY)
|
||||
priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
|
||||
|
||||
i801_add_tco(priv);
|
||||
|
||||
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
|
||||
|
370
drivers/i2c/busses/i2c-ls2x.c
Normal file
370
drivers/i2c/busses/i2c-ls2x.c
Normal file
@ -0,0 +1,370 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Loongson-2K/Loongson LS7A I2C master mode driver
|
||||
*
|
||||
* Copyright (C) 2013 Loongson Technology Corporation Limited.
|
||||
* Copyright (C) 2014-2017 Lemote, Inc.
|
||||
* Copyright (C) 2018-2022 Loongson Technology Corporation Limited.
|
||||
*
|
||||
* Originally written by liushaozong
|
||||
* Rewritten for mainline by Binbin Zhou <zhoubinbin@loongson.cn>
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
/* I2C Registers */
|
||||
#define I2C_LS2X_PRER 0x0 /* Freq Division Register(16 bits) */
|
||||
#define I2C_LS2X_CTR 0x2 /* Control Register */
|
||||
#define I2C_LS2X_TXR 0x3 /* Transport Data Register */
|
||||
#define I2C_LS2X_RXR 0x3 /* Receive Data Register */
|
||||
#define I2C_LS2X_CR 0x4 /* Command Control Register */
|
||||
#define I2C_LS2X_SR 0x4 /* State Register */
|
||||
|
||||
/* Command Control Register Bit */
|
||||
#define LS2X_CR_START BIT(7) /* Start signal */
|
||||
#define LS2X_CR_STOP BIT(6) /* Stop signal */
|
||||
#define LS2X_CR_READ BIT(5) /* Read signal */
|
||||
#define LS2X_CR_WRITE BIT(4) /* Write signal */
|
||||
#define LS2X_CR_ACK BIT(3) /* Response signal */
|
||||
#define LS2X_CR_IACK BIT(0) /* Interrupt response signal */
|
||||
|
||||
/* State Register Bit */
|
||||
#define LS2X_SR_NOACK BIT(7) /* Receive NACK */
|
||||
#define LS2X_SR_BUSY BIT(6) /* Bus busy state */
|
||||
#define LS2X_SR_AL BIT(5) /* Arbitration lost */
|
||||
#define LS2X_SR_TIP BIT(1) /* Transmission state */
|
||||
#define LS2X_SR_IF BIT(0) /* Interrupt flag */
|
||||
|
||||
/* Control Register Bit */
|
||||
#define LS2X_CTR_EN BIT(7) /* 0: I2c frequency setting 1: Normal */
|
||||
#define LS2X_CTR_IEN BIT(6) /* Enable i2c interrupt */
|
||||
#define LS2X_CTR_MST BIT(5) /* 0: Slave mode 1: Master mode */
|
||||
#define CTR_FREQ_MASK GENMASK(7, 6)
|
||||
#define CTR_READY_MASK GENMASK(7, 5)
|
||||
|
||||
/* The PCLK frequency from LPB */
|
||||
#define LS2X_I2C_PCLK_FREQ (50 * HZ_PER_MHZ)
|
||||
|
||||
/* The default bus frequency, which is an empirical value */
|
||||
#define LS2X_I2C_FREQ_STD (33 * HZ_PER_KHZ)
|
||||
|
||||
struct ls2x_i2c_priv {
|
||||
struct i2c_adapter adapter;
|
||||
void __iomem *base;
|
||||
struct i2c_timings i2c_t;
|
||||
struct completion cmd_complete;
|
||||
};
|
||||
|
||||
/*
|
||||
* Interrupt service routine.
|
||||
* This gets called whenever an I2C interrupt occurs.
|
||||
*/
|
||||
static irqreturn_t ls2x_i2c_isr(int this_irq, void *dev_id)
|
||||
{
|
||||
struct ls2x_i2c_priv *priv = dev_id;
|
||||
|
||||
if (!(readb(priv->base + I2C_LS2X_SR) & LS2X_SR_IF))
|
||||
return IRQ_NONE;
|
||||
|
||||
writeb(LS2X_CR_IACK, priv->base + I2C_LS2X_CR);
|
||||
complete(&priv->cmd_complete);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* The ls2x i2c controller supports standard mode and fast mode, so the
|
||||
* maximum bus frequency is '400kHz'.
|
||||
* The bus frequency is set to the empirical value of '33KHz' by default,
|
||||
* but it can also be taken from ACPI or FDT for compatibility with more
|
||||
* devices.
|
||||
*/
|
||||
static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
|
||||
{
|
||||
struct i2c_timings *t = &priv->i2c_t;
|
||||
struct device *dev = priv->adapter.dev.parent;
|
||||
u32 acpi_speed = i2c_acpi_find_bus_speed(dev);
|
||||
|
||||
i2c_parse_fw_timings(dev, t, false);
|
||||
|
||||
if (acpi_speed || t->bus_freq_hz)
|
||||
t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
|
||||
else
|
||||
t->bus_freq_hz = LS2X_I2C_FREQ_STD;
|
||||
|
||||
/* Calculate and set i2c frequency. */
|
||||
writew(LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1,
|
||||
priv->base + I2C_LS2X_PRER);
|
||||
}
|
||||
|
||||
static void ls2x_i2c_init(struct ls2x_i2c_priv *priv)
|
||||
{
|
||||
/* Set i2c frequency setting mode and disable interrupts. */
|
||||
writeb(readb(priv->base + I2C_LS2X_CTR) & ~CTR_FREQ_MASK,
|
||||
priv->base + I2C_LS2X_CTR);
|
||||
|
||||
ls2x_i2c_adjust_bus_speed(priv);
|
||||
|
||||
/* Set i2c normal operating mode and enable interrupts. */
|
||||
writeb(readb(priv->base + I2C_LS2X_CTR) | CTR_READY_MASK,
|
||||
priv->base + I2C_LS2X_CTR);
|
||||
}
|
||||
|
||||
static int ls2x_i2c_xfer_byte(struct ls2x_i2c_priv *priv, u8 txdata, u8 *rxdatap)
|
||||
{
|
||||
u8 rxdata;
|
||||
unsigned long time_left;
|
||||
|
||||
writeb(txdata, priv->base + I2C_LS2X_CR);
|
||||
|
||||
time_left = wait_for_completion_timeout(&priv->cmd_complete,
|
||||
priv->adapter.timeout);
|
||||
if (!time_left)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
rxdata = readb(priv->base + I2C_LS2X_SR);
|
||||
if (rxdatap)
|
||||
*rxdatap = rxdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_i2c_send_byte(struct ls2x_i2c_priv *priv, u8 txdata)
|
||||
{
|
||||
int ret;
|
||||
u8 rxdata;
|
||||
|
||||
ret = ls2x_i2c_xfer_byte(priv, txdata, &rxdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rxdata & LS2X_SR_AL)
|
||||
return -EAGAIN;
|
||||
|
||||
if (rxdata & LS2X_SR_NOACK)
|
||||
return -ENXIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_i2c_stop(struct ls2x_i2c_priv *priv)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
writeb(LS2X_CR_STOP, priv->base + I2C_LS2X_CR);
|
||||
return readb_poll_timeout(priv->base + I2C_LS2X_SR, value,
|
||||
!(value & LS2X_SR_BUSY), 100,
|
||||
jiffies_to_usecs(priv->adapter.timeout));
|
||||
}
|
||||
|
||||
static int ls2x_i2c_start(struct ls2x_i2c_priv *priv, struct i2c_msg *msgs)
|
||||
{
|
||||
reinit_completion(&priv->cmd_complete);
|
||||
|
||||
writeb(i2c_8bit_addr_from_msg(msgs), priv->base + I2C_LS2X_TXR);
|
||||
return ls2x_i2c_send_byte(priv, LS2X_CR_START | LS2X_CR_WRITE);
|
||||
}
|
||||
|
||||
static int ls2x_i2c_rx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
|
||||
{
|
||||
int ret;
|
||||
u8 rxdata, *buf = msg->buf;
|
||||
u16 len = msg->len;
|
||||
|
||||
/* Contains steps to send start condition and address. */
|
||||
ret = ls2x_i2c_start(priv, msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (len--) {
|
||||
ret = ls2x_i2c_xfer_byte(priv,
|
||||
LS2X_CR_READ | (len ? 0 : LS2X_CR_ACK),
|
||||
&rxdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*buf++ = readb(priv->base + I2C_LS2X_RXR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_i2c_tx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
|
||||
{
|
||||
int ret;
|
||||
u8 *buf = msg->buf;
|
||||
u16 len = msg->len;
|
||||
|
||||
/* Contains steps to send start condition and address. */
|
||||
ret = ls2x_i2c_start(priv, msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (len--) {
|
||||
writeb(*buf++, priv->base + I2C_LS2X_TXR);
|
||||
|
||||
ret = ls2x_i2c_send_byte(priv, LS2X_CR_WRITE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_i2c_xfer_one(struct ls2x_i2c_priv *priv,
|
||||
struct i2c_msg *msg, bool stop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
ret = ls2x_i2c_rx(priv, msg);
|
||||
else
|
||||
ret = ls2x_i2c_tx(priv, msg);
|
||||
|
||||
if (ret < 0) {
|
||||
/* Fatel error. Needs reinit. */
|
||||
if (ret == -ETIMEDOUT)
|
||||
ls2x_i2c_init(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
/* Failed to issue STOP. Needs reinit. */
|
||||
ret = ls2x_i2c_stop(priv);
|
||||
if (ret)
|
||||
ls2x_i2c_init(priv);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ls2x_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_msg *msg, *emsg = msgs + num;
|
||||
struct ls2x_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
for (msg = msgs; msg < emsg; msg++) {
|
||||
ret = ls2x_i2c_xfer_one(priv, msg, msg == emsg - 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static unsigned int ls2x_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm ls2x_i2c_algo = {
|
||||
.master_xfer = ls2x_i2c_master_xfer,
|
||||
.functionality = ls2x_i2c_func,
|
||||
};
|
||||
|
||||
static int ls2x_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret, irq;
|
||||
struct i2c_adapter *adap;
|
||||
struct ls2x_i2c_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Map hardware registers */
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
/* Add the i2c adapter */
|
||||
adap = &priv->adapter;
|
||||
adap->retries = 5;
|
||||
adap->nr = pdev->id;
|
||||
adap->dev.parent = dev;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->algo = &ls2x_i2c_algo;
|
||||
adap->timeout = msecs_to_jiffies(100);
|
||||
device_set_node(&adap->dev, dev_fwnode(dev));
|
||||
i2c_set_adapdata(adap, priv);
|
||||
strscpy(adap->name, pdev->name, sizeof(adap->name));
|
||||
init_completion(&priv->cmd_complete);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ls2x_i2c_init(priv);
|
||||
|
||||
ret = devm_request_irq(dev, irq, ls2x_i2c_isr, IRQF_SHARED, "ls2x-i2c",
|
||||
priv);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
|
||||
|
||||
return devm_i2c_add_adapter(dev, adap);
|
||||
}
|
||||
|
||||
static int ls2x_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct ls2x_i2c_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
/* Disable interrupts */
|
||||
writeb(readb(priv->base + I2C_LS2X_CTR) & ~LS2X_CTR_IEN,
|
||||
priv->base + I2C_LS2X_CTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls2x_i2c_resume(struct device *dev)
|
||||
{
|
||||
ls2x_i2c_init(dev_get_drvdata(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_RUNTIME_DEV_PM_OPS(ls2x_i2c_pm_ops,
|
||||
ls2x_i2c_suspend, ls2x_i2c_resume, NULL);
|
||||
|
||||
static const struct of_device_id ls2x_i2c_id_table[] = {
|
||||
{ .compatible = "loongson,ls2k-i2c" },
|
||||
{ .compatible = "loongson,ls7a-i2c" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table);
|
||||
|
||||
static const struct acpi_device_id ls2x_i2c_acpi_match[] = {
|
||||
{ "LOON0004" }, /* Loongson LS7A */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ls2x_i2c_acpi_match);
|
||||
|
||||
static struct platform_driver ls2x_i2c_driver = {
|
||||
.probe = ls2x_i2c_probe,
|
||||
.driver = {
|
||||
.name = "ls2x-i2c",
|
||||
.pm = pm_sleep_ptr(&ls2x_i2c_pm_ops),
|
||||
.of_match_table = ls2x_i2c_id_table,
|
||||
.acpi_match_table = ls2x_i2c_acpi_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ls2x_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Loongson LS2X I2C Bus driver");
|
||||
MODULE_AUTHOR("Loongson Technology Corporation Limited");
|
||||
MODULE_LICENSE("GPL");
|
@ -1366,20 +1366,17 @@ static int mtk_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mtk_i2c *i2c;
|
||||
struct resource *res;
|
||||
int i, irq, speed_clk;
|
||||
|
||||
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
i2c->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
i2c->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(i2c->base))
|
||||
return PTR_ERR(i2c->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
|
||||
i2c->pdmabase = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
|
||||
if (IS_ERR(i2c->pdmabase))
|
||||
return PTR_ERR(i2c->pdmabase);
|
||||
|
||||
|
@ -811,9 +811,15 @@ static const struct cci_data cci_v2_data = {
|
||||
|
||||
static const struct of_device_id cci_dt_match[] = {
|
||||
{ .compatible = "qcom,msm8226-cci", .data = &cci_v1_data},
|
||||
{ .compatible = "qcom,msm8916-cci", .data = &cci_v1_data},
|
||||
{ .compatible = "qcom,msm8974-cci", .data = &cci_v1_5_data},
|
||||
{ .compatible = "qcom,msm8996-cci", .data = &cci_v2_data},
|
||||
|
||||
|
||||
/*
|
||||
* Legacy compatibles kept for backwards compatibility.
|
||||
* Do not add any new ones unless they introduce a new config
|
||||
*/
|
||||
{ .compatible = "qcom,msm8916-cci", .data = &cci_v1_data},
|
||||
{ .compatible = "qcom,sdm845-cci", .data = &cci_v2_data},
|
||||
{ .compatible = "qcom,sm8250-cci", .data = &cci_v2_data},
|
||||
{ .compatible = "qcom,sm8450-cci", .data = &cci_v2_data},
|
||||
|
@ -1025,7 +1025,7 @@ static const struct dev_pm_ops geni_i2c_pm_ops = {
|
||||
NULL)
|
||||
};
|
||||
|
||||
const struct geni_i2c_desc i2c_master_hub = {
|
||||
static const struct geni_i2c_desc i2c_master_hub = {
|
||||
.has_core_clk = true,
|
||||
.icc_ddr = NULL,
|
||||
.no_dma_support = true,
|
||||
|
@ -740,7 +740,6 @@ static int st_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
return (ret < 0) ? ret : i;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int st_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct st_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||
@ -762,11 +761,7 @@ static int st_i2c_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(st_i2c_pm, st_i2c_suspend, st_i2c_resume);
|
||||
#define ST_I2C_PM (&st_i2c_pm)
|
||||
#else
|
||||
#define ST_I2C_PM NULL
|
||||
#endif
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(st_i2c_pm, st_i2c_suspend, st_i2c_resume);
|
||||
|
||||
static u32 st_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
@ -901,7 +896,7 @@ static struct platform_driver st_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "st-i2c",
|
||||
.of_match_table = st_i2c_match,
|
||||
.pm = ST_I2C_PM,
|
||||
.pm = pm_sleep_ptr(&st_i2c_pm),
|
||||
},
|
||||
.probe = st_i2c_probe,
|
||||
.remove = st_i2c_remove,
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define DRIVER_NAME "xiic-i2c"
|
||||
#define DYNAMIC_MODE_READ_BROKEN_BIT BIT(0)
|
||||
#define SMBUS_BLOCK_READ_MIN_LEN 3
|
||||
|
||||
enum xilinx_i2c_state {
|
||||
STATE_DONE,
|
||||
@ -44,6 +46,12 @@ enum xiic_endian {
|
||||
BIG
|
||||
};
|
||||
|
||||
enum i2c_scl_freq {
|
||||
REG_VALUES_100KHZ = 0,
|
||||
REG_VALUES_400KHZ = 1,
|
||||
REG_VALUES_1MHZ = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* struct xiic_i2c - Internal representation of the XIIC I2C bus
|
||||
* @dev: Pointer to device structure
|
||||
@ -60,6 +68,12 @@ enum xiic_endian {
|
||||
* @clk: Pointer to AXI4-lite input clock
|
||||
* @state: See STATE_
|
||||
* @singlemaster: Indicates bus is single master
|
||||
* @dynamic: Mode of controller
|
||||
* @prev_msg_tx: Previous message is Tx
|
||||
* @quirks: To hold platform specific bug info
|
||||
* @smbus_block_read: Flag to handle block read
|
||||
* @input_clk: Input clock to I2C controller
|
||||
* @i2c_clk: I2C SCL frequency
|
||||
*/
|
||||
struct xiic_i2c {
|
||||
struct device *dev;
|
||||
@ -76,6 +90,39 @@ struct xiic_i2c {
|
||||
struct clk *clk;
|
||||
enum xilinx_i2c_state state;
|
||||
bool singlemaster;
|
||||
bool dynamic;
|
||||
bool prev_msg_tx;
|
||||
u32 quirks;
|
||||
bool smbus_block_read;
|
||||
unsigned long input_clk;
|
||||
unsigned int i2c_clk;
|
||||
};
|
||||
|
||||
struct xiic_version_data {
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct timing_regs - AXI I2C timing registers that depend on I2C spec
|
||||
* @tsusta: setup time for a repeated START condition
|
||||
* @tsusto: setup time for a STOP condition
|
||||
* @thdsta: hold time for a repeated START condition
|
||||
* @tsudat: setup time for data
|
||||
* @tbuf: bus free time between STOP and START
|
||||
*/
|
||||
struct timing_regs {
|
||||
unsigned int tsusta;
|
||||
unsigned int tsusto;
|
||||
unsigned int thdsta;
|
||||
unsigned int tsudat;
|
||||
unsigned int tbuf;
|
||||
};
|
||||
|
||||
/* Reg values in ns derived from I2C spec and AXI I2C PG for different frequencies */
|
||||
static const struct timing_regs timing_reg_values[] = {
|
||||
{ 5700, 5000, 4300, 550, 5000 }, /* Reg values for 100KHz */
|
||||
{ 900, 900, 900, 400, 1600 }, /* Reg values for 400KHz */
|
||||
{ 380, 380, 380, 170, 620 }, /* Reg values for 1MHz */
|
||||
};
|
||||
|
||||
#define XIIC_MSB_OFFSET 0
|
||||
@ -96,6 +143,19 @@ struct xiic_i2c {
|
||||
#define XIIC_RFD_REG_OFFSET (0x20 + XIIC_REG_OFFSET) /* Rx FIFO Depth reg */
|
||||
#define XIIC_GPO_REG_OFFSET (0x24 + XIIC_REG_OFFSET) /* Output Register */
|
||||
|
||||
/*
|
||||
* Timing register offsets from RegisterBase. These are used only for
|
||||
* setting i2c clock frequency for the line.
|
||||
*/
|
||||
#define XIIC_TSUSTA_REG_OFFSET (0x28 + XIIC_REG_OFFSET) /* TSUSTA Register */
|
||||
#define XIIC_TSUSTO_REG_OFFSET (0x2C + XIIC_REG_OFFSET) /* TSUSTO Register */
|
||||
#define XIIC_THDSTA_REG_OFFSET (0x30 + XIIC_REG_OFFSET) /* THDSTA Register */
|
||||
#define XIIC_TSUDAT_REG_OFFSET (0x34 + XIIC_REG_OFFSET) /* TSUDAT Register */
|
||||
#define XIIC_TBUF_REG_OFFSET (0x38 + XIIC_REG_OFFSET) /* TBUF Register */
|
||||
#define XIIC_THIGH_REG_OFFSET (0x3C + XIIC_REG_OFFSET) /* THIGH Register */
|
||||
#define XIIC_TLOW_REG_OFFSET (0x40 + XIIC_REG_OFFSET) /* TLOW Register */
|
||||
#define XIIC_THDDAT_REG_OFFSET (0x44 + XIIC_REG_OFFSET) /* THDDAT Register */
|
||||
|
||||
/* Control Register masks */
|
||||
#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */
|
||||
#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */
|
||||
@ -143,6 +203,9 @@ struct xiic_i2c {
|
||||
#define XIIC_TX_DYN_START_MASK 0x0100 /* 1 = Set dynamic start */
|
||||
#define XIIC_TX_DYN_STOP_MASK 0x0200 /* 1 = Set dynamic stop */
|
||||
|
||||
/* Dynamic mode constants */
|
||||
#define MAX_READ_LENGTH_DYNAMIC 255 /* Max length for dynamic read */
|
||||
|
||||
/*
|
||||
* The following constants define the register offsets for the Interrupt
|
||||
* registers. There are some holes in the memory map for reserved addresses
|
||||
@ -275,12 +338,120 @@ static int xiic_clear_rx_fifo(struct xiic_i2c *i2c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xiic_wait_tx_empty(struct xiic_i2c *i2c)
|
||||
{
|
||||
u8 isr;
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + XIIC_I2C_TIMEOUT;
|
||||
for (isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
|
||||
!(isr & XIIC_INTR_TX_EMPTY_MASK);
|
||||
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET)) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(i2c->dev, "Timeout waiting at Tx empty\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xiic_setclk - Sets the configured clock rate
|
||||
* @i2c: Pointer to the xiic device structure
|
||||
*
|
||||
* The timing register values are calculated according to the input clock
|
||||
* frequency and configured scl frequency. For details, please refer the
|
||||
* AXI I2C PG and NXP I2C Spec.
|
||||
* Supported frequencies are 100KHz, 400KHz and 1MHz.
|
||||
*
|
||||
* Return: 0 on success (Supported frequency selected or not configurable in SW)
|
||||
* -EINVAL on failure (scl frequency not supported or THIGH is 0)
|
||||
*/
|
||||
static int xiic_setclk(struct xiic_i2c *i2c)
|
||||
{
|
||||
unsigned int clk_in_mhz;
|
||||
unsigned int index = 0;
|
||||
u32 reg_val;
|
||||
|
||||
dev_dbg(i2c->adap.dev.parent,
|
||||
"%s entry, i2c->input_clk: %ld, i2c->i2c_clk: %d\n",
|
||||
__func__, i2c->input_clk, i2c->i2c_clk);
|
||||
|
||||
/* If not specified in DT, do not configure in SW. Rely only on Vivado design */
|
||||
if (!i2c->i2c_clk || !i2c->input_clk)
|
||||
return 0;
|
||||
|
||||
clk_in_mhz = DIV_ROUND_UP(i2c->input_clk, 1000000);
|
||||
|
||||
switch (i2c->i2c_clk) {
|
||||
case I2C_MAX_FAST_MODE_PLUS_FREQ:
|
||||
index = REG_VALUES_1MHZ;
|
||||
break;
|
||||
case I2C_MAX_FAST_MODE_FREQ:
|
||||
index = REG_VALUES_400KHZ;
|
||||
break;
|
||||
case I2C_MAX_STANDARD_MODE_FREQ:
|
||||
index = REG_VALUES_100KHZ;
|
||||
break;
|
||||
default:
|
||||
dev_warn(i2c->adap.dev.parent, "Unsupported scl frequency\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Value to be stored in a register is the number of clock cycles required
|
||||
* for the time duration. So the time is divided by the input clock time
|
||||
* period to get the number of clock cycles required. Refer Xilinx AXI I2C
|
||||
* PG document and I2C specification for further details.
|
||||
*/
|
||||
|
||||
/* THIGH - Depends on SCL clock frequency(i2c_clk) as below */
|
||||
reg_val = (DIV_ROUND_UP(i2c->input_clk, 2 * i2c->i2c_clk)) - 7;
|
||||
if (reg_val == 0)
|
||||
return -EINVAL;
|
||||
|
||||
xiic_setreg32(i2c, XIIC_THIGH_REG_OFFSET, reg_val - 1);
|
||||
|
||||
/* TLOW - Value same as THIGH */
|
||||
xiic_setreg32(i2c, XIIC_TLOW_REG_OFFSET, reg_val - 1);
|
||||
|
||||
/* TSUSTA */
|
||||
reg_val = (timing_reg_values[index].tsusta * clk_in_mhz) / 1000;
|
||||
xiic_setreg32(i2c, XIIC_TSUSTA_REG_OFFSET, reg_val - 1);
|
||||
|
||||
/* TSUSTO */
|
||||
reg_val = (timing_reg_values[index].tsusto * clk_in_mhz) / 1000;
|
||||
xiic_setreg32(i2c, XIIC_TSUSTO_REG_OFFSET, reg_val - 1);
|
||||
|
||||
/* THDSTA */
|
||||
reg_val = (timing_reg_values[index].thdsta * clk_in_mhz) / 1000;
|
||||
xiic_setreg32(i2c, XIIC_THDSTA_REG_OFFSET, reg_val - 1);
|
||||
|
||||
/* TSUDAT */
|
||||
reg_val = (timing_reg_values[index].tsudat * clk_in_mhz) / 1000;
|
||||
xiic_setreg32(i2c, XIIC_TSUDAT_REG_OFFSET, reg_val - 1);
|
||||
|
||||
/* TBUF */
|
||||
reg_val = (timing_reg_values[index].tbuf * clk_in_mhz) / 1000;
|
||||
xiic_setreg32(i2c, XIIC_TBUF_REG_OFFSET, reg_val - 1);
|
||||
|
||||
/* THDDAT */
|
||||
xiic_setreg32(i2c, XIIC_THDDAT_REG_OFFSET, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xiic_reinit(struct xiic_i2c *i2c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK);
|
||||
|
||||
ret = xiic_setclk(i2c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set receive Fifo depth to maximum (zero based). */
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1);
|
||||
|
||||
@ -314,15 +485,72 @@ static void xiic_deinit(struct xiic_i2c *i2c)
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr & ~XIIC_CR_ENABLE_DEVICE_MASK);
|
||||
}
|
||||
|
||||
static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c)
|
||||
{
|
||||
u8 rxmsg_len, rfd_set = 0;
|
||||
|
||||
/*
|
||||
* Clear the I2C_M_RECV_LEN flag to avoid setting
|
||||
* message length again
|
||||
*/
|
||||
i2c->rx_msg->flags &= ~I2C_M_RECV_LEN;
|
||||
|
||||
/* Set smbus_block_read flag to identify in isr */
|
||||
i2c->smbus_block_read = true;
|
||||
|
||||
/* Read byte from rx fifo and set message length */
|
||||
rxmsg_len = xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET);
|
||||
|
||||
i2c->rx_msg->buf[i2c->rx_pos++] = rxmsg_len;
|
||||
|
||||
/* Check if received length is valid */
|
||||
if (rxmsg_len <= I2C_SMBUS_BLOCK_MAX) {
|
||||
/* Set Receive fifo depth */
|
||||
if (rxmsg_len > IIC_RX_FIFO_DEPTH) {
|
||||
/*
|
||||
* When Rx msg len greater than or equal to Rx fifo capacity
|
||||
* Receive fifo depth should set to Rx fifo capacity minus 1
|
||||
*/
|
||||
rfd_set = IIC_RX_FIFO_DEPTH - 1;
|
||||
i2c->rx_msg->len = rxmsg_len + 1;
|
||||
} else if ((rxmsg_len == 1) ||
|
||||
(rxmsg_len == 0)) {
|
||||
/*
|
||||
* Minimum of 3 bytes required to exit cleanly. 1 byte
|
||||
* already received, Second byte is being received. Have
|
||||
* to set NACK in read_rx before receiving the last byte
|
||||
*/
|
||||
rfd_set = 0;
|
||||
i2c->rx_msg->len = SMBUS_BLOCK_READ_MIN_LEN;
|
||||
} else {
|
||||
/*
|
||||
* When Rx msg len less than Rx fifo capacity
|
||||
* Receive fifo depth should set to Rx msg len minus 2
|
||||
*/
|
||||
rfd_set = rxmsg_len - 2;
|
||||
i2c->rx_msg->len = rxmsg_len + 1;
|
||||
}
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Invalid message length, trigger STATE_ERROR with tx_msg_len in ISR */
|
||||
i2c->tx_msg->len = 3;
|
||||
i2c->smbus_block_read = false;
|
||||
dev_err(i2c->adap.dev.parent, "smbus_block_read Invalid msg length\n");
|
||||
}
|
||||
|
||||
static void xiic_read_rx(struct xiic_i2c *i2c)
|
||||
{
|
||||
u8 bytes_in_fifo;
|
||||
u8 bytes_in_fifo, cr = 0, bytes_to_read = 0;
|
||||
u32 bytes_rem = 0;
|
||||
int i;
|
||||
|
||||
bytes_in_fifo = xiic_getreg8(i2c, XIIC_RFO_REG_OFFSET) + 1;
|
||||
|
||||
dev_dbg(i2c->adap.dev.parent,
|
||||
"%s entry, bytes in fifo: %d, msg: %d, SR: 0x%x, CR: 0x%x\n",
|
||||
"%s entry, bytes in fifo: %d, rem: %d, SR: 0x%x, CR: 0x%x\n",
|
||||
__func__, bytes_in_fifo, xiic_rx_space(i2c),
|
||||
xiic_getreg8(i2c, XIIC_SR_REG_OFFSET),
|
||||
xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
|
||||
@ -330,13 +558,58 @@ static void xiic_read_rx(struct xiic_i2c *i2c)
|
||||
if (bytes_in_fifo > xiic_rx_space(i2c))
|
||||
bytes_in_fifo = xiic_rx_space(i2c);
|
||||
|
||||
for (i = 0; i < bytes_in_fifo; i++)
|
||||
bytes_to_read = bytes_in_fifo;
|
||||
|
||||
if (!i2c->dynamic) {
|
||||
bytes_rem = xiic_rx_space(i2c) - bytes_in_fifo;
|
||||
|
||||
/* Set msg length if smbus_block_read */
|
||||
if (i2c->rx_msg->flags & I2C_M_RECV_LEN) {
|
||||
xiic_smbus_block_read_setup(i2c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes_rem > IIC_RX_FIFO_DEPTH) {
|
||||
bytes_to_read = bytes_in_fifo;
|
||||
} else if (bytes_rem > 1) {
|
||||
bytes_to_read = bytes_rem - 1;
|
||||
} else if (bytes_rem == 1) {
|
||||
bytes_to_read = 1;
|
||||
/* Set NACK in CR to indicate slave transmitter */
|
||||
cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr |
|
||||
XIIC_CR_NO_ACK_MASK);
|
||||
} else if (bytes_rem == 0) {
|
||||
bytes_to_read = bytes_in_fifo;
|
||||
|
||||
/* Generate stop on the bus if it is last message */
|
||||
if (i2c->nmsgs == 1) {
|
||||
cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr &
|
||||
~XIIC_CR_MSMS_MASK);
|
||||
}
|
||||
|
||||
/* Make TXACK=0, clean up for next transaction */
|
||||
cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr &
|
||||
~XIIC_CR_NO_ACK_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the fifo */
|
||||
for (i = 0; i < bytes_to_read; i++) {
|
||||
i2c->rx_msg->buf[i2c->rx_pos++] =
|
||||
xiic_getreg8(i2c, XIIC_DRR_REG_OFFSET);
|
||||
}
|
||||
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET,
|
||||
(xiic_rx_space(i2c) > IIC_RX_FIFO_DEPTH) ?
|
||||
IIC_RX_FIFO_DEPTH - 1 : xiic_rx_space(i2c) - 1);
|
||||
if (i2c->dynamic) {
|
||||
u8 bytes;
|
||||
|
||||
/* Receive remaining bytes if less than fifo depth */
|
||||
bytes = min_t(u8, xiic_rx_space(i2c), IIC_RX_FIFO_DEPTH);
|
||||
bytes--;
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
static int xiic_tx_fifo_space(struct xiic_i2c *i2c)
|
||||
@ -360,7 +633,22 @@ static void xiic_fill_tx_fifo(struct xiic_i2c *i2c)
|
||||
|
||||
if (!xiic_tx_space(i2c) && i2c->nmsgs == 1) {
|
||||
/* last message in transfer -> STOP */
|
||||
data |= XIIC_TX_DYN_STOP_MASK;
|
||||
if (i2c->dynamic) {
|
||||
data |= XIIC_TX_DYN_STOP_MASK;
|
||||
} else {
|
||||
u8 cr;
|
||||
int status;
|
||||
|
||||
/* Wait till FIFO is empty so STOP is sent last */
|
||||
status = xiic_wait_tx_empty(i2c);
|
||||
if (status)
|
||||
return;
|
||||
|
||||
/* Write to CR to stop */
|
||||
cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr &
|
||||
~XIIC_CR_MSMS_MASK);
|
||||
}
|
||||
dev_dbg(i2c->adap.dev.parent, "%s TX STOP\n", __func__);
|
||||
}
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
|
||||
@ -401,7 +689,9 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
||||
dev_dbg(i2c->adap.dev.parent, "%s: SR: 0x%x, msg: %p, nmsgs: %d\n",
|
||||
__func__, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET),
|
||||
i2c->tx_msg, i2c->nmsgs);
|
||||
|
||||
dev_dbg(i2c->adap.dev.parent, "%s, ISR: 0x%x, CR: 0x%x\n",
|
||||
__func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
|
||||
xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
|
||||
|
||||
/* Service requesting interrupt */
|
||||
if ((pend & XIIC_INTR_ARB_LOST_MASK) ||
|
||||
@ -512,6 +802,12 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
||||
/* The bus is not busy, disable BusNotBusy interrupt */
|
||||
xiic_irq_dis(i2c, XIIC_INTR_BNB_MASK);
|
||||
|
||||
if (i2c->tx_msg && i2c->smbus_block_read) {
|
||||
i2c->smbus_block_read = false;
|
||||
/* Set requested message len=1 to indicate STATE_DONE */
|
||||
i2c->tx_msg->len = 1;
|
||||
}
|
||||
|
||||
if (!i2c->tx_msg)
|
||||
goto out;
|
||||
|
||||
@ -579,31 +875,113 @@ static int xiic_busy(struct xiic_i2c *i2c)
|
||||
static void xiic_start_recv(struct xiic_i2c *i2c)
|
||||
{
|
||||
u16 rx_watermark;
|
||||
u8 cr = 0, rfd_set = 0;
|
||||
struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg;
|
||||
|
||||
/* Clear and enable Rx full interrupt. */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK | XIIC_INTR_TX_ERROR_MASK);
|
||||
dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
|
||||
__func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
|
||||
xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
|
||||
|
||||
/* we want to get all but last byte, because the TX_ERROR IRQ is used
|
||||
* to inidicate error ACK on the address, and negative ack on the last
|
||||
* received byte, so to not mix them receive all but last.
|
||||
* In the case where there is only one byte to receive
|
||||
* we can check if ERROR and RX full is set at the same time
|
||||
*/
|
||||
rx_watermark = msg->len;
|
||||
if (rx_watermark > IIC_RX_FIFO_DEPTH)
|
||||
rx_watermark = IIC_RX_FIFO_DEPTH;
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, (u8)(rx_watermark - 1));
|
||||
/* Disable Tx interrupts */
|
||||
xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK | XIIC_INTR_TX_EMPTY_MASK);
|
||||
|
||||
if (i2c->dynamic) {
|
||||
u8 bytes;
|
||||
u16 val;
|
||||
|
||||
/* Clear and enable Rx full interrupt. */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK |
|
||||
XIIC_INTR_TX_ERROR_MASK);
|
||||
|
||||
/*
|
||||
* We want to get all but last byte, because the TX_ERROR IRQ
|
||||
* is used to indicate error ACK on the address, and
|
||||
* negative ack on the last received byte, so to not mix
|
||||
* them receive all but last.
|
||||
* In the case where there is only one byte to receive
|
||||
* we can check if ERROR and RX full is set at the same time
|
||||
*/
|
||||
rx_watermark = msg->len;
|
||||
bytes = min_t(u8, rx_watermark, IIC_RX_FIFO_DEPTH);
|
||||
|
||||
if (rx_watermark > 0)
|
||||
bytes--;
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, bytes);
|
||||
|
||||
if (!(msg->flags & I2C_M_NOSTART))
|
||||
/* write the address */
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
|
||||
i2c_8bit_addr_from_msg(msg) | XIIC_TX_DYN_START_MASK);
|
||||
i2c_8bit_addr_from_msg(msg) |
|
||||
XIIC_TX_DYN_START_MASK);
|
||||
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK);
|
||||
/* If last message, include dynamic stop bit with length */
|
||||
val = (i2c->nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0;
|
||||
val |= msg->len;
|
||||
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
|
||||
msg->len | ((i2c->nmsgs == 1) ? XIIC_TX_DYN_STOP_MASK : 0));
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, val);
|
||||
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_BNB_MASK);
|
||||
} else {
|
||||
/*
|
||||
* If previous message is Tx, make sure that Tx FIFO is empty
|
||||
* before starting a new transfer as the repeated start in
|
||||
* standard mode can corrupt the transaction if there are
|
||||
* still bytes to be transmitted in FIFO
|
||||
*/
|
||||
if (i2c->prev_msg_tx) {
|
||||
int status;
|
||||
|
||||
status = xiic_wait_tx_empty(i2c);
|
||||
if (status)
|
||||
return;
|
||||
}
|
||||
|
||||
cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
|
||||
|
||||
/* Set Receive fifo depth */
|
||||
rx_watermark = msg->len;
|
||||
if (rx_watermark > IIC_RX_FIFO_DEPTH) {
|
||||
rfd_set = IIC_RX_FIFO_DEPTH - 1;
|
||||
} else if (rx_watermark == 1) {
|
||||
rfd_set = rx_watermark - 1;
|
||||
|
||||
/* Set No_ACK, except for smbus_block_read */
|
||||
if (!(i2c->rx_msg->flags & I2C_M_RECV_LEN)) {
|
||||
/* Handle single byte transfer separately */
|
||||
cr |= XIIC_CR_NO_ACK_MASK;
|
||||
}
|
||||
} else if (rx_watermark == 0) {
|
||||
rfd_set = rx_watermark;
|
||||
} else {
|
||||
rfd_set = rx_watermark - 2;
|
||||
}
|
||||
/* Check if RSTA should be set */
|
||||
if (cr & XIIC_CR_MSMS_MASK) {
|
||||
/* Already a master, RSTA should be set */
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, (cr |
|
||||
XIIC_CR_REPEATED_START_MASK) &
|
||||
~(XIIC_CR_DIR_IS_TX_MASK));
|
||||
}
|
||||
|
||||
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
|
||||
|
||||
/* Clear and enable Rx full and transmit complete interrupts */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_RX_FULL_MASK |
|
||||
XIIC_INTR_TX_ERROR_MASK);
|
||||
|
||||
/* Write the address */
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET,
|
||||
i2c_8bit_addr_from_msg(msg));
|
||||
|
||||
/* Write to Control Register,to start transaction in Rx mode */
|
||||
if ((cr & XIIC_CR_MSMS_MASK) == 0) {
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, (cr |
|
||||
XIIC_CR_MSMS_MASK)
|
||||
& ~(XIIC_CR_DIR_IS_TX_MASK));
|
||||
}
|
||||
dev_dbg(i2c->adap.dev.parent, "%s end, ISR: 0x%x, CR: 0x%x\n",
|
||||
__func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
|
||||
xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
|
||||
}
|
||||
|
||||
if (i2c->nmsgs == 1)
|
||||
/* very last, enable bus not busy as well */
|
||||
@ -611,10 +989,17 @@ static void xiic_start_recv(struct xiic_i2c *i2c)
|
||||
|
||||
/* the message is tx:ed */
|
||||
i2c->tx_pos = msg->len;
|
||||
|
||||
/* Enable interrupts */
|
||||
xiic_setreg32(i2c, XIIC_DGIER_OFFSET, XIIC_GINTR_ENABLE_MASK);
|
||||
|
||||
i2c->prev_msg_tx = false;
|
||||
}
|
||||
|
||||
static void xiic_start_send(struct xiic_i2c *i2c)
|
||||
{
|
||||
u8 cr = 0;
|
||||
u16 data;
|
||||
struct i2c_msg *msg = i2c->tx_msg;
|
||||
|
||||
dev_dbg(i2c->adap.dev.parent, "%s entry, msg: %p, len: %d",
|
||||
@ -623,24 +1008,70 @@ static void xiic_start_send(struct xiic_i2c *i2c)
|
||||
__func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
|
||||
xiic_getreg8(i2c, XIIC_CR_REG_OFFSET));
|
||||
|
||||
if (!(msg->flags & I2C_M_NOSTART)) {
|
||||
if (i2c->dynamic) {
|
||||
/* write the address */
|
||||
u16 data = i2c_8bit_addr_from_msg(msg) |
|
||||
XIIC_TX_DYN_START_MASK;
|
||||
if ((i2c->nmsgs == 1) && msg->len == 0)
|
||||
data = i2c_8bit_addr_from_msg(msg) |
|
||||
XIIC_TX_DYN_START_MASK;
|
||||
|
||||
if (i2c->nmsgs == 1 && msg->len == 0)
|
||||
/* no data and last message -> add STOP */
|
||||
data |= XIIC_TX_DYN_STOP_MASK;
|
||||
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
|
||||
|
||||
/* Clear any pending Tx empty, Tx Error and then enable them */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK |
|
||||
XIIC_INTR_TX_ERROR_MASK |
|
||||
XIIC_INTR_BNB_MASK |
|
||||
((i2c->nmsgs > 1 || xiic_tx_space(i2c)) ?
|
||||
XIIC_INTR_TX_HALF_MASK : 0));
|
||||
|
||||
xiic_fill_tx_fifo(i2c);
|
||||
} else {
|
||||
/*
|
||||
* If previous message is Tx, make sure that Tx FIFO is empty
|
||||
* before starting a new transfer as the repeated start in
|
||||
* standard mode can corrupt the transaction if there are
|
||||
* still bytes to be transmitted in FIFO
|
||||
*/
|
||||
if (i2c->prev_msg_tx) {
|
||||
int status;
|
||||
|
||||
status = xiic_wait_tx_empty(i2c);
|
||||
if (status)
|
||||
return;
|
||||
}
|
||||
/* Check if RSTA should be set */
|
||||
cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
|
||||
if (cr & XIIC_CR_MSMS_MASK) {
|
||||
/* Already a master, RSTA should be set */
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, (cr |
|
||||
XIIC_CR_REPEATED_START_MASK |
|
||||
XIIC_CR_DIR_IS_TX_MASK) &
|
||||
~(XIIC_CR_NO_ACK_MASK));
|
||||
}
|
||||
|
||||
/* Write address to FIFO */
|
||||
data = i2c_8bit_addr_from_msg(msg);
|
||||
xiic_setreg16(i2c, XIIC_DTR_REG_OFFSET, data);
|
||||
|
||||
/* Fill fifo */
|
||||
xiic_fill_tx_fifo(i2c);
|
||||
|
||||
if ((cr & XIIC_CR_MSMS_MASK) == 0) {
|
||||
/* Start Tx by writing to CR */
|
||||
cr = xiic_getreg8(i2c, XIIC_CR_REG_OFFSET);
|
||||
xiic_setreg8(i2c, XIIC_CR_REG_OFFSET, cr |
|
||||
XIIC_CR_MSMS_MASK |
|
||||
XIIC_CR_DIR_IS_TX_MASK);
|
||||
}
|
||||
|
||||
/* Clear any pending Tx empty, Tx Error and then enable them */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK |
|
||||
XIIC_INTR_TX_ERROR_MASK |
|
||||
XIIC_INTR_BNB_MASK);
|
||||
}
|
||||
|
||||
/* Clear any pending Tx empty, Tx Error and then enable them. */
|
||||
xiic_irq_clr_en(i2c, XIIC_INTR_TX_EMPTY_MASK | XIIC_INTR_TX_ERROR_MASK |
|
||||
XIIC_INTR_BNB_MASK |
|
||||
((i2c->nmsgs > 1 || xiic_tx_space(i2c)) ?
|
||||
XIIC_INTR_TX_HALF_MASK : 0));
|
||||
|
||||
xiic_fill_tx_fifo(i2c);
|
||||
i2c->prev_msg_tx = true;
|
||||
}
|
||||
|
||||
static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
||||
@ -666,7 +1097,8 @@ static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
||||
|
||||
static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret;
|
||||
bool broken_read, max_read_len, smbus_blk_read;
|
||||
int ret, count;
|
||||
|
||||
mutex_lock(&i2c->lock);
|
||||
|
||||
@ -679,6 +1111,34 @@ static int xiic_start_xfer(struct xiic_i2c *i2c, struct i2c_msg *msgs, int num)
|
||||
i2c->nmsgs = num;
|
||||
init_completion(&i2c->completion);
|
||||
|
||||
/* Decide standard mode or Dynamic mode */
|
||||
i2c->dynamic = true;
|
||||
|
||||
/* Initialize prev message type */
|
||||
i2c->prev_msg_tx = false;
|
||||
|
||||
/*
|
||||
* Scan through nmsgs, use dynamic mode when none of the below three
|
||||
* conditions occur. We need standard mode even if one condition holds
|
||||
* true in the entire array of messages in a single transfer.
|
||||
* If read transaction as dynamic mode is broken for delayed reads
|
||||
* in xlnx,axi-iic-2.0 / xlnx,xps-iic-2.00.a IP versions.
|
||||
* If read length is > 255 bytes.
|
||||
* If smbus_block_read transaction.
|
||||
*/
|
||||
for (count = 0; count < i2c->nmsgs; count++) {
|
||||
broken_read = (i2c->quirks & DYNAMIC_MODE_READ_BROKEN_BIT) &&
|
||||
(i2c->tx_msg[count].flags & I2C_M_RD);
|
||||
max_read_len = (i2c->tx_msg[count].flags & I2C_M_RD) &&
|
||||
(i2c->tx_msg[count].len > MAX_READ_LENGTH_DYNAMIC);
|
||||
smbus_blk_read = (i2c->tx_msg[count].flags & I2C_M_RECV_LEN);
|
||||
|
||||
if (broken_read || max_read_len || smbus_blk_read) {
|
||||
i2c->dynamic = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = xiic_reinit(i2c);
|
||||
if (!ret)
|
||||
__xiic_start_xfer(i2c);
|
||||
@ -714,10 +1174,6 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
i2c->rx_msg = NULL;
|
||||
i2c->nmsgs = 0;
|
||||
err = -ETIMEDOUT;
|
||||
} else if (err < 0) { /* Completion error */
|
||||
i2c->tx_msg = NULL;
|
||||
i2c->rx_msg = NULL;
|
||||
i2c->nmsgs = 0;
|
||||
} else {
|
||||
err = (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
}
|
||||
@ -729,7 +1185,7 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
|
||||
static u32 xiic_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm xiic_algorithm = {
|
||||
@ -737,21 +1193,30 @@ static const struct i2c_algorithm xiic_algorithm = {
|
||||
.functionality = xiic_func,
|
||||
};
|
||||
|
||||
static const struct i2c_adapter_quirks xiic_quirks = {
|
||||
.max_read_len = 255,
|
||||
};
|
||||
|
||||
static const struct i2c_adapter xiic_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.class = I2C_CLASS_DEPRECATED,
|
||||
.algo = &xiic_algorithm,
|
||||
.quirks = &xiic_quirks,
|
||||
};
|
||||
|
||||
static const struct xiic_version_data xiic_2_00 = {
|
||||
.quirks = DYNAMIC_MODE_READ_BROKEN_BIT,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id xiic_of_match[] = {
|
||||
{ .compatible = "xlnx,xps-iic-2.00.a", .data = &xiic_2_00 },
|
||||
{ .compatible = "xlnx,axi-iic-2.1", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xiic_of_match);
|
||||
#endif
|
||||
|
||||
static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct xiic_i2c *i2c;
|
||||
struct xiic_i2c_platform_data *pdata;
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
int ret, irq;
|
||||
u8 i;
|
||||
@ -761,6 +1226,13 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
match = of_match_node(xiic_of_match, pdev->dev.of_node);
|
||||
if (match && match->data) {
|
||||
const struct xiic_version_data *data = match->data;
|
||||
|
||||
i2c->quirks = data->quirks;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
i2c->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(i2c->base))
|
||||
@ -798,6 +1270,15 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
pm_runtime_use_autosuspend(i2c->dev);
|
||||
pm_runtime_set_active(i2c->dev);
|
||||
pm_runtime_enable(i2c->dev);
|
||||
|
||||
/* SCL frequency configuration */
|
||||
i2c->input_clk = clk_get_rate(i2c->clk);
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
&i2c->i2c_clk);
|
||||
/* If clock-frequency not specified in DT, do not configure in SW */
|
||||
if (ret || i2c->i2c_clk > I2C_MAX_FAST_MODE_PLUS_FREQ)
|
||||
i2c->i2c_clk = 0;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
xiic_process, IRQF_ONESHOT,
|
||||
pdev->name, i2c);
|
||||
@ -841,6 +1322,9 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
i2c_new_client_device(&i2c->adap, pdata->devices + i);
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "mmio %08lx irq %d scl clock frequency %d\n",
|
||||
(unsigned long)res->start, irq, i2c->i2c_clk);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_dis:
|
||||
@ -875,14 +1359,6 @@ static int xiic_i2c_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id xiic_of_match[] = {
|
||||
{ .compatible = "xlnx,xps-iic-2.00.a", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xiic_of_match);
|
||||
#endif
|
||||
|
||||
static int __maybe_unused xiic_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct xiic_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/devinfo.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
@ -282,7 +283,9 @@ static void i2c_gpio_init_pinctrl_recovery(struct i2c_adapter *adap)
|
||||
{
|
||||
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
|
||||
struct device *dev = &adap->dev;
|
||||
struct pinctrl *p = bri->pinctrl;
|
||||
struct pinctrl *p = bri->pinctrl ?: dev_pinctrl(dev->parent);
|
||||
|
||||
bri->pinctrl = p;
|
||||
|
||||
/*
|
||||
* we can't change states without pinctrl, so remove the states if
|
||||
|
@ -653,12 +653,12 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy)
|
||||
int res;
|
||||
|
||||
if (dev->type != &i2c_adapter_type)
|
||||
return 0;
|
||||
return NOTIFY_DONE;
|
||||
adap = to_i2c_adapter(dev);
|
||||
|
||||
i2c_dev = get_free_i2c_dev(adap);
|
||||
if (IS_ERR(i2c_dev))
|
||||
return PTR_ERR(i2c_dev);
|
||||
return NOTIFY_DONE;
|
||||
|
||||
cdev_init(&i2c_dev->cdev, &i2cdev_fops);
|
||||
i2c_dev->cdev.owner = THIS_MODULE;
|
||||
@ -678,11 +678,11 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy)
|
||||
goto err_put_i2c_dev;
|
||||
|
||||
pr_debug("adapter [%s] registered as minor %d\n", adap->name, adap->nr);
|
||||
return 0;
|
||||
return NOTIFY_OK;
|
||||
|
||||
err_put_i2c_dev:
|
||||
put_i2c_dev(i2c_dev, false);
|
||||
return res;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int i2cdev_detach_adapter(struct device *dev, void *dummy)
|
||||
@ -691,17 +691,17 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy)
|
||||
struct i2c_dev *i2c_dev;
|
||||
|
||||
if (dev->type != &i2c_adapter_type)
|
||||
return 0;
|
||||
return NOTIFY_DONE;
|
||||
adap = to_i2c_adapter(dev);
|
||||
|
||||
i2c_dev = i2c_dev_get_by_minor(adap->nr);
|
||||
if (!i2c_dev) /* attach_adapter must have failed */
|
||||
return 0;
|
||||
return NOTIFY_DONE;
|
||||
|
||||
put_i2c_dev(i2c_dev, true);
|
||||
|
||||
pr_debug("adapter [%s] unregistered\n", adap->name);
|
||||
return 0;
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
|
||||
@ -716,7 +716,7 @@ static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
|
||||
return i2cdev_detach_adapter(dev, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block i2cdev_notifier = {
|
||||
|
@ -18,6 +18,8 @@ struct device;
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
/* The device core acts as a consumer toward pinctrl */
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
@ -44,6 +46,14 @@ struct dev_pin_info {
|
||||
extern int pinctrl_bind_pins(struct device *dev);
|
||||
extern int pinctrl_init_done(struct device *dev);
|
||||
|
||||
static inline struct pinctrl *dev_pinctrl(struct device *dev)
|
||||
{
|
||||
if (!dev->pins)
|
||||
return NULL;
|
||||
|
||||
return dev->pins->p;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Stubs if we're not using pinctrl */
|
||||
@ -58,5 +68,10 @@ static inline int pinctrl_init_done(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct pinctrl *dev_pinctrl(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PINCTRL */
|
||||
#endif /* PINCTRL_DEVINFO_H */
|
||||
|
@ -16,16 +16,25 @@
|
||||
* isn't actively driven high when setting the output value high.
|
||||
* gpio_get_value() must return the actual pin state even if the
|
||||
* pin is configured as an output.
|
||||
* @sda_is_output_only: SDA output drivers can't be turned off.
|
||||
* This is for clients that can only read SDA/SCL.
|
||||
* @sda_has_no_pullup: SDA is used in a non-compliant way and has no pull-up.
|
||||
* Therefore disable open-drain.
|
||||
* @scl_is_open_drain: SCL is set up as open drain. Same requirements
|
||||
* as for sda_is_open_drain apply.
|
||||
* @scl_is_output_only: SCL output drivers cannot be turned off.
|
||||
* @scl_has_no_pullup: SCL is used in a non-compliant way and has no pull-up.
|
||||
* Therefore disable open-drain.
|
||||
*/
|
||||
struct i2c_gpio_platform_data {
|
||||
int udelay;
|
||||
int timeout;
|
||||
unsigned int sda_is_open_drain:1;
|
||||
unsigned int sda_is_output_only:1;
|
||||
unsigned int sda_has_no_pullup:1;
|
||||
unsigned int scl_is_open_drain:1;
|
||||
unsigned int scl_is_output_only:1;
|
||||
unsigned int scl_has_no_pullup:1;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_I2C_GPIO_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user