aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds2022-03-26 12:46:08 -0700
committerLinus Torvalds2022-03-26 12:46:08 -0700
commit5627ecb8374a00163d90bc92c33f829ac27895b2 (patch)
tree2fde352c8f5412d1ed70655c44beb8c919967257
parenta060c9409e25d573c23fccb8e02f098aa33f812e (diff)
parent1a22aabf20adf89cb216f566913196128766f25b (diff)
Merge branch 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - tracepoints when Linux acts as an I2C client - added support for AMD PSP - whole subsystem now uses generic_handle_irq_safe() - piix4 driver gained MMIO access enabling so far missed controllers with AMD chipsets - a bulk of device driver updates, refactorization, and fixes. * 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (61 commits) i2c: mux: demux-pinctrl: do not deactivate a master that is not active i2c: meson: Fix wrong speed use from probe i2c: add tracepoints for I2C slave events i2c: designware: Remove code duplication i2c: cros-ec-tunnel: Fix syntax errors in comments MAINTAINERS: adjust XLP9XX I2C DRIVER after removing the devicetree binding i2c: designware: Mark dw_i2c_plat_{suspend,resume}() as __maybe_unused i2c: mediatek: Add i2c compatible for Mediatek MT8168 dt-bindings: i2c: update bindings for MT8168 SoC i2c: mt65xx: Simplify with clk-bulk i2c: i801: Drop two outdated comments i2c: xiic: Make bus names unique i2c: i801: Add support for the Process Call command i2c: i801: Drop useless masking in i801_access i2c: tegra: Add SMBus block read function i2c: designware: Use the i2c_mark_adapter_suspended/resumed() helpers i2c: designware: Lock the adapter while setting the suspended flag i2c: mediatek: remove redundant null check i2c: mediatek: modify bus speed calculation formula i2c: designware: Fix improper usage of readl ...
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt2
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt4
-rw-r--r--Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml56
-rw-r--r--Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml6
-rw-r--r--Documentation/firmware-guide/acpi/enumeration.rst39
-rw-r--r--Documentation/i2c/busses/i2c-i801.rst1
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/acpi/acpi_apd.c7
-rw-r--r--drivers/base/property.c29
-rw-r--r--drivers/i2c/busses/Kconfig25
-rw-r--r--drivers/i2c/busses/Makefile4
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2-pci.c7
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c21
-rw-r--r--drivers/i2c/busses/i2c-ccgx-ucsi.c30
-rw-r--r--drivers/i2c/busses/i2c-ccgx-ucsi.h11
-rw-r--r--drivers/i2c/busses/i2c-cht-wc.c11
-rw-r--r--drivers/i2c/busses/i2c-cros-ec-tunnel.c4
-rw-r--r--drivers/i2c/busses/i2c-designware-amdpsp.c388
-rw-r--r--drivers/i2c/busses/i2c-designware-baytrail.c12
-rw-r--r--drivers/i2c/busses/i2c-designware-common.c12
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h20
-rw-r--r--drivers/i2c/busses/i2c-designware-master.c11
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c61
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c88
-rw-r--r--drivers/i2c/busses/i2c-i801.c24
-rw-r--r--drivers/i2c/busses/i2c-meson.c12
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c206
-rw-r--r--drivers/i2c/busses/i2c-npcm7xx.c16
-rw-r--r--drivers/i2c/busses/i2c-nvidia-gpu.c62
-rw-r--r--drivers/i2c/busses/i2c-pasemi-core.c1
-rw-r--r--drivers/i2c/busses/i2c-pasemi-pci.c1
-rw-r--r--drivers/i2c/busses/i2c-piix4.c213
-rw-r--r--drivers/i2c/busses/i2c-qcom-cci.c3
-rw-r--r--drivers/i2c/busses/i2c-qcom-geni.c308
-rw-r--r--drivers/i2c/busses/i2c-rcar.c1
-rw-r--r--drivers/i2c/busses/i2c-riic.c34
-rw-r--r--drivers/i2c/busses/i2c-tegra.c18
-rw-r--r--drivers/i2c/busses/i2c-xiic.c3
-rw-r--r--drivers/i2c/i2c-core-acpi.c17
-rw-r--r--drivers/i2c/i2c-core-base.c4
-rw-r--r--drivers/i2c/i2c-core-slave.c15
-rw-r--r--drivers/i2c/i2c-core-smbus.c14
-rw-r--r--drivers/i2c/i2c-core.h9
-rw-r--r--drivers/i2c/i2c-smbus.c5
-rw-r--r--drivers/i2c/muxes/i2c-demux-pinctrl.c5
-rw-r--r--include/linux/i2c-smbus.h8
-rw-r--r--include/linux/i2c.h8
-rw-r--r--include/linux/ioport.h2
-rw-r--r--include/linux/property.h1
-rw-r--r--include/trace/events/i2c_slave.h67
50 files changed, 1545 insertions, 363 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt b/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt
index 5ea216ae7084..026985b8f61a 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt
@@ -12,8 +12,10 @@ Required properties:
"mediatek,mt7622-i2c": for MediaTek MT7622
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
+ "mediatek,mt8168-i2c": for MediaTek MT8168
"mediatek,mt8173-i2c": for MediaTek MT8173
"mediatek,mt8183-i2c": for MediaTek MT8183
+ "mediatek,mt8186-i2c": for MediaTek MT8186
"mediatek,mt8192-i2c": for MediaTek MT8192
"mediatek,mt8195-i2c", "mediatek,mt8192-i2c": for MediaTek MT8195
"mediatek,mt8516-i2c", "mediatek,mt2712-i2c": for MediaTek MT8516
diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
index 7b9fc0c22eaf..924ad8c03464 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
@@ -10,6 +10,7 @@ PROPERTIES:
"qcom,msm8996-cci"
"qcom,sdm845-cci"
"qcom,sm8250-cci"
+ "qcom,sm8450-cci"
- reg
Usage: required
@@ -43,7 +44,8 @@ PROPERTIES:
SUBNODES:
The CCI provides I2C masters for one (msm8916) or two i2c busses (msm8996,
-sdm845 and sm8250), described as subdevices named "i2c-bus@0" and "i2c-bus@1".
+sdm845, sm8250 and sm8450), described as subdevices named "i2c-bus@0" and
+"i2c-bus@1".
PROPERTIES:
diff --git a/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml b/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml
new file mode 100644
index 000000000000..7bad4b946a34
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/microchip,corei2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip MPFS I2C Controller Device Tree Bindings
+
+maintainers:
+ - Daire McNamara <daire.mcnamara@microchip.com>
+
+allOf:
+ - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - const: microchip,mpfs-i2c # Microchip PolarFire SoC compatible SoCs
+ - const: microchip,corei2c-rtl-v7 # Microchip Fabric based i2c IP core
+ - const: microchip,corei2c-rtl-v7 # Microchip Fabric based i2c IP core
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-frequency:
+ description: |
+ Desired I2C bus clock frequency in Hz. As only Standard and Fast
+ modes are supported, possible values are 100000 and 400000.
+ enum: [100000, 400000]
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c@2010a000 {
+ compatible = "microchip,mpfs-i2c", "microchip,corei2c-rtl-v7";
+ reg = <0x2010a000 0x1000>;
+ clocks = <&clkcfg 15>;
+ interrupt-parent = <&plic>;
+ interrupts = <58>;
+ clock-frequency = <100000>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml b/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml
index 052aad44e781..c30107833a51 100644
--- a/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/renesas,rcar-i2c.yaml
@@ -49,6 +49,11 @@ properties:
- renesas,i2c-r8a779a0 # R-Car V3U
- const: renesas,rcar-gen3-i2c # R-Car Gen3 and RZ/G2
+ - items:
+ - enum:
+ - renesas,i2c-r8a779f0 # R-Car S4-8
+ - const: renesas,rcar-gen4-i2c # R-Car Gen4
+
reg:
maxItems: 1
@@ -132,6 +137,7 @@ allOf:
enum:
- renesas,rcar-gen2-i2c
- renesas,rcar-gen3-i2c
+ - renesas,rcar-gen4-i2c
then:
required:
- resets
diff --git a/Documentation/firmware-guide/acpi/enumeration.rst b/Documentation/firmware-guide/acpi/enumeration.rst
index c414646a1bb4..47fb4d6d4557 100644
--- a/Documentation/firmware-guide/acpi/enumeration.rst
+++ b/Documentation/firmware-guide/acpi/enumeration.rst
@@ -142,6 +142,45 @@ In robust cases the client unfortunately needs to call
acpi_dma_request_slave_chan_by_index() directly and therefore choose the
specific FixedDMA resource by its index.
+Named Interrupts
+================
+
+Drivers enumerated via ACPI can have names to interrupts in the ACPI table
+which can be used to get the IRQ number in the driver.
+
+The interrupt name can be listed in _DSD as 'interrupt-names'. The names
+should be listed as an array of strings which will map to the Interrupt()
+resource in the ACPI table corresponding to its index.
+
+The table below shows an example of its usage::
+
+ Device (DEV0) {
+ ...
+ Name (_CRS, ResourceTemplate() {
+ ...
+ Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive) {
+ 0x20,
+ 0x24
+ }
+ })
+
+ Name (_DSD, Package () {
+ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+ Package () {
+ Package () {"interrupt-names",
+ Package (2) {"default", "alert"}},
+ }
+ ...
+ })
+ }
+
+The interrupt name 'default' will correspond to 0x20 in Interrupt()
+resource and 'alert' to 0x24. Note that only the Interrupt() resource
+is mapped and not GpioInt() or similar.
+
+The driver can call the function - fwnode_irq_get_byname() with the fwnode
+and interrupt name as arguments to get the corresponding IRQ number.
+
SPI serial bus support
======================
diff --git a/Documentation/i2c/busses/i2c-i801.rst b/Documentation/i2c/busses/i2c-i801.rst
index 42bbdd6e7fd8..cad59170b2ad 100644
--- a/Documentation/i2c/busses/i2c-i801.rst
+++ b/Documentation/i2c/busses/i2c-i801.rst
@@ -45,6 +45,7 @@ Supported adapters:
* Intel Jasper Lake (SOC)
* Intel Emmitsburg (PCH)
* Intel Alder Lake (PCH)
+ * Intel Raptor Lake (PCH)
Datasheets: Publicly available at the Intel website
diff --git a/MAINTAINERS b/MAINTAINERS
index 22fd4127f7a8..1d7d89fe873c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18877,6 +18877,7 @@ SYNOPSYS DESIGNWARE I2C DRIVER
M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
R: Mika Westerberg <mika.westerberg@linux.intel.com>
+R: Jan Dabros <jsd@semihalf.com>
L: linux-i2c@vger.kernel.org
S: Maintained
F: drivers/i2c/busses/i2c-designware-*
@@ -21520,7 +21521,6 @@ M: George Cherian <gcherian@marvell.com>
L: linux-i2c@vger.kernel.org
S: Supported
W: http://www.marvell.com
-F: Documentation/devicetree/bindings/i2c/i2c-xlp9xx.txt
F: drivers/i2c/busses/i2c-xlp9xx.c
XRA1403 GPIO EXPANDER
diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c
index e7934ba79b02..ad245bbd965e 100644
--- a/drivers/acpi/acpi_apd.c
+++ b/drivers/acpi/acpi_apd.c
@@ -232,12 +232,13 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
/* Generic apd devices */
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
{ "AMD0010", APD_ADDR(cz_i2c_desc) },
- { "AMDI0010", APD_ADDR(wt_i2c_desc) },
{ "AMD0020", APD_ADDR(cz_uart_desc) },
- { "AMDI0020", APD_ADDR(cz_uart_desc) },
- { "AMDI0022", APD_ADDR(cz_uart_desc) },
{ "AMD0030", },
{ "AMD0040", APD_ADDR(fch_misc_desc)},
+ { "AMDI0010", APD_ADDR(wt_i2c_desc) },
+ { "AMDI0019", APD_ADDR(wt_i2c_desc) },
+ { "AMDI0020", APD_ADDR(cz_uart_desc) },
+ { "AMDI0022", APD_ADDR(cz_uart_desc) },
{ "HYGO0010", APD_ADDR(wt_i2c_desc) },
#endif
#ifdef CONFIG_ARM64
diff --git a/drivers/base/property.c b/drivers/base/property.c
index e6497f6877ee..fc59e0f7f9cc 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -936,6 +936,35 @@ void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index)
EXPORT_SYMBOL(fwnode_iomap);
/**
+ * fwnode_irq_get_byname - Get IRQ from a fwnode using its name
+ * @fwnode: Pointer to the firmware node
+ * @name: IRQ name
+ *
+ * Description:
+ * Find a match to the string @name in the 'interrupt-names' string array
+ * in _DSD for ACPI, or of_node for Device Tree. Then get the Linux IRQ
+ * number of the IRQ resource corresponding to the index of the matched
+ * string.
+ *
+ * Return:
+ * Linux IRQ number on success, or negative errno otherwise.
+ */
+int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name)
+{
+ int index;
+
+ if (!name)
+ return -EINVAL;
+
+ index = fwnode_property_match_string(fwnode, "interrupt-names", name);
+ if (index < 0)
+ return index;
+
+ return fwnode_irq_get(fwnode, index);
+}
+EXPORT_SYMBOL(fwnode_irq_get_byname);
+
+/**
* fwnode_graph_get_next_endpoint - Get next endpoint firmware node
* @fwnode: Pointer to the parent firmware node
* @prev: Previous endpoint node or %NULL to get the first
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 8a6c6ee28556..a1bae59208e3 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -9,6 +9,13 @@ menu "I2C Hardware Bus support"
comment "PC SMBus host controller drivers"
depends on PCI
+config I2C_CCGX_UCSI
+ tristate
+ help
+ A common module to provide an API to instantiate UCSI device
+ for Cypress CCGx Type-C controller. Individual bus drivers
+ need to select this one on demand.
+
config I2C_ALI1535
tristate "ALI 1535"
depends on PCI
@@ -148,6 +155,7 @@ config I2C_I801
Jasper Lake (SOC)
Emmitsburg (PCH)
Alder Lake (PCH)
+ Raptor Lake (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -245,6 +253,7 @@ config I2C_NFORCE2_S4985
config I2C_NVIDIA_GPU
tristate "NVIDIA GPU I2C controller"
depends on PCI
+ select I2C_CCGX_UCSI
help
If you say yes to this option, support will be included for the
NVIDIA GPU I2C controller which is used to communicate with the GPU's
@@ -477,8 +486,8 @@ config I2C_BCM_KONA
config I2C_BRCMSTB
tristate "BRCM Settop/DSL I2C controller"
- depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC || \
- ARCH_BCM_63XX || COMPILE_TEST
+ depends on ARCH_BCM2835 || ARCH_BCM4908 || ARCH_BCM_63XX || \
+ ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
default y
help
If you say yes to this option, support will be included for the
@@ -553,6 +562,17 @@ config I2C_DESIGNWARE_PLATFORM
This driver can also be built as a module. If so, the module
will be called i2c-designware-platform.
+config I2C_DESIGNWARE_AMDPSP
+ bool "AMD PSP I2C semaphore support"
+ depends on X86_MSR
+ depends on ACPI
+ depends on I2C_DESIGNWARE_PLATFORM
+ help
+ This driver enables managed host access to the selected I2C bus shared
+ between AMD CPU and AMD PSP.
+
+ You should say Y if running on an AMD system equipped with the PSP.
+
config I2C_DESIGNWARE_BAYTRAIL
bool "Intel Baytrail I2C semaphore support"
depends on ACPI
@@ -570,6 +590,7 @@ config I2C_DESIGNWARE_PCI
tristate "Synopsys DesignWare PCI"
depends on PCI
select I2C_DESIGNWARE_CORE
+ select I2C_CCGX_UCSI
help
If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported.
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 1d00dce77098..479f60e4ee3d 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -6,6 +6,9 @@
# ACPI drivers
obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
+# Auxiliary I2C/SMBus modules
+obj-$(CONFIG_I2C_CCGX_UCSI) += i2c-ccgx-ucsi.o
+
# PC SMBus host controller drivers
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
@@ -54,6 +57,7 @@ i2c-designware-core-y += i2c-designware-master.o
i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
i2c-designware-platform-y := i2c-designware-platdrv.o
+i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_AMDPSP) += i2c-designware-amdpsp.o
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
i2c-designware-pci-y := i2c-designware-pcidrv.o
diff --git a/drivers/i2c/busses/i2c-amd-mp2-pci.c b/drivers/i2c/busses/i2c-amd-mp2-pci.c
index adf0e8c1ec01..f57077a7448d 100644
--- a/drivers/i2c/busses/i2c-amd-mp2-pci.c
+++ b/drivers/i2c/busses/i2c-amd-mp2-pci.c
@@ -308,11 +308,8 @@ static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
pci_set_master(pci_dev);
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
- if (rc) {
- rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
- if (rc)
- goto err_dma_mask;
- }
+ if (rc)
+ goto err_dma_mask;
/* Set up intx irq */
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index 5149454eef4a..f72c6576d8a3 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -454,18 +454,20 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
ret = clk_prepare_enable(i2c_dev->bus_clk);
if (ret) {
dev_err(&pdev->dev, "Couldn't prepare clock");
- return ret;
+ goto err_put_exclusive_rate;
}
i2c_dev->irq = platform_get_irq(pdev, 0);
- if (i2c_dev->irq < 0)
- return i2c_dev->irq;
+ if (i2c_dev->irq < 0) {
+ ret = i2c_dev->irq;
+ goto err_disable_unprepare_clk;
+ }
ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
dev_name(&pdev->dev), i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Could not request IRQ\n");
- return -ENODEV;
+ goto err_disable_unprepare_clk;
}
adap = &i2c_dev->adapter;
@@ -489,7 +491,16 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
ret = i2c_add_adapter(adap);
if (ret)
- free_irq(i2c_dev->irq, i2c_dev);
+ goto err_free_irq;
+
+ return 0;
+
+err_free_irq:
+ free_irq(i2c_dev->irq, i2c_dev);
+err_disable_unprepare_clk:
+ clk_disable_unprepare(i2c_dev->bus_clk);
+err_put_exclusive_rate:
+ clk_rate_exclusive_put(i2c_dev->bus_clk);
return ret;
}
diff --git a/drivers/i2c/busses/i2c-ccgx-ucsi.c b/drivers/i2c/busses/i2c-ccgx-ucsi.c
new file mode 100644
index 000000000000..092dc92dea9f
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ccgx-ucsi.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Instantiate UCSI device for Cypress CCGx Type-C controller.
+ * Derived from i2c-designware-pcidrv.c and i2c-nvidia-gpu.c.
+ */
+
+#include <linux/i2c.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include "i2c-ccgx-ucsi.h"
+
+struct software_node;
+
+struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
+ const struct software_node *swnode)
+{
+ struct i2c_board_info info = {};
+
+ strscpy(info.type, "ccgx-ucsi", sizeof(info.type));
+ info.addr = 0x08;
+ info.irq = irq;
+ info.swnode = swnode;
+
+ return i2c_new_client_device(adapter, &info);
+}
+EXPORT_SYMBOL_GPL(i2c_new_ccgx_ucsi);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-ccgx-ucsi.h b/drivers/i2c/busses/i2c-ccgx-ucsi.h
new file mode 100644
index 000000000000..739ac7a4b117
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ccgx-ucsi.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __I2C_CCGX_UCSI_H_
+#define __I2C_CCGX_UCSI_H_
+
+struct i2c_adapter;
+struct i2c_client;
+struct software_node;
+
+struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
+ const struct software_node *swnode);
+#endif /* __I2C_CCGX_UCSI_H_ */
diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c
index 54e909f9eab6..de15f09c9b47 100644
--- a/drivers/i2c/busses/i2c-cht-wc.c
+++ b/drivers/i2c/busses/i2c-cht-wc.c
@@ -100,15 +100,8 @@ static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data)
* interrupt handler as well, so running the client irq handler from
* this thread will cause things to lock up.
*/
- if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) {
- /*
- * generic_handle_irq expects local IRQs to be disabled
- * as normally it is called from interrupt context.
- */
- local_irq_disable();
- generic_handle_irq(adap->client_irq);
- local_irq_enable();
- }
+ if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ)
+ generic_handle_irq_safe(adap->client_irq);
return IRQ_HANDLED;
}
diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
index 790ea3fda693..892213d51f43 100644
--- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c
+++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
@@ -107,7 +107,7 @@ static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[],
/**
* ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response
*
- * @i2c_msgs: The i2c messages to to fill up.
+ * @i2c_msgs: The i2c messages to fill up.
* @num: The number of i2c messages expected.
*
* Returns the number of response bytes expeced.
@@ -131,7 +131,7 @@ static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num)
* We'll take the EC's response and copy it back into msgs.
*
* @buf: The buffer to parse.
- * @i2c_msgs: The i2c messages to to fill up.
+ * @i2c_msgs: The i2c messages to fill up.
* @num: The number of i2c messages; will be modified to include the actual
* number received.
*
diff --git a/drivers/i2c/busses/i2c-designware-amdpsp.c b/drivers/i2c/busses/i2c-designware-amdpsp.c
new file mode 100644
index 000000000000..9b37f2b95abc
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-amdpsp.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/psp-sev.h>
+#include <linux/types.h>
+
+#include <asm/msr.h>
+
+#include "i2c-designware-core.h"
+
+#define MSR_AMD_PSP_ADDR 0xc00110a2
+#define PSP_MBOX_OFFSET 0x10570
+#define PSP_CMD_TIMEOUT_US (500 * USEC_PER_MSEC)
+
+#define PSP_I2C_REQ_BUS_CMD 0x64
+#define PSP_I2C_REQ_RETRY_CNT 10
+#define PSP_I2C_REQ_RETRY_DELAY_US (50 * USEC_PER_MSEC)
+#define PSP_I2C_REQ_STS_OK 0x0
+#define PSP_I2C_REQ_STS_BUS_BUSY 0x1
+#define PSP_I2C_REQ_STS_INV_PARAM 0x3
+
+#define PSP_MBOX_FIELDS_STS GENMASK(15, 0)
+#define PSP_MBOX_FIELDS_CMD GENMASK(23, 16)
+#define PSP_MBOX_FIELDS_RESERVED GENMASK(29, 24)
+#define PSP_MBOX_FIELDS_RECOVERY BIT(30)
+#define PSP_MBOX_FIELDS_READY BIT(31)
+
+struct psp_req_buffer_hdr {
+ u32 total_size;
+ u32 status;
+};
+
+enum psp_i2c_req_type {
+ PSP_I2C_REQ_ACQUIRE,
+ PSP_I2C_REQ_RELEASE,
+ PSP_I2C_REQ_MAX
+};
+
+struct psp_i2c_req {
+ struct psp_req_buffer_hdr hdr;
+ enum psp_i2c_req_type type;
+};
+
+struct psp_mbox {
+ u32 cmd_fields;
+ u64 i2c_req_addr;
+} __packed;
+
+static DEFINE_MUTEX(psp_i2c_access_mutex);
+static unsigned long psp_i2c_sem_acquired;
+static void __iomem *mbox_iomem;
+static u32 psp_i2c_access_count;
+static bool psp_i2c_mbox_fail;
+static struct device *psp_i2c_dev;
+
+/*
+ * Implementation of PSP-x86 i2c-arbitration mailbox introduced for AMD Cezanne
+ * family of SoCs.
+ */
+
+static int psp_get_mbox_addr(unsigned long *mbox_addr)
+{
+ unsigned long long psp_mmio;
+
+ if (rdmsrl_safe(MSR_AMD_PSP_ADDR, &psp_mmio))
+ return -EIO;
+
+ *mbox_addr = (unsigned long)(psp_mmio + PSP_MBOX_OFFSET);
+
+ return 0;
+}
+
+static int psp_mbox_probe(void)
+{
+ unsigned long mbox_addr;
+ int ret;
+
+ ret = psp_get_mbox_addr(&mbox_addr);
+ if (ret)
+ return ret;
+
+ mbox_iomem = ioremap(mbox_addr, sizeof(struct psp_mbox));
+ if (!mbox_iomem)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* Recovery field should be equal 0 to start sending commands */
+static int psp_check_mbox_recovery(struct psp_mbox __iomem *mbox)
+{
+ u32 tmp;
+
+ tmp = readl(&mbox->cmd_fields);
+
+ return FIELD_GET(PSP_MBOX_FIELDS_RECOVERY, tmp);
+}
+
+static int psp_wait_cmd(struct psp_mbox __iomem *mbox)
+{
+ u32 tmp, expected;
+
+ /* Expect mbox_cmd to be cleared and ready bit to be set by PSP */
+ expected = FIELD_PREP(PSP_MBOX_FIELDS_READY, 1);
+
+ /*
+ * Check for readiness of PSP mailbox in a tight loop in order to
+ * process further as soon as command was consumed.
+ */
+ return readl_poll_timeout(&mbox->cmd_fields, tmp, (tmp == expected),
+ 0, PSP_CMD_TIMEOUT_US);
+}
+
+/* Status equal to 0 means that PSP succeed processing command */
+static u32 psp_check_mbox_sts(struct psp_mbox __iomem *mbox)
+{
+ u32 cmd_reg;
+
+ cmd_reg = readl(&mbox->cmd_fields);
+
+ return FIELD_GET(PSP_MBOX_FIELDS_STS, cmd_reg);
+}
+
+static int psp_send_cmd(struct psp_i2c_req *req)
+{
+ struct psp_mbox __iomem *mbox = mbox_iomem;
+ phys_addr_t req_addr;
+ u32 cmd_reg;
+
+ if (psp_check_mbox_recovery(mbox))
+ return -EIO;
+
+ if (psp_wait_cmd(mbox))
+ return -EBUSY;
+
+ /*
+ * Fill mailbox with address of command-response buffer, which will be
+ * used for sending i2c requests as well as reading status returned by
+ * PSP. Use physical address of buffer, since PSP will map this region.
+ */
+ req_addr = __psp_pa((void *)req);
+ writeq(req_addr, &mbox->i2c_req_addr);
+
+ /* Write command register to trigger processing */
+ cmd_reg = FIELD_PREP(PSP_MBOX_FIELDS_CMD, PSP_I2C_REQ_BUS_CMD);
+ writel(cmd_reg, &mbox->cmd_fields);
+
+ if (psp_wait_cmd(mbox))
+ return -ETIMEDOUT;
+
+ if (psp_check_mbox_sts(mbox))
+ return -EIO;
+
+ return 0;
+}
+
+/* Helper to verify status returned by PSP */
+static int check_i2c_req_sts(struct psp_i2c_req *req)
+{
+ u32 status;
+
+ /* Status field in command-response buffer is updated by PSP */
+ status = READ_ONCE(req->hdr.status);
+
+ switch (status) {
+ case PSP_I2C_REQ_STS_OK:
+ return 0;
+ case PSP_I2C_REQ_STS_BUS_BUSY:
+ return -EBUSY;
+ case PSP_I2C_REQ_STS_INV_PARAM:
+ default:
+ return -EIO;
+ }
+}
+
+static int psp_send_check_i2c_req(struct psp_i2c_req *req)
+{
+ /*
+ * Errors in x86-PSP i2c-arbitration protocol may occur at two levels:
+ * 1. mailbox communication - PSP is not operational or some IO errors
+ * with basic communication had happened;
+ * 2. i2c-requests - PSP refuses to grant i2c arbitration to x86 for too
+ * long.
+ * In order to distinguish between these two in error handling code, all
+ * errors on the first level (returned by psp_send_cmd) are shadowed by
+ * -EIO.
+ */
+ if (psp_send_cmd(req))
+ return -EIO;
+
+ return check_i2c_req_sts(req);
+}
+
+static int psp_send_i2c_req(enum psp_i2c_req_type i2c_req_type)
+{
+ struct psp_i2c_req *req;
+ unsigned long start;
+ int status, ret;
+
+ /* Allocate command-response buffer */
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->hdr.total_size = sizeof(*req);
+ req->type = i2c_req_type;
+
+ start = jiffies;
+ ret = read_poll_timeout(psp_send_check_i2c_req, status,
+ (status != -EBUSY),
+ PSP_I2C_REQ_RETRY_DELAY_US,
+ PSP_I2C_REQ_RETRY_CNT * PSP_I2C_REQ_RETRY_DELAY_US,
+ 0, req);
+ if (ret) {
+ dev_err(psp_i2c_dev, "Timed out waiting for PSP to %s I2C bus\n",
+ (i2c_req_type == PSP_I2C_REQ_ACQUIRE) ?
+ "release" : "acquire");
+ goto cleanup;
+ }
+
+ ret = status;
+ if (ret) {
+ dev_err(psp_i2c_dev, "PSP communication error\n");
+ goto cleanup;
+ }
+
+ dev_dbg(psp_i2c_dev, "Request accepted by PSP after %ums\n",
+ jiffies_to_msecs(jiffies - start));
+
+cleanup:
+ if (ret) {
+ dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n");
+ psp_i2c_mbox_fail = true;
+ }
+
+ kfree(req);
+ return ret;
+}
+
+static int psp_acquire_i2c_bus(void)
+{
+ int status;
+
+ mutex_lock(&psp_i2c_access_mutex);
+
+ /* Return early if mailbox malfunctioned */
+ if (psp_i2c_mbox_fail)
+ goto cleanup;
+
+ /*
+ * Simply increment usage counter and return if PSP semaphore was
+ * already taken by kernel.
+ */
+ if (psp_i2c_access_count) {
+ psp_i2c_access_count++;
+ goto cleanup;
+ }
+
+ status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE);
+ if (status)
+ goto cleanup;
+
+ psp_i2c_sem_acquired = jiffies;
+ psp_i2c_access_count++;
+
+ /*
+ * In case of errors with PSP arbitrator psp_i2c_mbox_fail variable is
+ * set above. As a consequence consecutive calls to acquire will bypass
+ * communication with PSP. At any case i2c bus is granted to the caller,
+ * thus always return success.
+ */
+cleanup:
+ mutex_unlock(&psp_i2c_access_mutex);
+ return 0;
+}
+
+static void psp_release_i2c_bus(void)
+{
+ int status;
+
+ mutex_lock(&psp_i2c_access_mutex);
+
+ /* Return early if mailbox was malfunctional */
+ if (psp_i2c_mbox_fail)
+ goto cleanup;
+
+ /*
+ * If we are last owner of PSP semaphore, need to release aribtration
+ * via mailbox.
+ */
+ psp_i2c_access_count--;
+ if (psp_i2c_access_count)
+ goto cleanup;
+
+ /* Send a release command to PSP */
+ status = psp_send_i2c_req(PSP_I2C_REQ_RELEASE);
+ if (status)
+ goto cleanup;
+
+ dev_dbg(psp_i2c_dev, "PSP semaphore held for %ums\n",
+ jiffies_to_msecs(jiffies - psp_i2c_sem_acquired));
+
+cleanup:
+ mutex_unlock(&psp_i2c_access_mutex);
+}
+
+/*
+ * Locking methods are based on the default implementation from
+ * drivers/i2c/i2c-core-base.c, but with psp acquire and release operations
+ * added. With this in place we can ensure that i2c clients on the bus shared
+ * with psp are able to lock HW access to the bus for arbitrary number of
+ * operations - that is e.g. write-wait-read.
+ */
+static void i2c_adapter_dw_psp_lock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ psp_acquire_i2c_bus();
+ rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter));
+}
+
+static int i2c_adapter_dw_psp_trylock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = rt_mutex_trylock(&adapter->bus_lock);
+ if (ret)
+ return ret;
+
+ psp_acquire_i2c_bus();
+
+ return ret;
+}
+
+static void i2c_adapter_dw_psp_unlock_bus(struct i2c_adapter *adapter,
+ unsigned int flags)
+{
+ psp_release_i2c_bus();
+ rt_mutex_unlock(&adapter->bus_lock);
+}
+
+static const struct i2c_lock_operations i2c_dw_psp_lock_ops = {
+ .lock_bus = i2c_adapter_dw_psp_lock_bus,
+ .trylock_bus = i2c_adapter_dw_psp_trylock_bus,
+ .unlock_bus = i2c_adapter_dw_psp_unlock_bus,
+};
+
+int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev)
+{
+ int ret;
+
+ if (!dev)
+ return -ENODEV;
+
+ if (!(dev->flags & ARBITRATION_SEMAPHORE))
+ return -ENODEV;
+
+ /* Allow to bind only one instance of a driver */
+ if (psp_i2c_dev)
+ return -EEXIST;
+
+ psp_i2c_dev = dev->dev;
+
+ ret = psp_mbox_probe();
+ if (ret)
+ return ret;
+
+ dev_info(psp_i2c_dev, "I2C bus managed by AMD PSP\n");
+
+ /*
+ * Install global locking callbacks for adapter as well as internal i2c
+ * controller locks.
+ */
+ dev->adapter.lock_ops = &i2c_dw_psp_lock_ops;
+ dev->acquire_lock = psp_acquire_i2c_bus;
+ dev->release_lock = psp_release_i2c_bus;
+
+ return 0;
+}
+
+/* Unmap area used as a mailbox with PSP */
+void i2c_dw_amdpsp_remove_lock_support(struct dw_i2c_dev *dev)
+{
+ iounmap(mbox_iomem);
+}
diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c
index c6a7a00e1d52..45774aa47c28 100644
--- a/drivers/i2c/busses/i2c-designware-baytrail.c
+++ b/drivers/i2c/busses/i2c-designware-baytrail.c
@@ -12,25 +12,25 @@
#include "i2c-designware-core.h"
-int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
+int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev)
{
acpi_status status;
unsigned long long shared_host = 0;
acpi_handle handle;
- if (!dev || !dev->dev)
- return 0;
+ if (!dev)
+ return -ENODEV;
handle = ACPI_HANDLE(dev->dev);
if (!handle)
- return 0;
+ return -ENODEV;
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
if (ACPI_FAILURE(status))
- return 0;
+ return -ENODEV;
if (!shared_host)
- return 0;
+ return -ENODEV;
if (!iosf_mbi_available())
return -EPROBE_DEFER;
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index bf2a4920638a..9f8574320eb2 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -578,7 +578,12 @@ int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
* Try to detect the FIFO depth if not set by interface driver,
* the depth could be from 2 to 256 from HW spec.
*/
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &param);
+ i2c_dw_release_lock(dev);
if (ret)
return ret;
@@ -607,6 +612,11 @@ u32 i2c_dw_func(struct i2c_adapter *adap)
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
u32 dummy;
+ int ret;
+
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return;
/* Disable controller */
__i2c_dw_disable(dev);
@@ -614,6 +624,8 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)
/* Disable all interrupts */
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
+
+ i2c_dw_release_lock(dev);
}
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 4b26cba40139..70b80e710990 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -227,6 +227,8 @@ struct reset_control;
* @hs_lcnt: high speed LCNT value
* @acquire_lock: function to acquire a hardware lock on the bus
* @release_lock: function to release a hardware lock on the bus
+ * @semaphore_idx: Index of table with semaphore type attached to the bus. It's
+ * -1 if there is no semaphore.
* @shared_with_punit: true if this bus is shared with the SoCs PUNIT
* @disable: function to disable the controller
* @disable_int: function to disable all interrupts
@@ -234,7 +236,6 @@ struct reset_control;
* @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
* @rinfo: I²C GPIO recovery information
- * @suspended: set to true if the controller is suspended
*
* HCNT and LCNT parameters can be used if the platform knows more accurate
* values than the one computed based only on the input clock frequency.
@@ -285,6 +286,7 @@ struct dw_i2c_dev {
u16 hs_lcnt;
int (*acquire_lock)(void);
void (*release_lock)(void);
+ int semaphore_idx;
bool shared_with_punit;
void (*disable)(struct dw_i2c_dev *dev);
void (*disable_int)(struct dw_i2c_dev *dev);
@@ -292,11 +294,11 @@ struct dw_i2c_dev {
int (*set_sda_hold_time)(struct dw_i2c_dev *dev);
int mode;
struct i2c_bus_recovery_info rinfo;
- bool suspended;
};
#define ACCESS_INTR_MASK BIT(0)
#define ACCESS_NO_IRQ_SUSPEND BIT(1)
+#define ARBITRATION_SEMAPHORE BIT(2)
#define MODEL_MSCC_OCELOT BIT(8)
#define MODEL_BAIKAL_BT1 BIT(9)
@@ -310,6 +312,11 @@ struct dw_i2c_dev {
#define AMD_UCSI_INTR_REG 0x474
#define AMD_UCSI_INTR_EN 0xd
+struct i2c_dw_semaphore_callbacks {
+ int (*probe)(struct dw_i2c_dev *dev);
+ void (*remove)(struct dw_i2c_dev *dev);
+};
+
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);
@@ -370,9 +377,12 @@ static inline void i2c_dw_configure(struct dw_i2c_dev *dev)
}
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
-extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev);
-#else
-static inline int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { return 0; }
+int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
+#endif
+
+#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_AMDPSP)
+int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev);
+void i2c_dw_amdpsp_remove_lock_support(struct dw_i2c_dev *dev);
#endif
int i2c_dw_validate_speed(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index 9177463c2cbb..44a94b225ed8 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -567,11 +567,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
goto done_nolock;
}
- if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) {
- ret = -ESHUTDOWN;
- goto done_nolock;
- }
-
reinit_completion(&dev->cmd_complete);
dev->msgs = msgs;
dev->msgs_num = num;
@@ -905,7 +900,13 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND;
}
+ ret = i2c_dw_acquire_lock(dev);
+ if (ret)
+ return ret;
+
i2c_dw_disable_int(dev);
+ i2c_dw_release_lock(dev);
+
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
dev_name(dev->dev), dev);
if (ret) {
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index ef4250f8852b..608e61209455 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include "i2c-designware-core.h"
+#include "i2c-ccgx-ucsi.h"
#define DRIVER_NAME "i2c-designware-pci"
#define AMD_CLK_RATE_HZ 100000
@@ -125,26 +126,6 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
return -ENODEV;
}
- /*
- * TODO find a better way how to deduplicate instantiation
- * of USB PD slave device from nVidia GPU driver.
- */
-static int navi_amd_register_client(struct dw_i2c_dev *dev)
-{
- struct i2c_board_info info;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE);
- info.addr = 0x08;
- info.irq = dev->irq;
-
- dev->slave = i2c_new_client_device(&dev->adapter, &info);
- if (IS_ERR(dev->slave))
- return PTR_ERR(dev->slave);
-
- return 0;
-}
-
static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
{
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
@@ -213,29 +194,46 @@ static struct dw_pci_controller dw_pci_controllers[] = {
},
};
-static int __maybe_unused i2c_dw_pci_suspend(struct device *dev)
+static int __maybe_unused i2c_dw_pci_runtime_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
- i_dev->suspended = true;
i_dev->disable(i_dev);
-
return 0;
}
+static int __maybe_unused i2c_dw_pci_suspend(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_suspended(&i_dev->adapter);
+
+ return i2c_dw_pci_runtime_suspend(dev);
+}
+
+static int __maybe_unused i2c_dw_pci_runtime_resume(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+ return i_dev->init(i_dev);
+}
+
static int __maybe_unused i2c_dw_pci_resume(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
int ret;
- ret = i_dev->init(i_dev);
- i_dev->suspended = false;
+ ret = i2c_dw_pci_runtime_resume(dev);
+
+ i2c_mark_adapter_resumed(&i_dev->adapter);
return ret;
}
-static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
- i2c_dw_pci_resume, NULL);
+static const struct dev_pm_ops i2c_dw_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume)
+ SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, i2c_dw_pci_runtime_resume, NULL)
+};
static int i2c_dw_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
@@ -325,11 +323,10 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
}
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
- r = navi_amd_register_client(dev);
- if (r) {
- dev_err(dev->dev, "register client failed with %d\n", r);
- return r;
- }
+ dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, NULL);
+ if (IS_ERR(dev->slave))
+ return dev_err_probe(dev->dev, PTR_ERR(dev->slave),
+ "register UCSI failed\n");
}
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 2bd81abc86f6..70ade5306e45 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -50,6 +50,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
{ "808622C1", ACCESS_NO_IRQ_SUSPEND },
{ "AMD0010", ACCESS_INTR_MASK },
{ "AMDI0010", ACCESS_INTR_MASK },
+ { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },
{ "AMDI0510", 0 },
{ "APMC0D0F", 0 },
{ "HISI02A1", 0 },
@@ -204,6 +205,63 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
{ } /* terminate list */
};
+static const struct i2c_dw_semaphore_callbacks i2c_dw_semaphore_cb_table[] = {
+#ifdef CONFIG_I2C_DESIGNWARE_BAYTRAIL
+ {
+ .probe = i2c_dw_baytrail_probe_lock_support,
+ },
+#endif
+#ifdef CONFIG_I2C_DESIGNWARE_AMDPSP
+ {
+ .probe = i2c_dw_amdpsp_probe_lock_support,
+ .remove = i2c_dw_amdpsp_remove_lock_support,
+ },
+#endif
+ {}
+};
+
+static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
+{
+ const struct i2c_dw_semaphore_callbacks *ptr;
+ int i = 0;
+ int ret;
+
+ ptr = i2c_dw_semaphore_cb_table;
+
+ dev->semaphore_idx = -1;
+
+ while (ptr->probe) {
+ ret = ptr->probe(dev);
+ if (ret) {
+ /*
+ * If there is no semaphore device attached to this
+ * controller, we shouldn't abort general i2c_controller
+ * probe.
+ */
+ if (ret != -ENODEV)
+ return ret;
+
+ i++;
+ ptr++;
+ continue;
+ }
+
+ dev->semaphore_idx = i;
+ break;
+ }
+
+ return 0;
+}
+
+static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev)
+{
+ if (dev->semaphore_idx < 0)
+ return;
+
+ if (i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove)
+ i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove(dev);
+}
+
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
struct i2c_adapter *adap;
@@ -334,6 +392,8 @@ static int dw_i2c_plat_remove(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev);
dw_i2c_plat_pm_cleanup(dev);
+ i2c_dw_remove_lock_support(dev);
+
reset_control_assert(dev->rst);
return 0;
@@ -368,12 +428,10 @@ static void dw_i2c_plat_complete(struct device *dev)
#endif
#ifdef CONFIG_PM
-static int dw_i2c_plat_suspend(struct device *dev)
+static int dw_i2c_plat_runtime_suspend(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
- i_dev->suspended = true;
-
if (i_dev->shared_with_punit)
return 0;
@@ -383,7 +441,16 @@ static int dw_i2c_plat_suspend(struct device *dev)
return 0;
}
-static int dw_i2c_plat_resume(struct device *dev)
+static int __maybe_unused dw_i2c_plat_suspend(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+ i2c_mark_adapter_suspended(&i_dev->adapter);
+
+ return dw_i2c_plat_runtime_suspend(dev);
+}
+
+static int dw_i2c_plat_runtime_resume(struct device *dev)
{
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
@@ -391,7 +458,16 @@ static int dw_i2c_plat_resume(struct device *dev)
i2c_dw_prepare_clk(i_dev, true);
i_dev->init(i_dev);
- i_dev->suspended = false;
+
+ return 0;
+}
+
+static int __maybe_unused dw_i2c_plat_resume(struct device *dev)
+{
+ struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+ dw_i2c_plat_runtime_resume(dev);
+ i2c_mark_adapter_resumed(&i_dev->adapter);
return 0;
}
@@ -400,7 +476,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
.prepare = dw_i2c_plat_prepare,
.complete = dw_i2c_plat_complete,
SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
- SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
+ SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL)
};
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 7428cc6af5cc..ff706349bdfb 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -75,6 +75,7 @@
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
* Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
* Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes
+ * Raptor Lake-S (PCH) 0x7a23 32 hard yes yes yes
*
* Features supported by this driver:
* Software PEC no
@@ -165,7 +166,7 @@
#define I801_BYTE 0x04
#define I801_BYTE_DATA 0x08
#define I801_WORD_DATA 0x0C
-#define I801_PROC_CALL 0x10 /* unimplemented */
+#define I801_PROC_CALL 0x10
#define I801_BLOCK_DATA 0x14
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
#define I801_BLOCK_PROC_CALL 0x1C
@@ -228,6 +229,7 @@
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
+#define PCI_DEVICE_ID_INTEL_RAPTOR_LAKE_S_SMBUS 0x7a23
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
@@ -362,9 +364,6 @@ static int i801_check_post(struct i801_priv *priv, int status)
/*
* If the SMBus is still busy, we give up
- * Note: This timeout condition only happens when using polling
- * transactions. For interrupt operation, NAK/timeout is indicated by
- * DEV_ERR.
*/
if (unlikely(status < 0)) {
dev_err(&priv->pci_dev->dev, "Transaction timeout\n");
@@ -473,8 +472,6 @@ static int i801_transaction(struct i801_priv *priv, int xact)
return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
}
- /* the current contents of SMBHSTCNT can be overwritten, since PEC,
- * SMBSCMD are passed in xact */
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
status = i801_wait_intr(priv);
@@ -790,7 +787,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
{
int hwpec;
int block = 0;
- int ret = 0, xact = 0;
+ int ret, xact;
struct i801_priv *priv = i2c_get_adapdata(adap);
mutex_lock(&priv->acpi_lock);
@@ -836,6 +833,14 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
}
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));
@@ -902,12 +907,13 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
goto out;
- switch (xact & 0x7f) {
+ 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;
@@ -933,6 +939,7 @@ static u32 i801_func(struct i2c_adapter *adapter)
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
((priv->features & FEATURE_BLOCK_PROC) ?
@@ -1041,6 +1048,7 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_M_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
+ { PCI_DEVICE_DATA(INTEL, RAPTOR_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c
index ef73a42577cc..07eb819072c4 100644
--- a/drivers/i2c/busses/i2c-meson.c
+++ b/drivers/i2c/busses/i2c-meson.c
@@ -465,18 +465,18 @@ static int meson_i2c_probe(struct platform_device *pdev)
*/
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
- ret = i2c_add_adapter(&i2c->adap);
- if (ret < 0) {
- clk_disable_unprepare(i2c->clk);
- return ret;
- }
-
/* Disable filtering */
meson_i2c_set_mask(i2c, REG_SLAVE_ADDR,
REG_SLV_SDA_FILTER | REG_SLV_SCL_FILTER, 0);
meson_i2c_set_clk_div(i2c, timings.bus_freq_hz);
+ ret = i2c_add_adapter(&i2c->adap);
+ if (ret < 0) {
+ clk_disable_unprepare(i2c->clk);
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 9ea427f53083..f651d3e124d6 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -67,11 +67,12 @@
#define MAX_SAMPLE_CNT_DIV 8
#define MAX_STEP_CNT_DIV 64
-#define MAX_CLOCK_DIV 256
+#define MAX_CLOCK_DIV_8BITS 256
+#define MAX_CLOCK_DIV_5BITS 32
#define MAX_HS_STEP_CNT_DIV 8
-#define I2C_STANDARD_MODE_BUFFER (1000 / 2)
-#define I2C_FAST_MODE_BUFFER (300 / 2)
-#define I2C_FAST_MODE_PLUS_BUFFER (20 / 2)
+#define I2C_STANDARD_MODE_BUFFER (1000 / 3)
+#define I2C_FAST_MODE_BUFFER (300 / 3)
+#define I2C_FAST_MODE_PLUS_BUFFER (20 / 3)
#define I2C_CONTROL_RS (0x1 << 1)
#define I2C_CONTROL_DMA_EN (0x1 << 2)
@@ -85,6 +86,27 @@
#define I2C_DRV_NAME "i2c-mt65xx"
+/**
+ * enum i2c_mt65xx_clks - Clocks enumeration for MT65XX I2C
+ *
+ * @I2C_MT65XX_CLK_MAIN: main clock for i2c bus
+ * @I2C_MT65XX_CLK_DMA: DMA clock for i2c via DMA
+ * @I2C_MT65XX_CLK_PMIC: PMIC clock for i2c from PMIC
+ * @I2C_MT65XX_CLK_ARB: Arbitrator clock for i2c
+ * @I2C_MT65XX_CLK_MAX: Number of supported clocks
+ */
+enum i2c_mt65xx_clks {
+ I2C_MT65XX_CLK_MAIN = 0,
+ I2C_MT65XX_CLK_DMA,
+ I2C_MT65XX_CLK_PMIC,
+ I2C_MT65XX_CLK_ARB,
+ I2C_MT65XX_CLK_MAX
+};
+
+static const char * const i2c_mt65xx_clk_ids[I2C_MT65XX_CLK_MAX] = {
+ "main", "dma", "pmic", "arb"
+};
+
enum DMA_REGS_OFFSET {
OFFSET_INT_FLAG = 0x0,
OFFSET_INT_EN = 0x04,
@@ -243,10 +265,7 @@ struct mtk_i2c {
/* set in i2c probe */
void __iomem *base; /* i2c base addr */
void __iomem *pdmabase; /* dma base address*/
- struct clk *clk_main; /* main clock for i2c bus */
- struct clk *clk_dma; /* DMA clock for i2c via DMA */
- struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
- struct clk *clk_arb; /* Arbitrator clock for i2c */
+ struct clk_bulk_data clocks[I2C_MT65XX_CLK_MAX]; /* clocks for i2c */
bool have_pmic; /* can use i2c pins from PMIC */
bool use_push_pull; /* IO config push-pull mode */
@@ -370,6 +389,19 @@ static const struct mtk_i2c_compatible mt7622_compat = {
.max_dma_support = 32,
};
+static const struct mtk_i2c_compatible mt8168_compat = {
+ .regs = mt_i2c_regs_v1,
+ .pmic_i2c = 0,
+ .dcm = 1,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 1,
+ .ltiming_adjust = 0,
+ .apdma_sync = 0,
+ .max_dma_support = 33,
+};
+
static const struct mtk_i2c_compatible mt8173_compat = {
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
@@ -397,6 +429,19 @@ static const struct mtk_i2c_compatible mt8183_compat = {
.max_dma_support = 33,
};
+static const struct mtk_i2c_compatible mt8186_compat = {
+ .regs = mt_i2c_regs_v2,
+ .pmic_i2c = 0,
+ .dcm = 0,
+ .auto_restart = 1,
+ .aux_len_reg = 1,
+ .timing_adjust = 1,
+ .dma_sync = 0,
+ .ltiming_adjust = 1,
+ .apdma_sync = 0,
+ .max_dma_support = 36,
+};
+
static const struct mtk_i2c_compatible mt8192_compat = {
.quirks = &mt8183_i2c_quirks,
.regs = mt_i2c_regs_v2,
@@ -416,8 +461,10 @@ static const struct of_device_id mtk_i2c_of_match[] = {
{ .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat },
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
+ { .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat },
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
{ .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
+ { .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat },
{ .compatible = "mediatek,mt8192-i2c", .data = &mt8192_compat },
{}
};
@@ -434,55 +481,6 @@ static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val,
writew(val, i2c->base + i2c->dev_comp->regs[reg]);
}
-static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
-{
- int ret;
-
- ret = clk_prepare_enable(i2c->clk_dma);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(i2c->clk_main);
- if (ret)
- goto err_main;
-
- if (i2c->have_pmic) {
- ret = clk_prepare_enable(i2c->clk_pmic);
- if (ret)
- goto err_pmic;
- }
-
- if (i2c->clk_arb) {
- ret = clk_prepare_enable(i2c->clk_arb);
- if (ret)
- goto err_arb;
- }
-
- return 0;
-
-err_arb:
- if (i2c->have_pmic)
- clk_disable_unprepare(i2c->clk_pmic);
-err_pmic:
- clk_disable_unprepare(i2c->clk_main);
-err_main:
- clk_disable_unprepare(i2c->clk_dma);
-
- return ret;
-}
-
-static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
-{
- if (i2c->clk_arb)
- clk_disable_unprepare(i2c->clk_arb);
-
- if (i2c->have_pmic)
- clk_disable_unprepare(i2c->clk_pmic);
-
- clk_disable_unprepare(i2c->clk_main);
- clk_disable_unprepare(i2c->clk_dma);
-}
-
static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
{
u16 control_reg;
@@ -590,6 +588,31 @@ static int mtk_i2c_max_step_cnt(unsigned int target_speed)
return MAX_STEP_CNT_DIV;
}
+static int mtk_i2c_get_clk_div_restri(struct mtk_i2c *i2c,
+ unsigned int sample_cnt)
+{
+ int clk_div_restri = 0;
+
+ if (i2c->dev_comp->ltiming_adjust == 0)
+ return 0;
+
+ if (sample_cnt == 1) {
+ if (i2c->ac_timing.inter_clk_div == 0)
+ clk_div_restri = 0;
+ else
+ clk_div_restri = 1;
+ } else {
+ if (i2c->ac_timing.inter_clk_div == 0)
+ clk_div_restri = -1;
+ else if (i2c->ac_timing.inter_clk_div == 1)
+ clk_div_restri = 0;
+ else
+ clk_div_restri = 1;
+ }
+
+ return clk_div_restri;
+}
+
/*
* Check and Calculate i2c ac-timing
*
@@ -718,6 +741,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
unsigned int best_mul;
unsigned int cnt_mul;
int ret = -EINVAL;
+ int clk_div_restri = 0;
if (target_speed > I2C_MAX_HIGH_SPEED_MODE_FREQ)
target_speed = I2C_MAX_HIGH_SPEED_MODE_FREQ;
@@ -735,7 +759,8 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
* optimizing for sample_cnt * step_cnt being minimal
*/
for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) {
- step_cnt = DIV_ROUND_UP(opt_div, sample_cnt);
+ clk_div_restri = mtk_i2c_get_clk_div_restri(i2c, sample_cnt);
+ step_cnt = DIV_ROUND_UP(opt_div + clk_div_restri, sample_cnt);
cnt_mul = step_cnt * sample_cnt;
if (step_cnt > max_step_cnt)
continue;
@@ -749,7 +774,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
best_mul = cnt_mul;
base_sample_cnt = sample_cnt;
base_step_cnt = step_cnt;
- if (best_mul == opt_div)
+ if (best_mul == (opt_div + clk_div_restri))
break;
}
}
@@ -760,7 +785,8 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
sample_cnt = base_sample_cnt;
step_cnt = base_step_cnt;
- if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) {
+ if ((clk_src / (2 * (sample_cnt * step_cnt - clk_div_restri))) >
+ target_speed) {
/* In this case, hardware can't support such
* low i2c_bus_freq
*/
@@ -789,13 +815,16 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
target_speed = i2c->speed_hz;
parent_clk /= i2c->clk_src_div;
- if (i2c->dev_comp->timing_adjust)
- max_clk_div = MAX_CLOCK_DIV;
+ if (i2c->dev_comp->timing_adjust && i2c->dev_comp->ltiming_adjust)
+ max_clk_div = MAX_CLOCK_DIV_5BITS;
+ else if (i2c->dev_comp->timing_adjust)
+ max_clk_div = MAX_CLOCK_DIV_8BITS;
else
max_clk_div = 1;
for (clk_div = 1; clk_div <= max_clk_div; clk_div++) {
clk_src = parent_clk / clk_div;
+ i2c->ac_timing.inter_clk_div = clk_div - 1;
if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) {
/* Set master code speed register */
@@ -842,7 +871,6 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
break;
}
- i2c->ac_timing.inter_clk_div = clk_div - 1;
return 0;
}
@@ -1149,7 +1177,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
int left_num = num;
struct mtk_i2c *i2c = i2c_get_adapdata(adap);
- ret = mtk_i2c_clock_enable(i2c);
+ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret)
return ret;
@@ -1203,7 +1231,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
ret = num;
err_exit:
- mtk_i2c_clock_disable(i2c);
+ clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
return ret;
}
@@ -1281,9 +1309,8 @@ static int mtk_i2c_probe(struct platform_device *pdev)
{
int ret = 0;
struct mtk_i2c *i2c;
- struct clk *clk;
struct resource *res;
- int irq;
+ int i, irq, speed_clk;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
@@ -1329,35 +1356,42 @@ static int mtk_i2c_probe(struct platform_device *pdev)
if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
return -EINVAL;
- i2c->clk_main = devm_clk_get(&pdev->dev, "main");
- if (IS_ERR(i2c->clk_main)) {
+ /* Fill in clk-bulk IDs */
+ for (i = 0; i < I2C_MT65XX_CLK_MAX; i++)
+ i2c->clocks[i].id = i2c_mt65xx_clk_ids[i];
+
+ /* Get clocks one by one, some may be optional */
+ i2c->clocks[I2C_MT65XX_CLK_MAIN].clk = devm_clk_get(&pdev->dev, "main");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk)) {
dev_err(&pdev->dev, "cannot get main clock\n");
- return PTR_ERR(i2c->clk_main);
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk);
}
- i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
- if (IS_ERR(i2c->clk_dma)) {
+ i2c->clocks[I2C_MT65XX_CLK_DMA].clk = devm_clk_get(&pdev->dev, "dma");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk)) {
dev_err(&pdev->dev, "cannot get dma clock\n");
- return PTR_ERR(i2c->clk_dma);
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk);
}
- i2c->clk_arb = devm_clk_get(&pdev->dev, "arb");
- if (IS_ERR(i2c->clk_arb))
- i2c->clk_arb = NULL;
+ i2c->clocks[I2C_MT65XX_CLK_ARB].clk = devm_clk_get_optional(&pdev->dev, "arb");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk))
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk);
- clk = i2c->clk_main;
if (i2c->have_pmic) {
- i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
- if (IS_ERR(i2c->clk_pmic)) {
+ i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = devm_clk_get(&pdev->dev, "pmic");
+ if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk)) {
dev_err(&pdev->dev, "cannot get pmic clock\n");
- return PTR_ERR(i2c->clk_pmic);
+ return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk);
}
- clk = i2c->clk_pmic;
+ speed_clk = I2C_MT65XX_CLK_PMIC;
+ } else {
+ i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = NULL;
+ speed_clk = I2C_MT65XX_CLK_MAIN;
}
strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
- ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
+ ret = mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk));
if (ret) {
dev_err(&pdev->dev, "Failed to set the speed.\n");
return -EINVAL;
@@ -1372,13 +1406,13 @@ static int mtk_i2c_probe(struct platform_device *pdev)
}
}
- ret = mtk_i2c_clock_enable(i2c);
+ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret) {
dev_err(&pdev->dev, "clock enable failed!\n");
return ret;
}
mtk_i2c_init_hw(i2c);
- mtk_i2c_clock_disable(i2c);
+ clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE,
@@ -1423,7 +1457,7 @@ static int mtk_i2c_resume_noirq(struct device *dev)
int ret;
struct mtk_i2c *i2c = dev_get_drvdata(dev);
- ret = mtk_i2c_clock_enable(i2c);
+ ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
if (ret) {
dev_err(dev, "clock enable failed!\n");
return ret;
@@ -1431,7 +1465,7 @@ static int mtk_i2c_resume_noirq(struct device *dev)
mtk_i2c_init_hw(i2c);
- mtk_i2c_clock_disable(i2c);
+ clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
i2c_mark_adapter_resumed(&i2c->adap);
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
index 2ad166355ec9..71aad029425d 100644
--- a/drivers/i2c/busses/i2c-npcm7xx.c
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -781,7 +781,7 @@ static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
/*
* if we are about to read the first byte in blk rd mode,
* don't NACK it. If slave returns zero size HW can't NACK
- * it immidiattly, it will read extra byte and then NACK.
+ * it immediately, it will read extra byte and then NACK.
*/
if (bus->rd_ind == 0 && bus->read_block_use) {
/* set fifo to read one byte, no last: */
@@ -981,7 +981,7 @@ static void npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite,
/*
* npcm_i2c_slave_wr_buf_sync:
* currently slave IF only supports single byte operations.
- * in order to utilyze the npcm HW FIFO, the driver will ask for 16 bytes
+ * in order to utilize the npcm HW FIFO, the driver will ask for 16 bytes
* at a time, pack them in buffer, and then transmit them all together
* to the FIFO and onward to the bus.
* NACK on read will be once reached to bus->adap->quirks->max_read_len.
@@ -1175,7 +1175,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus)
/*
* the i2c module can response to 10 own SA.
* check which one was addressed by the master.
- * repond to the first one.
+ * respond to the first one.
*/
addr = ((i2ccst3 & 0x07) << 7) |
(i2ccst2 & 0x7F);
@@ -1753,8 +1753,8 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap)
/*
* npcm i2c HW allows direct reading of SCL and SDA.
* However, it does not support setting SCL and SDA directly.
- * The recovery function can togle SCL when SDA is low (but not set)
- * Getter functions used internally, and can be used externaly.
+ * The recovery function can toggle SCL when SDA is low (but not set)
+ * Getter functions used internally, and can be used externally.
*/
rinfo->get_scl = npcm_i2c_get_SCL;
rinfo->get_sda = npcm_i2c_get_SDA;
@@ -1768,10 +1768,10 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap)
/*
* npcm_i2c_init_clk: init HW timing parameters.
- * NPCM7XX i2c module timing parameters are depenent on module core clk (APB)
+ * NPCM7XX i2c module timing parameters are dependent on module core clk (APB)
* and bus frequency.
- * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are simetric.
- * 400kHz bus requires assymetric HT and LT. A different equation is recomended
+ * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are symmetric.
+ * 400kHz bus requires asymmetric HT and LT. A different equation is recommended
* by the HW designer, given core clock range (equations in comments below).
*
*/
diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c
index b5055a3cbd93..6920c1b9a126 100644
--- a/drivers/i2c/busses/i2c-nvidia-gpu.c
+++ b/drivers/i2c/busses/i2c-nvidia-gpu.c
@@ -17,6 +17,8 @@
#include <asm/unaligned.h>
+#include "i2c-ccgx-ucsi.h"
+
/* I2C definitions */
#define I2C_MST_CNTL 0x00
#define I2C_MST_CNTL_GEN_START BIT(0)
@@ -266,54 +268,32 @@ static const struct software_node ccgx_node = {
.properties = ccgx_props,
};
-static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
-{
- i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
- sizeof(*i2cd->gpu_ccgx_ucsi),
- GFP_KERNEL);
- if (!i2cd->gpu_ccgx_ucsi)
- return -ENOMEM;
-
- strlcpy(i2cd->gpu_ccgx_ucsi->type, "ccgx-ucsi",
- sizeof(i2cd->gpu_ccgx_ucsi->type));
- i2cd->gpu_ccgx_ucsi->addr = 0x8;
- i2cd->gpu_ccgx_ucsi->irq = irq;
- i2cd->gpu_ccgx_ucsi->swnode = &ccgx_node;
- i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
- return PTR_ERR_OR_ZERO(i2cd->ccgx_client);
-}
-
static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
+ struct device *dev = &pdev->dev;
struct gpu_i2c_dev *i2cd;
int status;
- i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL);
+ i2cd = devm_kzalloc(dev, sizeof(*i2cd), GFP_KERNEL);
if (!i2cd)
return -ENOMEM;
- i2cd->dev = &pdev->dev;
- dev_set_drvdata(&pdev->dev, i2cd);
+ i2cd->dev = dev;
+ dev_set_drvdata(dev, i2cd);
status = pcim_enable_device(pdev);
- if (status < 0) {
- dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status);
- return status;
- }
+ if (status < 0)
+ return dev_err_probe(dev, status, "pcim_enable_device failed\n");
pci_set_master(pdev);
i2cd->regs = pcim_iomap(pdev, 0, 0);
- if (!i2cd->regs) {
- dev_err(&pdev->dev, "pcim_iomap failed\n");
- return -ENOMEM;
- }
+ if (!i2cd->regs)
+ return dev_err_probe(dev, -ENOMEM, "pcim_iomap failed\n");
status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
- if (status < 0) {
- dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status);
- return status;
- }
+ if (status < 0)
+ return dev_err_probe(dev, status, "pci_alloc_irq_vectors err\n");
gpu_enable_i2c_bus(i2cd);
@@ -323,21 +303,21 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sizeof(i2cd->adapter.name));
i2cd->adapter.algo = &gpu_i2c_algorithm;
i2cd->adapter.quirks = &gpu_i2c_quirks;
- i2cd->adapter.dev.parent = &pdev->dev;
+ i2cd->adapter.dev.parent = dev;
status = i2c_add_adapter(&i2cd->adapter);
if (status < 0)
goto free_irq_vectors;
- status = gpu_populate_client(i2cd, pdev->irq);
- if (status < 0) {
- dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status);
+ i2cd->ccgx_client = i2c_new_ccgx_ucsi(&i2cd->adapter, pdev->irq, &ccgx_node);
+ if (IS_ERR(i2cd->ccgx_client)) {
+ status = dev_err_probe(dev, PTR_ERR(i2cd->ccgx_client), "register UCSI failed\n");
goto del_adapter;
}
- pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_put_autosuspend(&pdev->dev);
- pm_runtime_allow(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put_autosuspend(dev);
+ pm_runtime_allow(dev);
return 0;
@@ -350,7 +330,7 @@ free_irq_vectors:
static void gpu_i2c_remove(struct pci_dev *pdev)
{
- struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev);
+ struct gpu_i2c_dev *i2cd = pci_get_drvdata(pdev);
pm_runtime_get_noresume(i2cd->dev);
i2c_del_adapter(&i2cd->adapter);
diff --git a/drivers/i2c/busses/i2c-pasemi-core.c b/drivers/i2c/busses/i2c-pasemi-core.c
index 4e161a4089d8..7728c8460dc0 100644
--- a/drivers/i2c/busses/i2c-pasemi-core.c
+++ b/drivers/i2c/busses/i2c-pasemi-core.c
@@ -333,7 +333,6 @@ int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)
smbus->adapter.owner = THIS_MODULE;
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
"PA Semi SMBus adapter (%s)", dev_name(smbus->dev));
- smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
diff --git a/drivers/i2c/busses/i2c-pasemi-pci.c b/drivers/i2c/busses/i2c-pasemi-pci.c
index 1ab1f28744fb..cfc89e04eb94 100644
--- a/drivers/i2c/busses/i2c-pasemi-pci.c
+++ b/drivers/i2c/busses/i2c-pasemi-pci.c
@@ -56,6 +56,7 @@ static int pasemi_smb_pci_probe(struct pci_dev *dev,
if (!smbus->ioaddr)
return -EBUSY;
+ smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
error = pasemi_i2c_common_probe(smbus);
if (error)
return error;
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 8c1b31ed0c42..ac8e7d60672a 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -77,6 +77,7 @@
/* SB800 constants */
#define SB800_PIIX4_SMB_IDX 0xcd6
+#define SB800_PIIX4_SMB_MAP_SIZE 2
#define KERNCZ_IMC_IDX 0x3e
#define KERNCZ_IMC_DATA 0x3f
@@ -97,6 +98,9 @@
#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18
#define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3
+#define SB800_PIIX4_FCH_PM_ADDR 0xFED80300
+#define SB800_PIIX4_FCH_PM_SIZE 8
+
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
@@ -155,6 +159,12 @@ static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = {
};
static const char *piix4_aux_port_name_sb800 = " port 1";
+struct sb800_mmio_cfg {
+ void __iomem *addr;
+ struct resource *res;
+ bool use_mmio;
+};
+
struct i2c_piix4_adapdata {
unsigned short smba;
@@ -162,8 +172,75 @@ struct i2c_piix4_adapdata {
bool sb800_main;
bool notify_imc;
u8 port; /* Port number, shifted */
+ struct sb800_mmio_cfg mmio_cfg;
};
+static int piix4_sb800_region_request(struct device *dev,
+ struct sb800_mmio_cfg *mmio_cfg)
+{
+ if (mmio_cfg->use_mmio) {
+ struct resource *res;
+ void __iomem *addr;
+
+ res = request_mem_region_muxed(SB800_PIIX4_FCH_PM_ADDR,
+ SB800_PIIX4_FCH_PM_SIZE,
+ "sb800_piix4_smb");
+ if (!res) {
+ dev_err(dev,
+ "SMBus base address memory region 0x%x already in use.\n",
+ SB800_PIIX4_FCH_PM_ADDR);
+ return -EBUSY;
+ }
+
+ addr = ioremap(SB800_PIIX4_FCH_PM_ADDR,
+ SB800_PIIX4_FCH_PM_SIZE);
+ if (!addr) {
+ release_resource(res);
+ dev_err(dev, "SMBus base address mapping failed.\n");
+ return -ENOMEM;
+ }
+
+ mmio_cfg->res = res;
+ mmio_cfg->addr = addr;
+
+ return 0;
+ }
+
+ if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE,
+ "sb800_piix4_smb")) {
+ dev_err(dev,
+ "SMBus base address index region 0x%x already in use.\n",
+ SB800_PIIX4_SMB_IDX);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void piix4_sb800_region_release(struct device *dev,
+ struct sb800_mmio_cfg *mmio_cfg)
+{
+ if (mmio_cfg->use_mmio) {
+ iounmap(mmio_cfg->addr);
+ release_resource(mmio_cfg->res);
+ return;
+ }
+
+ release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE);
+}
+
+static bool piix4_sb800_use_mmio(struct pci_dev *PIIX4_dev)
+{
+ /*
+ * cd6h/cd7h port I/O accesses can be disabled on AMD processors
+ * w/ SMBus PCI revision ID 0x51 or greater. MMIO is supported on
+ * the same processors and is the recommended access method.
+ */
+ return (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
+ PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
+ PIIX4_dev->revision >= 0x51);
+}
+
static int piix4_setup(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id)
{
@@ -263,12 +340,61 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
return piix4_smba;
}
+static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev,
+ u8 smb_en,
+ u8 aux,
+ u8 *smb_en_status,
+ unsigned short *piix4_smba)
+{
+ struct sb800_mmio_cfg mmio_cfg;
+ u8 smba_en_lo;
+ u8 smba_en_hi;
+ int retval;
+
+ mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev);
+ retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg);
+ if (retval)
+ return retval;
+
+ if (mmio_cfg.use_mmio) {
+ smba_en_lo = ioread8(mmio_cfg.addr);
+ smba_en_hi = ioread8(mmio_cfg.addr + 1);
+ } else {
+ outb_p(smb_en, SB800_PIIX4_SMB_IDX);
+ smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
+ outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
+ smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
+ }
+
+ piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg);
+
+ if (!smb_en) {
+ *smb_en_status = smba_en_lo & 0x10;
+ *piix4_smba = smba_en_hi << 8;
+ if (aux)
+ *piix4_smba |= 0x20;
+ } else {
+ *smb_en_status = smba_en_lo & 0x01;
+ *piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
+ }
+
+ if (!*smb_en_status) {
+ dev_err(&PIIX4_dev->dev,
+ "SMBus Host Controller not enabled!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id, u8 aux)
{
unsigned short piix4_smba;
- u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel;
+ u8 smb_en, smb_en_status, port_sel;
u8 i2ccfg, i2ccfg_offset = 0x10;
+ struct sb800_mmio_cfg mmio_cfg;
+ int retval;
/* SB800 and later SMBus does not support forcing address */
if (force || force_addr) {
@@ -290,35 +416,11 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
else
smb_en = (aux) ? 0x28 : 0x2c;
- if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) {
- dev_err(&PIIX4_dev->dev,
- "SMB base address index region 0x%x already in use.\n",
- SB800_PIIX4_SMB_IDX);
- return -EBUSY;
- }
-
- outb_p(smb_en, SB800_PIIX4_SMB_IDX);
- smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
- outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
- smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
+ retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, aux, &smb_en_status,
+ &piix4_smba);
- release_region(SB800_PIIX4_SMB_IDX, 2);
-
- if (!smb_en) {
- smb_en_status = smba_en_lo & 0x10;
- piix4_smba = smba_en_hi << 8;
- if (aux)
- piix4_smba |= 0x20;
- } else {
- smb_en_status = smba_en_lo & 0x01;
- piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
- }
-
- if (!smb_en_status) {
- dev_err(&PIIX4_dev->dev,
- "SMBus Host Controller not enabled!\n");
- return -ENODEV;
- }
+ if (retval)
+ return retval;
if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
return -ENODEV;
@@ -371,10 +473,11 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
}
} else {
- if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2,
- "sb800_piix4_smb")) {
+ mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev);
+ retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg);
+ if (retval) {
release_region(piix4_smba, SMBIOSIZE);
- return -EBUSY;
+ return retval;
}
outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
@@ -384,7 +487,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
SB800_PIIX4_PORT_IDX;
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
- release_region(SB800_PIIX4_SMB_IDX, 2);
+ piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg);
}
dev_info(&PIIX4_dev->dev,
@@ -662,6 +765,29 @@ static void piix4_imc_wakeup(void)
release_region(KERNCZ_IMC_IDX, 2);
}
+static int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg)
+{
+ u8 smba_en_lo, val;
+
+ if (mmio_cfg->use_mmio) {
+ smba_en_lo = ioread8(mmio_cfg->addr + piix4_port_sel_sb800);
+ val = (smba_en_lo & ~piix4_port_mask_sb800) | port;
+ if (smba_en_lo != val)
+ iowrite8(val, mmio_cfg->addr + piix4_port_sel_sb800);
+
+ return (smba_en_lo & piix4_port_mask_sb800);
+ }
+
+ outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
+ smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
+
+ val = (smba_en_lo & ~piix4_port_mask_sb800) | port;
+ if (smba_en_lo != val)
+ outb_p(val, SB800_PIIX4_SMB_IDX + 1);
+
+ return (smba_en_lo & piix4_port_mask_sb800);
+}
+
/*
* Handles access to multiple SMBus ports on the SB800.
* The port is selected by bits 2:1 of the smb_en register (0x2c).
@@ -678,12 +804,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
unsigned short piix4_smba = adapdata->smba;
int retries = MAX_TIMEOUT;
int smbslvcnt;
- u8 smba_en_lo;
- u8 port;
+ u8 prev_port;
int retval;
- if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb"))
- return -EBUSY;
+ retval = piix4_sb800_region_request(&adap->dev, &adapdata->mmio_cfg);
+ if (retval)
+ return retval;
/* Request the SMBUS semaphore, avoid conflicts with the IMC */
smbslvcnt = inb_p(SMBSLVCNT);
@@ -738,18 +864,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
}
}
- outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX);
- smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
-
- port = adapdata->port;
- if ((smba_en_lo & piix4_port_mask_sb800) != port)
- outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port,
- SB800_PIIX4_SMB_IDX + 1);
+ prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg);
retval = piix4_access(adap, addr, flags, read_write,
command, size, data);
- outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1);
+ piix4_sb800_port_sel(prev_port, &adapdata->mmio_cfg);
/* Release the semaphore */
outb_p(smbslvcnt | 0x20, SMBSLVCNT);
@@ -758,7 +878,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
piix4_imc_wakeup();
release:
- release_region(SB800_PIIX4_SMB_IDX, 2);
+ piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg);
return retval;
}
@@ -836,6 +956,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
return -ENOMEM;
}
+ adapdata->mmio_cfg.use_mmio = piix4_sb800_use_mmio(dev);
adapdata->smba = smba;
adapdata->sb800_main = sb800_main;
adapdata->port = port << piix4_port_shift_sb800;
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index cf54f1cb4c57..5c7cc862f08f 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
-// Copyright (c) 2017-20 Linaro Limited.
+// Copyright (c) 2017-2022 Linaro Limited.
#include <linux/clk.h>
#include <linux/completion.h>
@@ -776,6 +776,7 @@ static const struct of_device_id cci_dt_match[] = {
{ .compatible = "qcom,msm8996-cci", .data = &cci_v2_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},
{}
};
MODULE_DEVICE_TABLE(of, cci_dt_match);
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 6d635a7c104c..fc1dcc19f2a1 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -3,7 +3,9 @@
#include <linux/acpi.h>
#include <linux/clk.h>
+#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
+#include <linux/dma/qcom-gpi-dma.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -48,6 +50,9 @@
#define LOW_COUNTER_SHFT 10
#define CYCLE_COUNTER_MSK GENMASK(9, 0)
+#define I2C_PACK_TX BIT(0)
+#define I2C_PACK_RX BIT(1)
+
enum geni_i2c_err_code {
GP_IRQ0,
NACK,
@@ -89,6 +94,9 @@ struct geni_i2c_dev {
void *dma_buf;
size_t xfer_len;
dma_addr_t dma_addr;
+ struct dma_chan *tx_c;
+ struct dma_chan *rx_c;
+ bool gpi_mode;
};
struct geni_i2c_err_log {
@@ -456,12 +464,207 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
return gi2c->err;
}
+static void i2c_gpi_cb_result(void *cb, const struct dmaengine_result *result)
+{
+ struct geni_i2c_dev *gi2c = cb;
+
+ if (result->result != DMA_TRANS_NOERROR) {
+ dev_err(gi2c->se.dev, "DMA txn failed:%d\n", result->result);
+ gi2c->err = -EIO;
+ } else if (result->residue) {
+ dev_dbg(gi2c->se.dev, "DMA xfer has pending: %d\n", result->residue);
+ }
+
+ complete(&gi2c->done);
+}
+
+static void geni_i2c_gpi_unmap(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
+ void *tx_buf, dma_addr_t tx_addr,
+ void *rx_buf, dma_addr_t rx_addr)
+{
+ if (tx_buf) {
+ dma_unmap_single(gi2c->se.dev->parent, tx_addr, msg->len, DMA_TO_DEVICE);
+ i2c_put_dma_safe_msg_buf(tx_buf, msg, false);
+ }
+
+ if (rx_buf) {
+ dma_unmap_single(gi2c->se.dev->parent, rx_addr, msg->len, DMA_FROM_DEVICE);
+ i2c_put_dma_safe_msg_buf(rx_buf, msg, false);
+ }
+}
+
+static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
+ struct dma_slave_config *config, dma_addr_t *dma_addr_p,
+ void **buf, unsigned int op, struct dma_chan *dma_chan)
+{
+ struct gpi_i2c_config *peripheral;
+ unsigned int flags;
+ void *dma_buf;
+ dma_addr_t addr;
+ enum dma_data_direction map_dirn;
+ enum dma_transfer_direction dma_dirn;
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+
+ peripheral = config->peripheral_config;
+
+ dma_buf = i2c_get_dma_safe_msg_buf(msg, 1);
+ if (!dma_buf)
+ return -ENOMEM;
+
+ if (op == I2C_WRITE)
+ map_dirn = DMA_TO_DEVICE;
+ else
+ map_dirn = DMA_FROM_DEVICE;
+
+ addr = dma_map_single(gi2c->se.dev->parent, dma_buf, msg->len, map_dirn);
+ if (dma_mapping_error(gi2c->se.dev->parent, addr)) {
+ i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
+ return -ENOMEM;
+ }
+
+ /* set the length as message for rx txn */
+ peripheral->rx_len = msg->len;
+ peripheral->op = op;
+
+ ret = dmaengine_slave_config(dma_chan, config);
+ if (ret) {
+ dev_err(gi2c->se.dev, "dma config error: %d for op:%d\n", ret, op);
+ goto err_config;
+ }
+
+ peripheral->set_config = 0;
+ peripheral->multi_msg = true;
+ flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+
+ if (op == I2C_WRITE)
+ dma_dirn = DMA_MEM_TO_DEV;
+ else
+ dma_dirn = DMA_DEV_TO_MEM;
+
+ desc = dmaengine_prep_slave_single(dma_chan, addr, msg->len, dma_dirn, flags);
+ if (!desc) {
+ dev_err(gi2c->se.dev, "prep_slave_sg failed\n");
+ ret = -EIO;
+ goto err_config;
+ }
+
+ desc->callback_result = i2c_gpi_cb_result;
+ desc->callback_param = gi2c;
+
+ dmaengine_submit(desc);
+ *dma_addr_p = addr;
+
+ return 0;
+
+err_config:
+ dma_unmap_single(gi2c->se.dev->parent, addr, msg->len, map_dirn);
+ i2c_put_dma_safe_msg_buf(dma_buf, msg, false);
+ return ret;
+}
+
+static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], int num)
+{
+ struct dma_slave_config config = {};
+ struct gpi_i2c_config peripheral = {};
+ int i, ret = 0, timeout;
+ dma_addr_t tx_addr, rx_addr;
+ void *tx_buf = NULL, *rx_buf = NULL;
+ const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
+
+ config.peripheral_config = &peripheral;
+ config.peripheral_size = sizeof(peripheral);
+
+ peripheral.pack_enable = I2C_PACK_TX | I2C_PACK_RX;
+ peripheral.cycle_count = itr->t_cycle_cnt;
+ peripheral.high_count = itr->t_high_cnt;
+ peripheral.low_count = itr->t_low_cnt;
+ peripheral.clk_div = itr->clk_div;
+ peripheral.set_config = 1;
+ peripheral.multi_msg = false;
+
+ for (i = 0; i < num; i++) {
+ gi2c->cur = &msgs[i];
+ gi2c->err = 0;
+ dev_dbg(gi2c->se.dev, "msg[%d].len:%d\n", i, gi2c->cur->len);
+
+ peripheral.stretch = 0;
+ if (i < num - 1)
+ peripheral.stretch = 1;
+
+ peripheral.addr = msgs[i].addr;
+
+ if (msgs[i].flags & I2C_M_RD) {
+ ret = geni_i2c_gpi(gi2c, &msgs[i], &config,
+ &rx_addr, &rx_buf, I2C_READ, gi2c->rx_c);
+ if (ret)
+ goto err;
+ }
+
+ ret = geni_i2c_gpi(gi2c, &msgs[i], &config,
+ &tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c);
+ if (ret)
+ goto err;
+
+ if (msgs[i].flags & I2C_M_RD)
+ dma_async_issue_pending(gi2c->rx_c);
+ dma_async_issue_pending(gi2c->tx_c);
+
+ timeout = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
+ if (!timeout) {
+ dev_err(gi2c->se.dev, "I2C timeout gpi flags:%d addr:0x%x\n",
+ gi2c->cur->flags, gi2c->cur->addr);
+ gi2c->err = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (gi2c->err) {
+ ret = gi2c->err;
+ goto err;
+ }
+
+ geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
+ }
+
+ return num;
+
+err:
+ dev_err(gi2c->se.dev, "GPI transfer failed: %d\n", ret);
+ dmaengine_terminate_sync(gi2c->rx_c);
+ dmaengine_terminate_sync(gi2c->tx_c);
+ geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr);
+ return ret;
+}
+
+static int geni_i2c_fifo_xfer(struct geni_i2c_dev *gi2c,
+ struct i2c_msg msgs[], int num)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < num; i++) {
+ u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
+
+ m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
+
+ gi2c->cur = &msgs[i];
+ if (msgs[i].flags & I2C_M_RD)
+ ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
+ else
+ ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
+
+ if (ret)
+ return ret;
+ }
+
+ return num;
+}
+
static int geni_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
- int i, ret;
+ int ret;
gi2c->err = 0;
reinit_completion(&gi2c->done);
@@ -475,28 +678,17 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
}
qcom_geni_i2c_conf(gi2c);
- for (i = 0; i < num; i++) {
- u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
-
- m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
-
- gi2c->cur = &msgs[i];
- if (msgs[i].flags & I2C_M_RD)
- ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
- else
- ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
- if (ret)
- break;
- }
- if (ret == 0)
- ret = num;
+ if (gi2c->gpi_mode)
+ ret = geni_i2c_gpi_xfer(gi2c, msgs, num);
+ else
+ ret = geni_i2c_fifo_xfer(gi2c, msgs, num);
pm_runtime_mark_last_busy(gi2c->se.dev);
pm_runtime_put_autosuspend(gi2c->se.dev);
gi2c->cur = NULL;
gi2c->err = 0;
- return ret;
+ return num;
}
static u32 geni_i2c_func(struct i2c_adapter *adap)
@@ -517,11 +709,50 @@ static const struct acpi_device_id geni_i2c_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);
#endif
+static void release_gpi_dma(struct geni_i2c_dev *gi2c)
+{
+ if (gi2c->rx_c)
+ dma_release_channel(gi2c->rx_c);
+
+ if (gi2c->tx_c)
+ dma_release_channel(gi2c->tx_c);
+}
+
+static int setup_gpi_dma(struct geni_i2c_dev *gi2c)
+{
+ int ret;
+
+ geni_se_select_mode(&gi2c->se, GENI_GPI_DMA);
+ gi2c->tx_c = dma_request_chan(gi2c->se.dev, "tx");
+ if (IS_ERR(gi2c->tx_c)) {
+ ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->tx_c),
+ "Failed to get tx DMA ch\n");
+ if (ret < 0)
+ goto err_tx;
+ }
+
+ gi2c->rx_c = dma_request_chan(gi2c->se.dev, "rx");
+ if (IS_ERR(gi2c->rx_c)) {
+ ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->rx_c),
+ "Failed to get rx DMA ch\n");
+ if (ret < 0)
+ goto err_rx;
+ }
+
+ dev_dbg(gi2c->se.dev, "Grabbed GPI dma channels\n");
+ return 0;
+
+err_rx:
+ dma_release_channel(gi2c->tx_c);
+err_tx:
+ return ret;
+}
+
static int geni_i2c_probe(struct platform_device *pdev)
{
struct geni_i2c_dev *gi2c;
struct resource *res;
- u32 proto, tx_depth;
+ u32 proto, tx_depth, fifo_disable;
int ret;
struct device *dev = &pdev->dev;
@@ -601,27 +832,43 @@ static int geni_i2c_probe(struct platform_device *pdev)
return ret;
}
proto = geni_se_read_proto(&gi2c->se);
- tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
if (proto != GENI_SE_I2C) {
dev_err(dev, "Invalid proto %d\n", proto);
geni_se_resources_off(&gi2c->se);
return -ENXIO;
}
- gi2c->tx_wm = tx_depth - 1;
- geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
- geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
- true, true, true);
+
+ fifo_disable = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE;
+ if (fifo_disable) {
+ /* FIFO is disabled, so we can only use GPI DMA */
+ gi2c->gpi_mode = true;
+ ret = setup_gpi_dma(gi2c);
+ if (ret) {
+ dev_err(dev, "Failed to setup GPI DMA mode:%d ret\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "Using GPI DMA mode for I2C\n");
+ } else {
+ gi2c->gpi_mode = false;
+ tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
+ gi2c->tx_wm = tx_depth - 1;
+ geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
+ geni_se_config_packing(&gi2c->se, BITS_PER_BYTE,
+ PACKING_BYTES_PW, true, true, true);
+
+ dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
+ }
+
ret = geni_se_resources_off(&gi2c->se);
if (ret) {
dev_err(dev, "Error turning off resources %d\n", ret);
- return ret;
+ goto err_dma;
}
ret = geni_icc_disable(&gi2c->se);
if (ret)
- return ret;
-
- dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
+ goto err_dma;
gi2c->suspended = 1;
pm_runtime_set_suspended(gi2c->se.dev);
@@ -633,12 +880,16 @@ static int geni_i2c_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "Error adding i2c adapter %d\n", ret);
pm_runtime_disable(gi2c->se.dev);
- return ret;
+ goto err_dma;
}
dev_dbg(dev, "Geni-I2C adaptor successfully added\n");
return 0;
+
+err_dma:
+ release_gpi_dma(gi2c);
+ return ret;
}
static int geni_i2c_remove(struct platform_device *pdev)
@@ -646,6 +897,7 @@ static int geni_i2c_remove(struct platform_device *pdev)
struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
i2c_del_adapter(&gi2c->adap);
+ release_gpi_dma(gi2c);
pm_runtime_disable(gi2c->se.dev);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index f71c730f9838..0db3d7559066 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -1008,6 +1008,7 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
{ .compatible = "renesas,rcar-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 },
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
{ .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 },
+ { .compatible = "renesas,rcar-gen4-i2c", .data = (void *)I2C_RCAR_GEN3 },
{},
};
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index 8dfd27dc6149..cded77e06670 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -88,11 +88,6 @@
#define RIIC_INIT_MSG -1
-enum riic_type {
- RIIC_RZ_A,
- RIIC_RZ_G2L,
-};
-
struct riic_dev {
void __iomem *base;
u8 *buf;
@@ -396,6 +391,11 @@ static struct riic_irq_desc riic_irqs[] = {
{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
};
+static void riic_reset_control_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
static int riic_i2c_probe(struct platform_device *pdev)
{
struct riic_dev *riic;
@@ -404,7 +404,6 @@ static int riic_i2c_probe(struct platform_device *pdev)
struct i2c_timings i2c_t;
struct reset_control *rstc;
int i, ret;
- enum riic_type type;
riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
if (!riic)
@@ -421,16 +420,18 @@ static int riic_i2c_probe(struct platform_device *pdev)
return PTR_ERR(riic->clk);
}
- type = (enum riic_type)of_device_get_match_data(&pdev->dev);
- if (type == RIIC_RZ_G2L) {
- rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
- if (IS_ERR(rstc)) {
- dev_err(&pdev->dev, "Error: missing reset ctrl\n");
- return PTR_ERR(rstc);
- }
+ rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
+ "Error: missing reset ctrl\n");
- reset_control_deassert(rstc);
- }
+ ret = reset_control_deassert(rstc);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev, riic_reset_control_assert, rstc);
+ if (ret)
+ return ret;
for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) {
ret = platform_get_irq(pdev, riic_irqs[i].res_num);
@@ -492,8 +493,7 @@ static int riic_i2c_remove(struct platform_device *pdev)
}
static const struct of_device_id riic_i2c_dt_ids[] = {
- { .compatible = "renesas,riic-r9a07g044", .data = (void *)RIIC_RZ_G2L },
- { .compatible = "renesas,riic-rz", .data = (void *)RIIC_RZ_A },
+ { .compatible = "renesas,riic-rz", },
{ /* Sentinel */ },
};
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 03cea102ab76..2941e42aa6a0 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -1233,6 +1233,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
return err;
i2c_dev->msg_buf = msg->buf;
+
+ /* The condition true implies smbus block read and len is already read */
+ if (msg->flags & I2C_M_RECV_LEN && end_state != MSG_END_CONTINUE)
+ i2c_dev->msg_buf = msg->buf + 1;
+
i2c_dev->msg_buf_remaining = msg->len;
i2c_dev->msg_err = I2C_ERR_NONE;
i2c_dev->msg_read = !!(msg->flags & I2C_M_RD);
@@ -1389,6 +1394,15 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
else
end_type = MSG_END_REPEAT_START;
}
+ /* If M_RECV_LEN use ContinueXfer to read the first byte */
+ if (msgs[i].flags & I2C_M_RECV_LEN) {
+ ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], MSG_END_CONTINUE);
+ if (ret)
+ break;
+ /* Set the read byte as msg len */
+ msgs[i].len = msgs[i].buf[0];
+ dev_dbg(i2c_dev->dev, "reading %d bytes\n", msgs[i].len);
+ }
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
if (ret)
break;
@@ -1416,10 +1430,10 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap)
{
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
- I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
+ I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
if (i2c_dev->hw->has_continue_xfer_support)
- ret |= I2C_FUNC_NOSTART;
+ ret |= I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
return ret;
}
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index eb789cfb9973..ffefe3c482e9 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -734,7 +734,6 @@ static const struct i2c_adapter_quirks xiic_quirks = {
static const struct i2c_adapter xiic_adapter = {
.owner = THIS_MODULE,
- .name = DRIVER_NAME,
.class = I2C_CLASS_DEPRECATED,
.algo = &xiic_algorithm,
.quirks = &xiic_quirks,
@@ -771,6 +770,8 @@ static int xiic_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
+ snprintf(i2c->adap.name, sizeof(i2c->adap.name),
+ DRIVER_NAME " %s", pdev->name);
mutex_init(&i2c->lock);
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index 85ed4c1d4924..08b561f0709d 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -236,7 +236,8 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
struct acpi_device *adapter_adev;
/* The adapter must be present */
- if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev))
+ adapter_adev = acpi_fetch_acpi_dev(lookup.adapter_handle);
+ if (!adapter_adev)
return -ENODEV;
if (acpi_bus_get_status(adapter_adev) ||
!adapter_adev->status.present)
@@ -275,13 +276,10 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_adapter *adapter = data;
- struct acpi_device *adev;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
struct i2c_board_info info;
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
-
- if (i2c_acpi_get_info(adev, &info, adapter, NULL))
+ if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL))
return AE_OK;
i2c_acpi_register_device(adapter, adev, &info);
@@ -341,12 +339,9 @@ static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct i2c_acpi_lookup *lookup = data;
- struct acpi_device *adev;
-
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
- if (i2c_acpi_do_lookup(adev, lookup))
+ if (!adev || i2c_acpi_do_lookup(adev, lookup))
return AE_OK;
if (lookup->search_handle != lookup->adapter_handle)
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 2c59dd748a49..d43db2c3876e 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1424,7 +1424,7 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
if (irq <= 0)
return -ENXIO;
- generic_handle_irq(irq);
+ generic_handle_irq_safe(irq);
return 0;
}
@@ -1479,7 +1479,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
goto out_list;
}
- res = of_i2c_setup_smbus_alert(adap);
+ res = i2c_setup_smbus_alert(adap);
if (res)
goto out_reg;
diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c
index 1589179d5eb9..e3765e12f93b 100644
--- a/drivers/i2c/i2c-core-slave.c
+++ b/drivers/i2c/i2c-core-slave.c
@@ -14,6 +14,9 @@
#include "i2c-core.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/i2c_slave.h>
+
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
{
int ret;
@@ -79,6 +82,18 @@ int i2c_slave_unregister(struct i2c_client *client)
}
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
+int i2c_slave_event(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val)
+{
+ int ret = client->slave_cb(client, event, val);
+
+ if (trace_i2c_slave_enabled())
+ trace_i2c_slave(client, event, val, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(i2c_slave_event);
+
/**
* i2c_detect_slave_mode - detect operation mode
* @dev: The device owning the bus
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index e5b2d1465e7e..e3b96fc53b5c 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include "i2c-core.h"
@@ -701,13 +702,17 @@ struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter,
}
EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device);
-#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
-int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
+#if IS_ENABLED(CONFIG_I2C_SMBUS)
+int i2c_setup_smbus_alert(struct i2c_adapter *adapter)
{
+ struct device *parent = adapter->dev.parent;
int irq;
- irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
- "smbus_alert");
+ /* Adapter instantiated without parent, skip the SMBus alert setup */
+ if (!parent)
+ return 0;
+
+ irq = device_property_match_string(parent, "interrupt-names", "smbus_alert");
if (irq == -EINVAL || irq == -ENODATA)
return 0;
else if (irq < 0)
@@ -715,5 +720,4 @@ int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL));
}
-EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
#endif
diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h
index 8ce261167a2d..87e2c914f1c5 100644
--- a/drivers/i2c/i2c-core.h
+++ b/drivers/i2c/i2c-core.h
@@ -86,3 +86,12 @@ void of_i2c_register_devices(struct i2c_adapter *adap);
static inline void of_i2c_register_devices(struct i2c_adapter *adap) { }
#endif
extern struct notifier_block i2c_of_notifier;
+
+#if IS_ENABLED(CONFIG_I2C_SMBUS)
+int i2c_setup_smbus_alert(struct i2c_adapter *adap);
+#else
+static inline int i2c_setup_smbus_alert(struct i2c_adapter *adap)
+{
+ return 0;
+}
+#endif
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index d3d06e3b4f3b..775332945ad0 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -13,7 +13,7 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of_irq.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -128,7 +128,8 @@ static int smbalert_probe(struct i2c_client *ara,
if (setup) {
irq = setup->irq;
} else {
- irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
+ irq = fwnode_irq_get_byname(dev_fwnode(adapter->dev.parent),
+ "smbus_alert");
if (irq <= 0)
return irq;
}
diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c
index 5365199a31f4..f7a7405d4350 100644
--- a/drivers/i2c/muxes/i2c-demux-pinctrl.c
+++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c
@@ -261,7 +261,7 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
err = device_create_file(&pdev->dev, &dev_attr_available_masters);
if (err)
- goto err_rollback;
+ goto err_rollback_activation;
err = device_create_file(&pdev->dev, &dev_attr_current_master);
if (err)
@@ -271,8 +271,9 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
err_rollback_available:
device_remove_file(&pdev->dev, &dev_attr_available_masters);
-err_rollback:
+err_rollback_activation:
i2c_demux_deactivate_master(priv);
+err_rollback:
for (j = 0; j < i; j++) {
of_node_put(priv->chan[j].parent_np);
of_changeset_destroy(&priv->chan[j].chgset);
diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
index 1ef421818d3a..ced1c6ead52a 100644
--- a/include/linux/i2c-smbus.h
+++ b/include/linux/i2c-smbus.h
@@ -30,14 +30,6 @@ struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter,
struct i2c_smbus_alert_setup *setup);
int i2c_handle_smbus_alert(struct i2c_client *ara);
-#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
-int of_i2c_setup_smbus_alert(struct i2c_adapter *adap);
-#else
-static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
-{
- return 0;
-}
-#endif
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_I2C_SLAVE)
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter);
void i2c_free_slave_host_notify_device(struct i2c_client *client);
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 7d4f52ceb7b5..fbda5ada2afc 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -392,12 +392,8 @@ enum i2c_slave_event {
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
int i2c_slave_unregister(struct i2c_client *client);
bool i2c_detect_slave_mode(struct device *dev);
-
-static inline int i2c_slave_event(struct i2c_client *client,
- enum i2c_slave_event event, u8 *val)
-{
- return client->slave_cb(client, event, val);
-}
+int i2c_slave_event(struct i2c_client *client,
+ enum i2c_slave_event event, u8 *val);
#else
static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
#endif
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 8359c50f9988..ec5f71f7135b 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -262,6 +262,8 @@ resource_union(struct resource *r1, struct resource *r2, struct resource *r)
#define request_muxed_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), IORESOURCE_MUXED)
#define __request_mem_region(start,n,name, excl) __request_region(&iomem_resource, (start), (n), (name), excl)
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
+#define request_mem_region_muxed(start, n, name) \
+ __request_region(&iomem_resource, (start), (n), (name), IORESOURCE_MUXED)
#define request_mem_region_exclusive(start,n,name) \
__request_region(&iomem_resource, (start), (n), (name), IORESOURCE_EXCLUSIVE)
#define rename_region(region, newname) do { (region)->name = (newname); } while (0)
diff --git a/include/linux/property.h b/include/linux/property.h
index 7399a0b45f98..95d56a562b6a 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -121,6 +121,7 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode);
void fwnode_handle_put(struct fwnode_handle *fwnode);
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index);
+int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name);
void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index);
diff --git a/include/trace/events/i2c_slave.h b/include/trace/events/i2c_slave.h
new file mode 100644
index 000000000000..811166abbe3a
--- /dev/null
+++ b/include/trace/events/i2c_slave.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * I2C slave tracepoints
+ *
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM i2c_slave
+
+#if !defined(_TRACE_I2C_SLAVE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_I2C_SLAVE_H
+
+#include <linux/i2c.h>
+#include <linux/tracepoint.h>
+
+TRACE_DEFINE_ENUM(I2C_SLAVE_READ_REQUESTED);
+TRACE_DEFINE_ENUM(I2C_SLAVE_WRITE_REQUESTED);
+TRACE_DEFINE_ENUM(I2C_SLAVE_READ_PROCESSED);
+TRACE_DEFINE_ENUM(I2C_SLAVE_WRITE_RECEIVED);
+TRACE_DEFINE_ENUM(I2C_SLAVE_STOP);
+
+#define show_event_type(type) \
+ __print_symbolic(type, \
+ { I2C_SLAVE_READ_REQUESTED, "RD_REQ" }, \
+ { I2C_SLAVE_WRITE_REQUESTED, "WR_REQ" }, \
+ { I2C_SLAVE_READ_PROCESSED, "RD_PRO" }, \
+ { I2C_SLAVE_WRITE_RECEIVED, "WR_RCV" }, \
+ { I2C_SLAVE_STOP, " STOP" })
+
+TRACE_EVENT(i2c_slave,
+ TP_PROTO(const struct i2c_client *client, enum i2c_slave_event event,
+ __u8 *val, int cb_ret),
+ TP_ARGS(client, event, val, cb_ret),
+ TP_STRUCT__entry(
+ __field(int, adapter_nr )
+ __field(int, ret )
+ __field(__u16, addr )
+ __field(__u16, len )
+ __field(enum i2c_slave_event, event )
+ __array(__u8, buf, 1) ),
+
+ TP_fast_assign(
+ __entry->adapter_nr = client->adapter->nr;
+ __entry->addr = client->addr;
+ __entry->event = event;
+ __entry->ret = cb_ret;
+ switch (event) {
+ case I2C_SLAVE_READ_REQUESTED:
+ case I2C_SLAVE_READ_PROCESSED:
+ case I2C_SLAVE_WRITE_RECEIVED:
+ __entry->len = 1;
+ memcpy(__entry->buf, val, __entry->len);
+ break;
+ default:
+ __entry->len = 0;
+ break;
+ }
+ ),
+ TP_printk("i2c-%d a=%03x ret=%d %s [%*phD]",
+ __entry->adapter_nr, __entry->addr, __entry->ret,
+ show_event_type(__entry->event), __entry->len, __entry->buf
+ ));
+
+#endif /* _TRACE_I2C_SLAVE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>