mirror of
https://github.com/torvalds/linux.git
synced 2026-07-30 19:21:28 +02:00
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
...
This commit is contained in:
commit
5627ecb837
|
|
@ -12,8 +12,10 @@ Required properties:
|
||||||
"mediatek,mt7622-i2c": for MediaTek MT7622
|
"mediatek,mt7622-i2c": for MediaTek MT7622
|
||||||
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
|
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
|
||||||
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
|
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
|
||||||
|
"mediatek,mt8168-i2c": for MediaTek MT8168
|
||||||
"mediatek,mt8173-i2c": for MediaTek MT8173
|
"mediatek,mt8173-i2c": for MediaTek MT8173
|
||||||
"mediatek,mt8183-i2c": for MediaTek MT8183
|
"mediatek,mt8183-i2c": for MediaTek MT8183
|
||||||
|
"mediatek,mt8186-i2c": for MediaTek MT8186
|
||||||
"mediatek,mt8192-i2c": for MediaTek MT8192
|
"mediatek,mt8192-i2c": for MediaTek MT8192
|
||||||
"mediatek,mt8195-i2c", "mediatek,mt8192-i2c": for MediaTek MT8195
|
"mediatek,mt8195-i2c", "mediatek,mt8192-i2c": for MediaTek MT8195
|
||||||
"mediatek,mt8516-i2c", "mediatek,mt2712-i2c": for MediaTek MT8516
|
"mediatek,mt8516-i2c", "mediatek,mt2712-i2c": for MediaTek MT8516
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ PROPERTIES:
|
||||||
"qcom,msm8996-cci"
|
"qcom,msm8996-cci"
|
||||||
"qcom,sdm845-cci"
|
"qcom,sdm845-cci"
|
||||||
"qcom,sm8250-cci"
|
"qcom,sm8250-cci"
|
||||||
|
"qcom,sm8450-cci"
|
||||||
|
|
||||||
- reg
|
- reg
|
||||||
Usage: required
|
Usage: required
|
||||||
|
|
@ -43,7 +44,8 @@ PROPERTIES:
|
||||||
SUBNODES:
|
SUBNODES:
|
||||||
|
|
||||||
The CCI provides I2C masters for one (msm8916) or two i2c busses (msm8996,
|
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:
|
PROPERTIES:
|
||||||
|
|
||||||
|
|
|
||||||
56
Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml
Normal file
56
Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml
Normal file
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
...
|
||||||
|
|
@ -49,6 +49,11 @@ properties:
|
||||||
- renesas,i2c-r8a779a0 # R-Car V3U
|
- renesas,i2c-r8a779a0 # R-Car V3U
|
||||||
- const: renesas,rcar-gen3-i2c # R-Car Gen3 and RZ/G2
|
- 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:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
|
@ -132,6 +137,7 @@ allOf:
|
||||||
enum:
|
enum:
|
||||||
- renesas,rcar-gen2-i2c
|
- renesas,rcar-gen2-i2c
|
||||||
- renesas,rcar-gen3-i2c
|
- renesas,rcar-gen3-i2c
|
||||||
|
- renesas,rcar-gen4-i2c
|
||||||
then:
|
then:
|
||||||
required:
|
required:
|
||||||
- resets
|
- resets
|
||||||
|
|
|
||||||
|
|
@ -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
|
acpi_dma_request_slave_chan_by_index() directly and therefore choose the
|
||||||
specific FixedDMA resource by its index.
|
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
|
SPI serial bus support
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ Supported adapters:
|
||||||
* Intel Jasper Lake (SOC)
|
* Intel Jasper Lake (SOC)
|
||||||
* Intel Emmitsburg (PCH)
|
* Intel Emmitsburg (PCH)
|
||||||
* Intel Alder Lake (PCH)
|
* Intel Alder Lake (PCH)
|
||||||
|
* Intel Raptor Lake (PCH)
|
||||||
|
|
||||||
Datasheets: Publicly available at the Intel website
|
Datasheets: Publicly available at the Intel website
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18877,6 +18877,7 @@ SYNOPSYS DESIGNWARE I2C DRIVER
|
||||||
M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
|
M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
|
||||||
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||||
R: Mika Westerberg <mika.westerberg@linux.intel.com>
|
R: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||||
|
R: Jan Dabros <jsd@semihalf.com>
|
||||||
L: linux-i2c@vger.kernel.org
|
L: linux-i2c@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/i2c/busses/i2c-designware-*
|
F: drivers/i2c/busses/i2c-designware-*
|
||||||
|
|
@ -21520,7 +21521,6 @@ M: George Cherian <gcherian@marvell.com>
|
||||||
L: linux-i2c@vger.kernel.org
|
L: linux-i2c@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
W: http://www.marvell.com
|
W: http://www.marvell.com
|
||||||
F: Documentation/devicetree/bindings/i2c/i2c-xlp9xx.txt
|
|
||||||
F: drivers/i2c/busses/i2c-xlp9xx.c
|
F: drivers/i2c/busses/i2c-xlp9xx.c
|
||||||
|
|
||||||
XRA1403 GPIO EXPANDER
|
XRA1403 GPIO EXPANDER
|
||||||
|
|
|
||||||
|
|
@ -232,12 +232,13 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
|
||||||
/* Generic apd devices */
|
/* Generic apd devices */
|
||||||
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
|
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
|
||||||
{ "AMD0010", APD_ADDR(cz_i2c_desc) },
|
{ "AMD0010", APD_ADDR(cz_i2c_desc) },
|
||||||
{ "AMDI0010", APD_ADDR(wt_i2c_desc) },
|
|
||||||
{ "AMD0020", APD_ADDR(cz_uart_desc) },
|
{ "AMD0020", APD_ADDR(cz_uart_desc) },
|
||||||
{ "AMDI0020", APD_ADDR(cz_uart_desc) },
|
|
||||||
{ "AMDI0022", APD_ADDR(cz_uart_desc) },
|
|
||||||
{ "AMD0030", },
|
{ "AMD0030", },
|
||||||
{ "AMD0040", APD_ADDR(fch_misc_desc)},
|
{ "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) },
|
{ "HYGO0010", APD_ADDR(wt_i2c_desc) },
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_ARM64
|
#ifdef CONFIG_ARM64
|
||||||
|
|
|
||||||
|
|
@ -935,6 +935,35 @@ void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(fwnode_iomap);
|
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_graph_get_next_endpoint - Get next endpoint firmware node
|
||||||
* @fwnode: Pointer to the parent firmware node
|
* @fwnode: Pointer to the parent firmware node
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,13 @@ menu "I2C Hardware Bus support"
|
||||||
comment "PC SMBus host controller drivers"
|
comment "PC SMBus host controller drivers"
|
||||||
depends on PCI
|
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
|
config I2C_ALI1535
|
||||||
tristate "ALI 1535"
|
tristate "ALI 1535"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
|
@ -148,6 +155,7 @@ config I2C_I801
|
||||||
Jasper Lake (SOC)
|
Jasper Lake (SOC)
|
||||||
Emmitsburg (PCH)
|
Emmitsburg (PCH)
|
||||||
Alder Lake (PCH)
|
Alder Lake (PCH)
|
||||||
|
Raptor Lake (PCH)
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-i801.
|
will be called i2c-i801.
|
||||||
|
|
@ -245,6 +253,7 @@ config I2C_NFORCE2_S4985
|
||||||
config I2C_NVIDIA_GPU
|
config I2C_NVIDIA_GPU
|
||||||
tristate "NVIDIA GPU I2C controller"
|
tristate "NVIDIA GPU I2C controller"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
select I2C_CCGX_UCSI
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the
|
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
|
NVIDIA GPU I2C controller which is used to communicate with the GPU's
|
||||||
|
|
@ -477,8 +486,8 @@ config I2C_BCM_KONA
|
||||||
|
|
||||||
config I2C_BRCMSTB
|
config I2C_BRCMSTB
|
||||||
tristate "BRCM Settop/DSL I2C controller"
|
tristate "BRCM Settop/DSL I2C controller"
|
||||||
depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC || \
|
depends on ARCH_BCM2835 || ARCH_BCM4908 || ARCH_BCM_63XX || \
|
||||||
ARCH_BCM_63XX || COMPILE_TEST
|
ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the
|
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
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-designware-platform.
|
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
|
config I2C_DESIGNWARE_BAYTRAIL
|
||||||
bool "Intel Baytrail I2C semaphore support"
|
bool "Intel Baytrail I2C semaphore support"
|
||||||
depends on ACPI
|
depends on ACPI
|
||||||
|
|
@ -570,6 +590,7 @@ config I2C_DESIGNWARE_PCI
|
||||||
tristate "Synopsys DesignWare PCI"
|
tristate "Synopsys DesignWare PCI"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
select I2C_DESIGNWARE_CORE
|
select I2C_DESIGNWARE_CORE
|
||||||
|
select I2C_CCGX_UCSI
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the
|
If you say yes to this option, support will be included for the
|
||||||
Synopsys DesignWare I2C adapter. Only master mode is supported.
|
Synopsys DesignWare I2C adapter. Only master mode is supported.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
# ACPI drivers
|
# ACPI drivers
|
||||||
obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
|
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
|
# PC SMBus host controller drivers
|
||||||
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
|
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
|
||||||
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.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
|
i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o
|
||||||
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
|
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
|
||||||
i2c-designware-platform-y := i2c-designware-platdrv.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
|
i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
|
||||||
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
|
||||||
i2c-designware-pci-y := i2c-designware-pcidrv.o
|
i2c-designware-pci-y := i2c-designware-pcidrv.o
|
||||||
|
|
|
||||||
|
|
@ -308,11 +308,8 @@ static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
|
||||||
pci_set_master(pci_dev);
|
pci_set_master(pci_dev);
|
||||||
|
|
||||||
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
|
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64));
|
||||||
if (rc) {
|
if (rc)
|
||||||
rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
|
goto err_dma_mask;
|
||||||
if (rc)
|
|
||||||
goto err_dma_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up intx irq */
|
/* Set up intx irq */
|
||||||
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
|
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
|
||||||
|
|
|
||||||
|
|
@ -454,18 +454,20 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||||
ret = clk_prepare_enable(i2c_dev->bus_clk);
|
ret = clk_prepare_enable(i2c_dev->bus_clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Couldn't prepare clock");
|
dev_err(&pdev->dev, "Couldn't prepare clock");
|
||||||
return ret;
|
goto err_put_exclusive_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_dev->irq = platform_get_irq(pdev, 0);
|
i2c_dev->irq = platform_get_irq(pdev, 0);
|
||||||
if (i2c_dev->irq < 0)
|
if (i2c_dev->irq < 0) {
|
||||||
return i2c_dev->irq;
|
ret = i2c_dev->irq;
|
||||||
|
goto err_disable_unprepare_clk;
|
||||||
|
}
|
||||||
|
|
||||||
ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
|
ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
|
||||||
dev_name(&pdev->dev), i2c_dev);
|
dev_name(&pdev->dev), i2c_dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Could not request IRQ\n");
|
dev_err(&pdev->dev, "Could not request IRQ\n");
|
||||||
return -ENODEV;
|
goto err_disable_unprepare_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
adap = &i2c_dev->adapter;
|
adap = &i2c_dev->adapter;
|
||||||
|
|
@ -489,7 +491,16 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = i2c_add_adapter(adap);
|
ret = i2c_add_adapter(adap);
|
||||||
if (ret)
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
drivers/i2c/busses/i2c-ccgx-ucsi.c
Normal file
30
drivers/i2c/busses/i2c-ccgx-ucsi.c
Normal file
|
|
@ -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");
|
||||||
11
drivers/i2c/busses/i2c-ccgx-ucsi.h
Normal file
11
drivers/i2c/busses/i2c-ccgx-ucsi.h
Normal file
|
|
@ -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_ */
|
||||||
|
|
@ -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
|
* interrupt handler as well, so running the client irq handler from
|
||||||
* this thread will cause things to lock up.
|
* this thread will cause things to lock up.
|
||||||
*/
|
*/
|
||||||
if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) {
|
if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ)
|
||||||
/*
|
generic_handle_irq_safe(adap->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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
* 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.
|
* @num: The number of i2c messages expected.
|
||||||
*
|
*
|
||||||
* Returns the number of response bytes expeced.
|
* 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.
|
* We'll take the EC's response and copy it back into msgs.
|
||||||
*
|
*
|
||||||
* @buf: The buffer to parse.
|
* @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
|
* @num: The number of i2c messages; will be modified to include the actual
|
||||||
* number received.
|
* number received.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
388
drivers/i2c/busses/i2c-designware-amdpsp.c
Normal file
388
drivers/i2c/busses/i2c-designware-amdpsp.c
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -12,25 +12,25 @@
|
||||||
|
|
||||||
#include "i2c-designware-core.h"
|
#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;
|
acpi_status status;
|
||||||
unsigned long long shared_host = 0;
|
unsigned long long shared_host = 0;
|
||||||
acpi_handle handle;
|
acpi_handle handle;
|
||||||
|
|
||||||
if (!dev || !dev->dev)
|
if (!dev)
|
||||||
return 0;
|
return -ENODEV;
|
||||||
|
|
||||||
handle = ACPI_HANDLE(dev->dev);
|
handle = ACPI_HANDLE(dev->dev);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
return 0;
|
return -ENODEV;
|
||||||
|
|
||||||
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
|
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return 0;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!shared_host)
|
if (!shared_host)
|
||||||
return 0;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!iosf_mbi_available())
|
if (!iosf_mbi_available())
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
* Try to detect the FIFO depth if not set by interface driver,
|
||||||
* the depth could be from 2 to 256 from HW spec.
|
* 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, ¶m);
|
ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, ¶m);
|
||||||
|
i2c_dw_release_lock(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
@ -607,6 +612,11 @@ u32 i2c_dw_func(struct i2c_adapter *adap)
|
||||||
void i2c_dw_disable(struct dw_i2c_dev *dev)
|
void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 dummy;
|
u32 dummy;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
|
if (ret)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Disable controller */
|
/* Disable controller */
|
||||||
__i2c_dw_disable(dev);
|
__i2c_dw_disable(dev);
|
||||||
|
|
@ -614,6 +624,8 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||||
/* Disable all interrupts */
|
/* Disable all interrupts */
|
||||||
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
|
regmap_write(dev->map, DW_IC_INTR_MASK, 0);
|
||||||
regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
|
regmap_read(dev->map, DW_IC_CLR_INTR, &dummy);
|
||||||
|
|
||||||
|
i2c_dw_release_lock(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
|
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,8 @@ struct reset_control;
|
||||||
* @hs_lcnt: high speed LCNT value
|
* @hs_lcnt: high speed LCNT value
|
||||||
* @acquire_lock: function to acquire a hardware lock on the bus
|
* @acquire_lock: function to acquire a hardware lock on the bus
|
||||||
* @release_lock: function to release 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
|
* @shared_with_punit: true if this bus is shared with the SoCs PUNIT
|
||||||
* @disable: function to disable the controller
|
* @disable: function to disable the controller
|
||||||
* @disable_int: function to disable all interrupts
|
* @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
|
* @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
|
||||||
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
|
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
|
||||||
* @rinfo: I²C GPIO recovery information
|
* @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
|
* 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.
|
* values than the one computed based only on the input clock frequency.
|
||||||
|
|
@ -285,6 +286,7 @@ struct dw_i2c_dev {
|
||||||
u16 hs_lcnt;
|
u16 hs_lcnt;
|
||||||
int (*acquire_lock)(void);
|
int (*acquire_lock)(void);
|
||||||
void (*release_lock)(void);
|
void (*release_lock)(void);
|
||||||
|
int semaphore_idx;
|
||||||
bool shared_with_punit;
|
bool shared_with_punit;
|
||||||
void (*disable)(struct dw_i2c_dev *dev);
|
void (*disable)(struct dw_i2c_dev *dev);
|
||||||
void (*disable_int)(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 (*set_sda_hold_time)(struct dw_i2c_dev *dev);
|
||||||
int mode;
|
int mode;
|
||||||
struct i2c_bus_recovery_info rinfo;
|
struct i2c_bus_recovery_info rinfo;
|
||||||
bool suspended;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ACCESS_INTR_MASK BIT(0)
|
#define ACCESS_INTR_MASK BIT(0)
|
||||||
#define ACCESS_NO_IRQ_SUSPEND BIT(1)
|
#define ACCESS_NO_IRQ_SUSPEND BIT(1)
|
||||||
|
#define ARBITRATION_SEMAPHORE BIT(2)
|
||||||
|
|
||||||
#define MODEL_MSCC_OCELOT BIT(8)
|
#define MODEL_MSCC_OCELOT BIT(8)
|
||||||
#define MODEL_BAIKAL_BT1 BIT(9)
|
#define MODEL_BAIKAL_BT1 BIT(9)
|
||||||
|
|
@ -310,6 +312,11 @@ struct dw_i2c_dev {
|
||||||
#define AMD_UCSI_INTR_REG 0x474
|
#define AMD_UCSI_INTR_REG 0x474
|
||||||
#define AMD_UCSI_INTR_EN 0xd
|
#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);
|
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_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
|
u32 i2c_dw_scl_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)
|
#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
|
||||||
extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev);
|
int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);
|
||||||
#else
|
#endif
|
||||||
static inline int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { return 0; }
|
|
||||||
|
#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
|
#endif
|
||||||
|
|
||||||
int i2c_dw_validate_speed(struct dw_i2c_dev *dev);
|
int i2c_dw_validate_speed(struct dw_i2c_dev *dev);
|
||||||
|
|
|
||||||
|
|
@ -567,11 +567,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
goto done_nolock;
|
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);
|
reinit_completion(&dev->cmd_complete);
|
||||||
dev->msgs = msgs;
|
dev->msgs = msgs;
|
||||||
dev->msgs_num = num;
|
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;
|
irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
i2c_dw_disable_int(dev);
|
i2c_dw_disable_int(dev);
|
||||||
|
i2c_dw_release_lock(dev);
|
||||||
|
|
||||||
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
|
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags,
|
||||||
dev_name(dev->dev), dev);
|
dev_name(dev->dev), dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include "i2c-designware-core.h"
|
#include "i2c-designware-core.h"
|
||||||
|
#include "i2c-ccgx-ucsi.h"
|
||||||
|
|
||||||
#define DRIVER_NAME "i2c-designware-pci"
|
#define DRIVER_NAME "i2c-designware-pci"
|
||||||
#define AMD_CLK_RATE_HZ 100000
|
#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;
|
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)
|
static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||||
{
|
{
|
||||||
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
|
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
@ -213,14 +194,28 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int __maybe_unused i2c_dw_pci_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
i_dev->disable(i_dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int __maybe_unused i2c_dw_pci_suspend(struct device *dev)
|
static int __maybe_unused i2c_dw_pci_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
i_dev->suspended = true;
|
i2c_mark_adapter_suspended(&i_dev->adapter);
|
||||||
i_dev->disable(i_dev);
|
|
||||||
|
|
||||||
return 0;
|
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)
|
static int __maybe_unused i2c_dw_pci_resume(struct device *dev)
|
||||||
|
|
@ -228,14 +223,17 @@ static int __maybe_unused i2c_dw_pci_resume(struct device *dev)
|
||||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = i_dev->init(i_dev);
|
ret = i2c_dw_pci_runtime_resume(dev);
|
||||||
i_dev->suspended = false;
|
|
||||||
|
i2c_mark_adapter_resumed(&i_dev->adapter);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
|
static const struct dev_pm_ops i2c_dw_pm_ops = {
|
||||||
i2c_dw_pci_resume, NULL);
|
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,
|
static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||||
const struct pci_device_id *id)
|
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) {
|
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
|
||||||
r = navi_amd_register_client(dev);
|
dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, NULL);
|
||||||
if (r) {
|
if (IS_ERR(dev->slave))
|
||||||
dev_err(dev->dev, "register client failed with %d\n", r);
|
return dev_err_probe(dev->dev, PTR_ERR(dev->slave),
|
||||||
return r;
|
"register UCSI failed\n");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
||||||
{ "808622C1", ACCESS_NO_IRQ_SUSPEND },
|
{ "808622C1", ACCESS_NO_IRQ_SUSPEND },
|
||||||
{ "AMD0010", ACCESS_INTR_MASK },
|
{ "AMD0010", ACCESS_INTR_MASK },
|
||||||
{ "AMDI0010", ACCESS_INTR_MASK },
|
{ "AMDI0010", ACCESS_INTR_MASK },
|
||||||
|
{ "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE },
|
||||||
{ "AMDI0510", 0 },
|
{ "AMDI0510", 0 },
|
||||||
{ "APMC0D0F", 0 },
|
{ "APMC0D0F", 0 },
|
||||||
{ "HISI02A1", 0 },
|
{ "HISI02A1", 0 },
|
||||||
|
|
@ -204,6 +205,63 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
|
||||||
{ } /* terminate list */
|
{ } /* 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)
|
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
|
|
@ -334,6 +392,8 @@ static int dw_i2c_plat_remove(struct platform_device *pdev)
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
dw_i2c_plat_pm_cleanup(dev);
|
dw_i2c_plat_pm_cleanup(dev);
|
||||||
|
|
||||||
|
i2c_dw_remove_lock_support(dev);
|
||||||
|
|
||||||
reset_control_assert(dev->rst);
|
reset_control_assert(dev->rst);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -368,12 +428,10 @@ static void dw_i2c_plat_complete(struct device *dev)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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);
|
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
i_dev->suspended = true;
|
|
||||||
|
|
||||||
if (i_dev->shared_with_punit)
|
if (i_dev->shared_with_punit)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -383,7 +441,16 @@ static int dw_i2c_plat_suspend(struct device *dev)
|
||||||
return 0;
|
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);
|
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);
|
i2c_dw_prepare_clk(i_dev, true);
|
||||||
|
|
||||||
i_dev->init(i_dev);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -400,7 +476,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
|
||||||
.prepare = dw_i2c_plat_prepare,
|
.prepare = dw_i2c_plat_prepare,
|
||||||
.complete = dw_i2c_plat_complete,
|
.complete = dw_i2c_plat_complete,
|
||||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
|
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)
|
#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@
|
||||||
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
|
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
|
||||||
* Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
|
* Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
|
||||||
* Alder Lake-M (PCH) 0x54a3 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:
|
* Features supported by this driver:
|
||||||
* Software PEC no
|
* Software PEC no
|
||||||
|
|
@ -165,7 +166,7 @@
|
||||||
#define I801_BYTE 0x04
|
#define I801_BYTE 0x04
|
||||||
#define I801_BYTE_DATA 0x08
|
#define I801_BYTE_DATA 0x08
|
||||||
#define I801_WORD_DATA 0x0C
|
#define I801_WORD_DATA 0x0C
|
||||||
#define I801_PROC_CALL 0x10 /* unimplemented */
|
#define I801_PROC_CALL 0x10
|
||||||
#define I801_BLOCK_DATA 0x14
|
#define I801_BLOCK_DATA 0x14
|
||||||
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
|
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
|
||||||
#define I801_BLOCK_PROC_CALL 0x1C
|
#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_P_SMBUS 0x51a3
|
||||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3
|
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3
|
||||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
#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_ALDER_LAKE_S_SMBUS 0x7aa3
|
||||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
||||||
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
|
#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
|
* 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)) {
|
if (unlikely(status < 0)) {
|
||||||
dev_err(&priv->pci_dev->dev, "Transaction timeout\n");
|
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);
|
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));
|
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
|
||||||
|
|
||||||
status = i801_wait_intr(priv);
|
status = i801_wait_intr(priv);
|
||||||
|
|
@ -790,7 +787,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
||||||
{
|
{
|
||||||
int hwpec;
|
int hwpec;
|
||||||
int block = 0;
|
int block = 0;
|
||||||
int ret = 0, xact = 0;
|
int ret, xact;
|
||||||
struct i801_priv *priv = i2c_get_adapdata(adap);
|
struct i801_priv *priv = i2c_get_adapdata(adap);
|
||||||
|
|
||||||
mutex_lock(&priv->acpi_lock);
|
mutex_lock(&priv->acpi_lock);
|
||||||
|
|
@ -836,6 +833,14 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
||||||
}
|
}
|
||||||
xact = I801_WORD_DATA;
|
xact = I801_WORD_DATA;
|
||||||
break;
|
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:
|
case I2C_SMBUS_BLOCK_DATA:
|
||||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||||
SMBHSTADD(priv));
|
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))
|
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
switch (xact & 0x7f) {
|
switch (xact) {
|
||||||
case I801_BYTE: /* Result put in SMBHSTDAT0 */
|
case I801_BYTE: /* Result put in SMBHSTDAT0 */
|
||||||
case I801_BYTE_DATA:
|
case I801_BYTE_DATA:
|
||||||
data->byte = inb_p(SMBHSTDAT0(priv));
|
data->byte = inb_p(SMBHSTDAT0(priv));
|
||||||
break;
|
break;
|
||||||
case I801_WORD_DATA:
|
case I801_WORD_DATA:
|
||||||
|
case I801_PROC_CALL:
|
||||||
data->word = inb_p(SMBHSTDAT0(priv)) +
|
data->word = inb_p(SMBHSTDAT0(priv)) +
|
||||||
(inb_p(SMBHSTDAT1(priv)) << 8);
|
(inb_p(SMBHSTDAT1(priv)) << 8);
|
||||||
break;
|
break;
|
||||||
|
|
@ -933,6 +939,7 @@ static u32 i801_func(struct i2c_adapter *adapter)
|
||||||
|
|
||||||
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
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 |
|
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
|
||||||
((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
|
((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
|
||||||
((priv->features & FEATURE_BLOCK_PROC) ?
|
((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_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_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||||
{ PCI_DEVICE_DATA(INTEL, ALDER_LAKE_M_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, }
|
{ 0, }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -465,18 +465,18 @@ static int meson_i2c_probe(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0);
|
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 */
|
/* Disable filtering */
|
||||||
meson_i2c_set_mask(i2c, REG_SLAVE_ADDR,
|
meson_i2c_set_mask(i2c, REG_SLAVE_ADDR,
|
||||||
REG_SLV_SDA_FILTER | REG_SLV_SCL_FILTER, 0);
|
REG_SLV_SDA_FILTER | REG_SLV_SCL_FILTER, 0);
|
||||||
|
|
||||||
meson_i2c_set_clk_div(i2c, timings.bus_freq_hz);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,12 @@
|
||||||
|
|
||||||
#define MAX_SAMPLE_CNT_DIV 8
|
#define MAX_SAMPLE_CNT_DIV 8
|
||||||
#define MAX_STEP_CNT_DIV 64
|
#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 MAX_HS_STEP_CNT_DIV 8
|
||||||
#define I2C_STANDARD_MODE_BUFFER (1000 / 2)
|
#define I2C_STANDARD_MODE_BUFFER (1000 / 3)
|
||||||
#define I2C_FAST_MODE_BUFFER (300 / 2)
|
#define I2C_FAST_MODE_BUFFER (300 / 3)
|
||||||
#define I2C_FAST_MODE_PLUS_BUFFER (20 / 2)
|
#define I2C_FAST_MODE_PLUS_BUFFER (20 / 3)
|
||||||
|
|
||||||
#define I2C_CONTROL_RS (0x1 << 1)
|
#define I2C_CONTROL_RS (0x1 << 1)
|
||||||
#define I2C_CONTROL_DMA_EN (0x1 << 2)
|
#define I2C_CONTROL_DMA_EN (0x1 << 2)
|
||||||
|
|
@ -85,6 +86,27 @@
|
||||||
|
|
||||||
#define I2C_DRV_NAME "i2c-mt65xx"
|
#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 {
|
enum DMA_REGS_OFFSET {
|
||||||
OFFSET_INT_FLAG = 0x0,
|
OFFSET_INT_FLAG = 0x0,
|
||||||
OFFSET_INT_EN = 0x04,
|
OFFSET_INT_EN = 0x04,
|
||||||
|
|
@ -243,10 +265,7 @@ struct mtk_i2c {
|
||||||
/* set in i2c probe */
|
/* set in i2c probe */
|
||||||
void __iomem *base; /* i2c base addr */
|
void __iomem *base; /* i2c base addr */
|
||||||
void __iomem *pdmabase; /* dma base address*/
|
void __iomem *pdmabase; /* dma base address*/
|
||||||
struct clk *clk_main; /* main clock for i2c bus */
|
struct clk_bulk_data clocks[I2C_MT65XX_CLK_MAX]; /* clocks for i2c */
|
||||||
struct clk *clk_dma; /* DMA clock for i2c via DMA */
|
|
||||||
struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
|
|
||||||
struct clk *clk_arb; /* Arbitrator clock for i2c */
|
|
||||||
bool have_pmic; /* can use i2c pins from PMIC */
|
bool have_pmic; /* can use i2c pins from PMIC */
|
||||||
bool use_push_pull; /* IO config push-pull mode */
|
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,
|
.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 = {
|
static const struct mtk_i2c_compatible mt8173_compat = {
|
||||||
.regs = mt_i2c_regs_v1,
|
.regs = mt_i2c_regs_v1,
|
||||||
.pmic_i2c = 0,
|
.pmic_i2c = 0,
|
||||||
|
|
@ -397,6 +429,19 @@ static const struct mtk_i2c_compatible mt8183_compat = {
|
||||||
.max_dma_support = 33,
|
.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 = {
|
static const struct mtk_i2c_compatible mt8192_compat = {
|
||||||
.quirks = &mt8183_i2c_quirks,
|
.quirks = &mt8183_i2c_quirks,
|
||||||
.regs = mt_i2c_regs_v2,
|
.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,mt6577-i2c", .data = &mt6577_compat },
|
||||||
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
|
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
|
||||||
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
|
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
|
||||||
|
{ .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat },
|
||||||
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
|
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
|
||||||
{ .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
|
{ .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
|
||||||
|
{ .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat },
|
||||||
{ .compatible = "mediatek,mt8192-i2c", .data = &mt8192_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]);
|
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)
|
static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
|
||||||
{
|
{
|
||||||
u16 control_reg;
|
u16 control_reg;
|
||||||
|
|
@ -590,6 +588,31 @@ static int mtk_i2c_max_step_cnt(unsigned int target_speed)
|
||||||
return MAX_STEP_CNT_DIV;
|
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
|
* 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 best_mul;
|
||||||
unsigned int cnt_mul;
|
unsigned int cnt_mul;
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
int clk_div_restri = 0;
|
||||||
|
|
||||||
if (target_speed > I2C_MAX_HIGH_SPEED_MODE_FREQ)
|
if (target_speed > I2C_MAX_HIGH_SPEED_MODE_FREQ)
|
||||||
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
|
* optimizing for sample_cnt * step_cnt being minimal
|
||||||
*/
|
*/
|
||||||
for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) {
|
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;
|
cnt_mul = step_cnt * sample_cnt;
|
||||||
if (step_cnt > max_step_cnt)
|
if (step_cnt > max_step_cnt)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -749,7 +774,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
|
||||||
best_mul = cnt_mul;
|
best_mul = cnt_mul;
|
||||||
base_sample_cnt = sample_cnt;
|
base_sample_cnt = sample_cnt;
|
||||||
base_step_cnt = step_cnt;
|
base_step_cnt = step_cnt;
|
||||||
if (best_mul == opt_div)
|
if (best_mul == (opt_div + clk_div_restri))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -760,7 +785,8 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src,
|
||||||
sample_cnt = base_sample_cnt;
|
sample_cnt = base_sample_cnt;
|
||||||
step_cnt = base_step_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
|
/* In this case, hardware can't support such
|
||||||
* low i2c_bus_freq
|
* 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;
|
target_speed = i2c->speed_hz;
|
||||||
parent_clk /= i2c->clk_src_div;
|
parent_clk /= i2c->clk_src_div;
|
||||||
|
|
||||||
if (i2c->dev_comp->timing_adjust)
|
if (i2c->dev_comp->timing_adjust && i2c->dev_comp->ltiming_adjust)
|
||||||
max_clk_div = MAX_CLOCK_DIV;
|
max_clk_div = MAX_CLOCK_DIV_5BITS;
|
||||||
|
else if (i2c->dev_comp->timing_adjust)
|
||||||
|
max_clk_div = MAX_CLOCK_DIV_8BITS;
|
||||||
else
|
else
|
||||||
max_clk_div = 1;
|
max_clk_div = 1;
|
||||||
|
|
||||||
for (clk_div = 1; clk_div <= max_clk_div; clk_div++) {
|
for (clk_div = 1; clk_div <= max_clk_div; clk_div++) {
|
||||||
clk_src = parent_clk / 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) {
|
if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) {
|
||||||
/* Set master code speed register */
|
/* Set master code speed register */
|
||||||
|
|
@ -842,7 +871,6 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c->ac_timing.inter_clk_div = clk_div - 1;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1149,7 +1177,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
|
||||||
int left_num = num;
|
int left_num = num;
|
||||||
struct mtk_i2c *i2c = i2c_get_adapdata(adap);
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
@ -1203,7 +1231,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
|
||||||
ret = num;
|
ret = num;
|
||||||
|
|
||||||
err_exit:
|
err_exit:
|
||||||
mtk_i2c_clock_disable(i2c);
|
clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1281,9 +1309,8 @@ static int mtk_i2c_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct mtk_i2c *i2c;
|
struct mtk_i2c *i2c;
|
||||||
struct clk *clk;
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int irq;
|
int i, irq, speed_clk;
|
||||||
|
|
||||||
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||||
if (!i2c)
|
if (!i2c)
|
||||||
|
|
@ -1329,35 +1356,42 @@ static int mtk_i2c_probe(struct platform_device *pdev)
|
||||||
if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
|
if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
i2c->clk_main = devm_clk_get(&pdev->dev, "main");
|
/* Fill in clk-bulk IDs */
|
||||||
if (IS_ERR(i2c->clk_main)) {
|
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");
|
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");
|
i2c->clocks[I2C_MT65XX_CLK_DMA].clk = devm_clk_get(&pdev->dev, "dma");
|
||||||
if (IS_ERR(i2c->clk_dma)) {
|
if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk)) {
|
||||||
dev_err(&pdev->dev, "cannot get dma clock\n");
|
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");
|
i2c->clocks[I2C_MT65XX_CLK_ARB].clk = devm_clk_get_optional(&pdev->dev, "arb");
|
||||||
if (IS_ERR(i2c->clk_arb))
|
if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk))
|
||||||
i2c->clk_arb = NULL;
|
return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk);
|
||||||
|
|
||||||
clk = i2c->clk_main;
|
|
||||||
if (i2c->have_pmic) {
|
if (i2c->have_pmic) {
|
||||||
i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
|
i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = devm_clk_get(&pdev->dev, "pmic");
|
||||||
if (IS_ERR(i2c->clk_pmic)) {
|
if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk)) {
|
||||||
dev_err(&pdev->dev, "cannot get pmic clock\n");
|
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));
|
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) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to set the speed.\n");
|
dev_err(&pdev->dev, "Failed to set the speed.\n");
|
||||||
return -EINVAL;
|
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) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "clock enable failed!\n");
|
dev_err(&pdev->dev, "clock enable failed!\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
mtk_i2c_init_hw(i2c);
|
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,
|
ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
|
||||||
IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE,
|
IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE,
|
||||||
|
|
@ -1423,7 +1457,7 @@ static int mtk_i2c_resume_noirq(struct device *dev)
|
||||||
int ret;
|
int ret;
|
||||||
struct mtk_i2c *i2c = dev_get_drvdata(dev);
|
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) {
|
if (ret) {
|
||||||
dev_err(dev, "clock enable failed!\n");
|
dev_err(dev, "clock enable failed!\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -1431,7 +1465,7 @@ static int mtk_i2c_resume_noirq(struct device *dev)
|
||||||
|
|
||||||
mtk_i2c_init_hw(i2c);
|
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);
|
i2c_mark_adapter_resumed(&i2c->adap);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
* 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
|
* 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) {
|
if (bus->rd_ind == 0 && bus->read_block_use) {
|
||||||
/* set fifo to read one byte, no last: */
|
/* 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:
|
* npcm_i2c_slave_wr_buf_sync:
|
||||||
* currently slave IF only supports single byte operations.
|
* 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
|
* at a time, pack them in buffer, and then transmit them all together
|
||||||
* to the FIFO and onward to the bus.
|
* to the FIFO and onward to the bus.
|
||||||
* NACK on read will be once reached to bus->adap->quirks->max_read_len.
|
* 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.
|
* the i2c module can response to 10 own SA.
|
||||||
* check which one was addressed by the master.
|
* check which one was addressed by the master.
|
||||||
* repond to the first one.
|
* respond to the first one.
|
||||||
*/
|
*/
|
||||||
addr = ((i2ccst3 & 0x07) << 7) |
|
addr = ((i2ccst3 & 0x07) << 7) |
|
||||||
(i2ccst2 & 0x7F);
|
(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.
|
* npcm i2c HW allows direct reading of SCL and SDA.
|
||||||
* However, it does not support setting SCL and SDA directly.
|
* However, it does not support setting SCL and SDA directly.
|
||||||
* The recovery function can togle SCL when SDA is low (but not set)
|
* The recovery function can toggle SCL when SDA is low (but not set)
|
||||||
* Getter functions used internally, and can be used externaly.
|
* Getter functions used internally, and can be used externally.
|
||||||
*/
|
*/
|
||||||
rinfo->get_scl = npcm_i2c_get_SCL;
|
rinfo->get_scl = npcm_i2c_get_SCL;
|
||||||
rinfo->get_sda = npcm_i2c_get_SDA;
|
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.
|
* 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.
|
* and bus frequency.
|
||||||
* 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are simetric.
|
* 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are symmetric.
|
||||||
* 400kHz bus requires assymetric HT and LT. A different equation is recomended
|
* 400kHz bus requires asymmetric HT and LT. A different equation is recommended
|
||||||
* by the HW designer, given core clock range (equations in comments below).
|
* by the HW designer, given core clock range (equations in comments below).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
#include "i2c-ccgx-ucsi.h"
|
||||||
|
|
||||||
/* I2C definitions */
|
/* I2C definitions */
|
||||||
#define I2C_MST_CNTL 0x00
|
#define I2C_MST_CNTL 0x00
|
||||||
#define I2C_MST_CNTL_GEN_START BIT(0)
|
#define I2C_MST_CNTL_GEN_START BIT(0)
|
||||||
|
|
@ -266,54 +268,32 @@ static const struct software_node ccgx_node = {
|
||||||
.properties = ccgx_props,
|
.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)
|
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;
|
struct gpu_i2c_dev *i2cd;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL);
|
i2cd = devm_kzalloc(dev, sizeof(*i2cd), GFP_KERNEL);
|
||||||
if (!i2cd)
|
if (!i2cd)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
i2cd->dev = &pdev->dev;
|
i2cd->dev = dev;
|
||||||
dev_set_drvdata(&pdev->dev, i2cd);
|
dev_set_drvdata(dev, i2cd);
|
||||||
|
|
||||||
status = pcim_enable_device(pdev);
|
status = pcim_enable_device(pdev);
|
||||||
if (status < 0) {
|
if (status < 0)
|
||||||
dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status);
|
return dev_err_probe(dev, status, "pcim_enable_device failed\n");
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
pci_set_master(pdev);
|
pci_set_master(pdev);
|
||||||
|
|
||||||
i2cd->regs = pcim_iomap(pdev, 0, 0);
|
i2cd->regs = pcim_iomap(pdev, 0, 0);
|
||||||
if (!i2cd->regs) {
|
if (!i2cd->regs)
|
||||||
dev_err(&pdev->dev, "pcim_iomap failed\n");
|
return dev_err_probe(dev, -ENOMEM, "pcim_iomap failed\n");
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
|
status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
|
||||||
if (status < 0) {
|
if (status < 0)
|
||||||
dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status);
|
return dev_err_probe(dev, status, "pci_alloc_irq_vectors err\n");
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu_enable_i2c_bus(i2cd);
|
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));
|
sizeof(i2cd->adapter.name));
|
||||||
i2cd->adapter.algo = &gpu_i2c_algorithm;
|
i2cd->adapter.algo = &gpu_i2c_algorithm;
|
||||||
i2cd->adapter.quirks = &gpu_i2c_quirks;
|
i2cd->adapter.quirks = &gpu_i2c_quirks;
|
||||||
i2cd->adapter.dev.parent = &pdev->dev;
|
i2cd->adapter.dev.parent = dev;
|
||||||
status = i2c_add_adapter(&i2cd->adapter);
|
status = i2c_add_adapter(&i2cd->adapter);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto free_irq_vectors;
|
goto free_irq_vectors;
|
||||||
|
|
||||||
status = gpu_populate_client(i2cd, pdev->irq);
|
i2cd->ccgx_client = i2c_new_ccgx_ucsi(&i2cd->adapter, pdev->irq, &ccgx_node);
|
||||||
if (status < 0) {
|
if (IS_ERR(i2cd->ccgx_client)) {
|
||||||
dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status);
|
status = dev_err_probe(dev, PTR_ERR(i2cd->ccgx_client), "register UCSI failed\n");
|
||||||
goto del_adapter;
|
goto del_adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
|
pm_runtime_set_autosuspend_delay(dev, 3000);
|
||||||
pm_runtime_use_autosuspend(&pdev->dev);
|
pm_runtime_use_autosuspend(dev);
|
||||||
pm_runtime_put_autosuspend(&pdev->dev);
|
pm_runtime_put_autosuspend(dev);
|
||||||
pm_runtime_allow(&pdev->dev);
|
pm_runtime_allow(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -350,7 +330,7 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||||
|
|
||||||
static void gpu_i2c_remove(struct pci_dev *pdev)
|
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);
|
pm_runtime_get_noresume(i2cd->dev);
|
||||||
i2c_del_adapter(&i2cd->adapter);
|
i2c_del_adapter(&i2cd->adapter);
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,6 @@ int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)
|
||||||
smbus->adapter.owner = THIS_MODULE;
|
smbus->adapter.owner = THIS_MODULE;
|
||||||
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
|
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
|
||||||
"PA Semi SMBus adapter (%s)", dev_name(smbus->dev));
|
"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 = &smbus_algorithm;
|
||||||
smbus->adapter.algo_data = smbus;
|
smbus->adapter.algo_data = smbus;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ static int pasemi_smb_pci_probe(struct pci_dev *dev,
|
||||||
if (!smbus->ioaddr)
|
if (!smbus->ioaddr)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
|
smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||||
error = pasemi_i2c_common_probe(smbus);
|
error = pasemi_i2c_common_probe(smbus);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@
|
||||||
|
|
||||||
/* SB800 constants */
|
/* SB800 constants */
|
||||||
#define SB800_PIIX4_SMB_IDX 0xcd6
|
#define SB800_PIIX4_SMB_IDX 0xcd6
|
||||||
|
#define SB800_PIIX4_SMB_MAP_SIZE 2
|
||||||
|
|
||||||
#define KERNCZ_IMC_IDX 0x3e
|
#define KERNCZ_IMC_IDX 0x3e
|
||||||
#define KERNCZ_IMC_DATA 0x3f
|
#define KERNCZ_IMC_DATA 0x3f
|
||||||
|
|
@ -97,6 +98,9 @@
|
||||||
#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18
|
#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18
|
||||||
#define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3
|
#define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3
|
||||||
|
|
||||||
|
#define SB800_PIIX4_FCH_PM_ADDR 0xFED80300
|
||||||
|
#define SB800_PIIX4_FCH_PM_SIZE 8
|
||||||
|
|
||||||
/* insmod parameters */
|
/* insmod parameters */
|
||||||
|
|
||||||
/* If force is set to anything different from 0, we forcibly enable the
|
/* 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";
|
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 {
|
struct i2c_piix4_adapdata {
|
||||||
unsigned short smba;
|
unsigned short smba;
|
||||||
|
|
||||||
|
|
@ -162,8 +172,75 @@ struct i2c_piix4_adapdata {
|
||||||
bool sb800_main;
|
bool sb800_main;
|
||||||
bool notify_imc;
|
bool notify_imc;
|
||||||
u8 port; /* Port number, shifted */
|
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,
|
static int piix4_setup(struct pci_dev *PIIX4_dev,
|
||||||
const struct pci_device_id *id)
|
const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
|
|
@ -263,12 +340,61 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
|
||||||
return piix4_smba;
|
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,
|
static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||||
const struct pci_device_id *id, u8 aux)
|
const struct pci_device_id *id, u8 aux)
|
||||||
{
|
{
|
||||||
unsigned short piix4_smba;
|
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;
|
u8 i2ccfg, i2ccfg_offset = 0x10;
|
||||||
|
struct sb800_mmio_cfg mmio_cfg;
|
||||||
|
int retval;
|
||||||
|
|
||||||
/* SB800 and later SMBus does not support forcing address */
|
/* SB800 and later SMBus does not support forcing address */
|
||||||
if (force || force_addr) {
|
if (force || force_addr) {
|
||||||
|
|
@ -290,35 +416,11 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||||
else
|
else
|
||||||
smb_en = (aux) ? 0x28 : 0x2c;
|
smb_en = (aux) ? 0x28 : 0x2c;
|
||||||
|
|
||||||
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) {
|
retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, aux, &smb_en_status,
|
||||||
dev_err(&PIIX4_dev->dev,
|
&piix4_smba);
|
||||||
"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);
|
if (retval)
|
||||||
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
return retval;
|
||||||
outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
|
|
||||||
smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
|
||||||
|
|
||||||
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 (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
|
if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
|
||||||
return -ENODEV;
|
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;
|
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2,
|
mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev);
|
||||||
"sb800_piix4_smb")) {
|
retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg);
|
||||||
|
if (retval) {
|
||||||
release_region(piix4_smba, SMBIOSIZE);
|
release_region(piix4_smba, SMBIOSIZE);
|
||||||
return -EBUSY;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
|
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;
|
SB800_PIIX4_PORT_IDX;
|
||||||
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
|
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
|
||||||
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
|
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,
|
dev_info(&PIIX4_dev->dev,
|
||||||
|
|
@ -662,6 +765,29 @@ static void piix4_imc_wakeup(void)
|
||||||
release_region(KERNCZ_IMC_IDX, 2);
|
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.
|
* Handles access to multiple SMBus ports on the SB800.
|
||||||
* The port is selected by bits 2:1 of the smb_en register (0x2c).
|
* 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;
|
unsigned short piix4_smba = adapdata->smba;
|
||||||
int retries = MAX_TIMEOUT;
|
int retries = MAX_TIMEOUT;
|
||||||
int smbslvcnt;
|
int smbslvcnt;
|
||||||
u8 smba_en_lo;
|
u8 prev_port;
|
||||||
u8 port;
|
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb"))
|
retval = piix4_sb800_region_request(&adap->dev, &adapdata->mmio_cfg);
|
||||||
return -EBUSY;
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
/* Request the SMBUS semaphore, avoid conflicts with the IMC */
|
/* Request the SMBUS semaphore, avoid conflicts with the IMC */
|
||||||
smbslvcnt = inb_p(SMBSLVCNT);
|
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);
|
prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg);
|
||||||
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);
|
|
||||||
|
|
||||||
retval = piix4_access(adap, addr, flags, read_write,
|
retval = piix4_access(adap, addr, flags, read_write,
|
||||||
command, size, data);
|
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 */
|
/* Release the semaphore */
|
||||||
outb_p(smbslvcnt | 0x20, SMBSLVCNT);
|
outb_p(smbslvcnt | 0x20, SMBSLVCNT);
|
||||||
|
|
@ -758,7 +878,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
|
||||||
piix4_imc_wakeup();
|
piix4_imc_wakeup();
|
||||||
|
|
||||||
release:
|
release:
|
||||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -836,6 +956,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adapdata->mmio_cfg.use_mmio = piix4_sb800_use_mmio(dev);
|
||||||
adapdata->smba = smba;
|
adapdata->smba = smba;
|
||||||
adapdata->sb800_main = sb800_main;
|
adapdata->sb800_main = sb800_main;
|
||||||
adapdata->port = port << piix4_port_shift_sb800;
|
adapdata->port = port << piix4_port_shift_sb800;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
// Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
|
// 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/clk.h>
|
||||||
#include <linux/completion.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,msm8996-cci", .data = &cci_v2_data},
|
||||||
{ .compatible = "qcom,sdm845-cci", .data = &cci_v2_data},
|
{ .compatible = "qcom,sdm845-cci", .data = &cci_v2_data},
|
||||||
{ .compatible = "qcom,sm8250-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);
|
MODULE_DEVICE_TABLE(of, cci_dt_match);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dma/qcom-gpi-dma.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
|
@ -48,6 +50,9 @@
|
||||||
#define LOW_COUNTER_SHFT 10
|
#define LOW_COUNTER_SHFT 10
|
||||||
#define CYCLE_COUNTER_MSK GENMASK(9, 0)
|
#define CYCLE_COUNTER_MSK GENMASK(9, 0)
|
||||||
|
|
||||||
|
#define I2C_PACK_TX BIT(0)
|
||||||
|
#define I2C_PACK_RX BIT(1)
|
||||||
|
|
||||||
enum geni_i2c_err_code {
|
enum geni_i2c_err_code {
|
||||||
GP_IRQ0,
|
GP_IRQ0,
|
||||||
NACK,
|
NACK,
|
||||||
|
|
@ -89,6 +94,9 @@ struct geni_i2c_dev {
|
||||||
void *dma_buf;
|
void *dma_buf;
|
||||||
size_t xfer_len;
|
size_t xfer_len;
|
||||||
dma_addr_t dma_addr;
|
dma_addr_t dma_addr;
|
||||||
|
struct dma_chan *tx_c;
|
||||||
|
struct dma_chan *rx_c;
|
||||||
|
bool gpi_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct geni_i2c_err_log {
|
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;
|
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,
|
static int geni_i2c_xfer(struct i2c_adapter *adap,
|
||||||
struct i2c_msg msgs[],
|
struct i2c_msg msgs[],
|
||||||
int num)
|
int num)
|
||||||
{
|
{
|
||||||
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
|
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
|
||||||
int i, ret;
|
int ret;
|
||||||
|
|
||||||
gi2c->err = 0;
|
gi2c->err = 0;
|
||||||
reinit_completion(&gi2c->done);
|
reinit_completion(&gi2c->done);
|
||||||
|
|
@ -475,28 +678,17 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
|
||||||
}
|
}
|
||||||
|
|
||||||
qcom_geni_i2c_conf(gi2c);
|
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);
|
if (gi2c->gpi_mode)
|
||||||
|
ret = geni_i2c_gpi_xfer(gi2c, msgs, num);
|
||||||
gi2c->cur = &msgs[i];
|
else
|
||||||
if (msgs[i].flags & I2C_M_RD)
|
ret = geni_i2c_fifo_xfer(gi2c, msgs, num);
|
||||||
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;
|
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(gi2c->se.dev);
|
pm_runtime_mark_last_busy(gi2c->se.dev);
|
||||||
pm_runtime_put_autosuspend(gi2c->se.dev);
|
pm_runtime_put_autosuspend(gi2c->se.dev);
|
||||||
gi2c->cur = NULL;
|
gi2c->cur = NULL;
|
||||||
gi2c->err = 0;
|
gi2c->err = 0;
|
||||||
return ret;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 geni_i2c_func(struct i2c_adapter *adap)
|
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);
|
MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match);
|
||||||
#endif
|
#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)
|
static int geni_i2c_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct geni_i2c_dev *gi2c;
|
struct geni_i2c_dev *gi2c;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
u32 proto, tx_depth;
|
u32 proto, tx_depth, fifo_disable;
|
||||||
int ret;
|
int ret;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
|
|
@ -601,27 +832,43 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
proto = geni_se_read_proto(&gi2c->se);
|
proto = geni_se_read_proto(&gi2c->se);
|
||||||
tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
|
|
||||||
if (proto != GENI_SE_I2C) {
|
if (proto != GENI_SE_I2C) {
|
||||||
dev_err(dev, "Invalid proto %d\n", proto);
|
dev_err(dev, "Invalid proto %d\n", proto);
|
||||||
geni_se_resources_off(&gi2c->se);
|
geni_se_resources_off(&gi2c->se);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
gi2c->tx_wm = tx_depth - 1;
|
|
||||||
geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
|
fifo_disable = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE;
|
||||||
geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
|
if (fifo_disable) {
|
||||||
true, true, true);
|
/* 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);
|
ret = geni_se_resources_off(&gi2c->se);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Error turning off resources %d\n", ret);
|
dev_err(dev, "Error turning off resources %d\n", ret);
|
||||||
return ret;
|
goto err_dma;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = geni_icc_disable(&gi2c->se);
|
ret = geni_icc_disable(&gi2c->se);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto err_dma;
|
||||||
|
|
||||||
dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
|
|
||||||
|
|
||||||
gi2c->suspended = 1;
|
gi2c->suspended = 1;
|
||||||
pm_runtime_set_suspended(gi2c->se.dev);
|
pm_runtime_set_suspended(gi2c->se.dev);
|
||||||
|
|
@ -633,12 +880,16 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Error adding i2c adapter %d\n", ret);
|
dev_err(dev, "Error adding i2c adapter %d\n", ret);
|
||||||
pm_runtime_disable(gi2c->se.dev);
|
pm_runtime_disable(gi2c->se.dev);
|
||||||
return ret;
|
goto err_dma;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "Geni-I2C adaptor successfully added\n");
|
dev_dbg(dev, "Geni-I2C adaptor successfully added\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_dma:
|
||||||
|
release_gpi_dma(gi2c);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int geni_i2c_remove(struct platform_device *pdev)
|
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);
|
struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
i2c_del_adapter(&gi2c->adap);
|
i2c_del_adapter(&gi2c->adap);
|
||||||
|
release_gpi_dma(gi2c);
|
||||||
pm_runtime_disable(gi2c->se.dev);
|
pm_runtime_disable(gi2c->se.dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 },
|
||||||
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
|
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
|
||||||
{ .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 },
|
{ .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);
|
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,6 @@
|
||||||
|
|
||||||
#define RIIC_INIT_MSG -1
|
#define RIIC_INIT_MSG -1
|
||||||
|
|
||||||
enum riic_type {
|
|
||||||
RIIC_RZ_A,
|
|
||||||
RIIC_RZ_G2L,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct riic_dev {
|
struct riic_dev {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
|
@ -396,6 +391,11 @@ static struct riic_irq_desc riic_irqs[] = {
|
||||||
{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
|
{ .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)
|
static int riic_i2c_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct riic_dev *riic;
|
struct riic_dev *riic;
|
||||||
|
|
@ -404,7 +404,6 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
||||||
struct i2c_timings i2c_t;
|
struct i2c_timings i2c_t;
|
||||||
struct reset_control *rstc;
|
struct reset_control *rstc;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
enum riic_type type;
|
|
||||||
|
|
||||||
riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
|
riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL);
|
||||||
if (!riic)
|
if (!riic)
|
||||||
|
|
@ -421,16 +420,18 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
||||||
return PTR_ERR(riic->clk);
|
return PTR_ERR(riic->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
type = (enum riic_type)of_device_get_match_data(&pdev->dev);
|
rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||||
if (type == RIIC_RZ_G2L) {
|
if (IS_ERR(rstc))
|
||||||
rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
|
||||||
if (IS_ERR(rstc)) {
|
"Error: missing reset ctrl\n");
|
||||||
dev_err(&pdev->dev, "Error: missing reset ctrl\n");
|
|
||||||
return PTR_ERR(rstc);
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) {
|
||||||
ret = platform_get_irq(pdev, riic_irqs[i].res_num);
|
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[] = {
|
static const struct of_device_id riic_i2c_dt_ids[] = {
|
||||||
{ .compatible = "renesas,riic-r9a07g044", .data = (void *)RIIC_RZ_G2L },
|
{ .compatible = "renesas,riic-rz", },
|
||||||
{ .compatible = "renesas,riic-rz", .data = (void *)RIIC_RZ_A },
|
|
||||||
{ /* Sentinel */ },
|
{ /* Sentinel */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1233,6 +1233,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
i2c_dev->msg_buf = msg->buf;
|
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_buf_remaining = msg->len;
|
||||||
i2c_dev->msg_err = I2C_ERR_NONE;
|
i2c_dev->msg_err = I2C_ERR_NONE;
|
||||||
i2c_dev->msg_read = !!(msg->flags & I2C_M_RD);
|
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
|
else
|
||||||
end_type = MSG_END_REPEAT_START;
|
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);
|
ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
|
@ -1416,10 +1430,10 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||||
u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
|
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)
|
if (i2c_dev->hw->has_continue_xfer_support)
|
||||||
ret |= I2C_FUNC_NOSTART;
|
ret |= I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -734,7 +734,6 @@ static const struct i2c_adapter_quirks xiic_quirks = {
|
||||||
|
|
||||||
static const struct i2c_adapter xiic_adapter = {
|
static const struct i2c_adapter xiic_adapter = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = DRIVER_NAME,
|
|
||||||
.class = I2C_CLASS_DEPRECATED,
|
.class = I2C_CLASS_DEPRECATED,
|
||||||
.algo = &xiic_algorithm,
|
.algo = &xiic_algorithm,
|
||||||
.quirks = &xiic_quirks,
|
.quirks = &xiic_quirks,
|
||||||
|
|
@ -771,6 +770,8 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||||
i2c_set_adapdata(&i2c->adap, i2c);
|
i2c_set_adapdata(&i2c->adap, i2c);
|
||||||
i2c->adap.dev.parent = &pdev->dev;
|
i2c->adap.dev.parent = &pdev->dev;
|
||||||
i2c->adap.dev.of_node = pdev->dev.of_node;
|
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);
|
mutex_init(&i2c->lock);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,8 @@ static int i2c_acpi_get_info(struct acpi_device *adev,
|
||||||
struct acpi_device *adapter_adev;
|
struct acpi_device *adapter_adev;
|
||||||
|
|
||||||
/* The adapter must be present */
|
/* 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;
|
return -ENODEV;
|
||||||
if (acpi_bus_get_status(adapter_adev) ||
|
if (acpi_bus_get_status(adapter_adev) ||
|
||||||
!adapter_adev->status.present)
|
!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)
|
void *data, void **return_value)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adapter = data;
|
struct i2c_adapter *adapter = data;
|
||||||
struct acpi_device *adev;
|
struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
|
||||||
struct i2c_board_info info;
|
struct i2c_board_info info;
|
||||||
|
|
||||||
if (acpi_bus_get_device(handle, &adev))
|
if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL))
|
||||||
return AE_OK;
|
|
||||||
|
|
||||||
if (i2c_acpi_get_info(adev, &info, adapter, NULL))
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
|
||||||
i2c_acpi_register_device(adapter, adev, &info);
|
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)
|
void *data, void **return_value)
|
||||||
{
|
{
|
||||||
struct i2c_acpi_lookup *lookup = data;
|
struct i2c_acpi_lookup *lookup = data;
|
||||||
struct acpi_device *adev;
|
struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
|
||||||
|
|
||||||
if (acpi_bus_get_device(handle, &adev))
|
if (!adev || i2c_acpi_do_lookup(adev, lookup))
|
||||||
return AE_OK;
|
|
||||||
|
|
||||||
if (i2c_acpi_do_lookup(adev, lookup))
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
|
||||||
if (lookup->search_handle != lookup->adapter_handle)
|
if (lookup->search_handle != lookup->adapter_handle)
|
||||||
|
|
|
||||||
|
|
@ -1424,7 +1424,7 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
|
||||||
if (irq <= 0)
|
if (irq <= 0)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
generic_handle_irq(irq);
|
generic_handle_irq_safe(irq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1479,7 +1479,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||||
goto out_list;
|
goto out_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = of_i2c_setup_smbus_alert(adap);
|
res = i2c_setup_smbus_alert(adap);
|
||||||
if (res)
|
if (res)
|
||||||
goto out_reg;
|
goto out_reg;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@
|
||||||
|
|
||||||
#include "i2c-core.h"
|
#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 i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -79,6 +82,18 @@ int i2c_slave_unregister(struct i2c_client *client)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_slave_unregister);
|
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
|
* i2c_detect_slave_mode - detect operation mode
|
||||||
* @dev: The device owning the bus
|
* @dev: The device owning the bus
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c-smbus.h>
|
#include <linux/i2c-smbus.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include "i2c-core.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);
|
EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
|
#if IS_ENABLED(CONFIG_I2C_SMBUS)
|
||||||
int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
|
int i2c_setup_smbus_alert(struct i2c_adapter *adapter)
|
||||||
{
|
{
|
||||||
|
struct device *parent = adapter->dev.parent;
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
|
/* Adapter instantiated without parent, skip the SMBus alert setup */
|
||||||
"smbus_alert");
|
if (!parent)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
irq = device_property_match_string(parent, "interrupt-names", "smbus_alert");
|
||||||
if (irq == -EINVAL || irq == -ENODATA)
|
if (irq == -EINVAL || irq == -ENODATA)
|
||||||
return 0;
|
return 0;
|
||||||
else if (irq < 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));
|
return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -86,3 +86,12 @@ void of_i2c_register_devices(struct i2c_adapter *adap);
|
||||||
static inline void of_i2c_register_devices(struct i2c_adapter *adap) { }
|
static inline void of_i2c_register_devices(struct i2c_adapter *adap) { }
|
||||||
#endif
|
#endif
|
||||||
extern struct notifier_block i2c_of_notifier;
|
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
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/property.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
|
@ -128,7 +128,8 @@ static int smbalert_probe(struct i2c_client *ara,
|
||||||
if (setup) {
|
if (setup) {
|
||||||
irq = setup->irq;
|
irq = setup->irq;
|
||||||
} else {
|
} 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)
|
if (irq <= 0)
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,7 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err = device_create_file(&pdev->dev, &dev_attr_available_masters);
|
err = device_create_file(&pdev->dev, &dev_attr_available_masters);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_rollback;
|
goto err_rollback_activation;
|
||||||
|
|
||||||
err = device_create_file(&pdev->dev, &dev_attr_current_master);
|
err = device_create_file(&pdev->dev, &dev_attr_current_master);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -271,8 +271,9 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_rollback_available:
|
err_rollback_available:
|
||||||
device_remove_file(&pdev->dev, &dev_attr_available_masters);
|
device_remove_file(&pdev->dev, &dev_attr_available_masters);
|
||||||
err_rollback:
|
err_rollback_activation:
|
||||||
i2c_demux_deactivate_master(priv);
|
i2c_demux_deactivate_master(priv);
|
||||||
|
err_rollback:
|
||||||
for (j = 0; j < i; j++) {
|
for (j = 0; j < i; j++) {
|
||||||
of_node_put(priv->chan[j].parent_np);
|
of_node_put(priv->chan[j].parent_np);
|
||||||
of_changeset_destroy(&priv->chan[j].chgset);
|
of_changeset_destroy(&priv->chan[j].chgset);
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,6 @@ struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter,
|
||||||
struct i2c_smbus_alert_setup *setup);
|
struct i2c_smbus_alert_setup *setup);
|
||||||
int i2c_handle_smbus_alert(struct i2c_client *ara);
|
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)
|
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||||
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter);
|
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter);
|
||||||
void i2c_free_slave_host_notify_device(struct i2c_client *client);
|
void i2c_free_slave_host_notify_device(struct i2c_client *client);
|
||||||
|
|
|
||||||
|
|
@ -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_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
|
||||||
int i2c_slave_unregister(struct i2c_client *client);
|
int i2c_slave_unregister(struct i2c_client *client);
|
||||||
bool i2c_detect_slave_mode(struct device *dev);
|
bool i2c_detect_slave_mode(struct device *dev);
|
||||||
|
int i2c_slave_event(struct i2c_client *client,
|
||||||
static inline int i2c_slave_event(struct i2c_client *client,
|
enum i2c_slave_event event, u8 *val);
|
||||||
enum i2c_slave_event event, u8 *val)
|
|
||||||
{
|
|
||||||
return client->slave_cb(client, event, val);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
|
static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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_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, 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(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) \
|
#define request_mem_region_exclusive(start,n,name) \
|
||||||
__request_region(&iomem_resource, (start), (n), (name), IORESOURCE_EXCLUSIVE)
|
__request_region(&iomem_resource, (start), (n), (name), IORESOURCE_EXCLUSIVE)
|
||||||
#define rename_region(region, newname) do { (region)->name = (newname); } while (0)
|
#define rename_region(region, newname) do { (region)->name = (newname); } while (0)
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode);
|
||||||
void fwnode_handle_put(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(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);
|
void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index);
|
||||||
|
|
||||||
|
|
|
||||||
67
include/trace/events/i2c_slave.h
Normal file
67
include/trace/events/i2c_slave.h
Normal file
|
|
@ -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>
|
||||||
Loading…
Reference in New Issue
Block a user