mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 21:54:11 +08:00
i2c-for-6.13-rc1
i2c-core (Wolfram) - drivers can now use a GPIO as a side channel for SMBus Alerts using a generic binding - regular stuff like mem leak fix, Makefile maintenance... i2c-host updates (Andi) Major Improvements and Refactoring: - All controllers using the 'remove_new' callback have been reverted to use the 'remove' callback. - Intel SCH controller underwent significant refactoring, this brings love and a modern look to the driver. - PIIX4 driver refactored to enable usage by other drivers (e.g., AMD ASF). - iMX/MXC improved message handling to reduce protocol overhead: Refactored DMA/non-DMA read/write and bus polling mechanisms to achieve this. - ACPI documentation for PIIX4. New Features: - i2c-cadence added support for atomic transfers. - Qualcomm CII added support for a 32MHz serial engine clock. Deprecated Features: - Dropped outdated support for AMD756 S4882 and NFORCE2 S4985. If somebody misses this, Jean will rewrite support using the proper i2c mux framework. New Hardware Support: - Added support for: - Intel Panther Lake (new ID) - AMD ASF (new driver) - S32G2/S32G3 SoCs (new ID) - Realtek RTL I2C Controller (new driver) - HJMC01 DesignWare ACPI HID (new ID) - PIC64GX to Microchip Core (new ID) - Qualcomm SDM670 to Qualcomm CCI (new ID) at24 updates (Bartosz) - add support for the lockable page on ST M24256E -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEOZGx6rniZ1Gk92RdFA3kzBSgKbYFAmc+/UUACgkQFA3kzBSg KbZrqA/+MqNiN8NIkgA3nIODQP1Cf/aUhywHITsRgoo85vVDHeiaJtKc25KMi6Tb I5GgHp9Q0TtYdXzKM+nC7xjO64h8obGRXPHv5fDbRAZlFbWSR8u+iQp9entuVJe4 TiRFh9M8chAC82QNCvYVKx3jTHlmZaR7bXDtoAf7bG0LdWnjey1JSFfn6cPweVNM 0sD95tjBDb53tCRPGVKrCsnrs5uGxugGvH5I16hB6iokbaiIlGosIBg/8YBGHCS8 V8RqLSfzCl7X7xICeRv2ttRWx78Nz38RBC+truR5OEvvdZ+J2UCZ0710/w6FWfPp F85ReZrG5bh49Jobt/AyC7VXqL6djg+C6jVU1rX8Eb3G6TapL4iCJe9CcI4EXANY 1mtzZ8fqg4EaVMVRQx3tiyjNFWQba9ssSyzRB8ZfCqmS6xV2HXJby3zNoDA9SoxH tNn3vp0FfOtdn4lGbYAJE4zUMupXiUkrgswj5sKltYjXh/OmaDLJfYeFhPp+SwvW uWuLZveM0mAn6BWyM4g9v6MIHWiUumkF5xwlAfNOh3ogxqUtAPtbzk7FQ332ni0Z X11J5EOBzbMQR24moRo7xtQSdoDDSQDpytOVXTY2QWv3rZuk0oVL57aifJdtTkrK QktmylTfYlfI2hWLRoWFJ1j7nQyBveiNHh/qq0hxCkXLP3vA5gs= =lrn5 -----END PGP SIGNATURE----- Merge tag 'i2c-for-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux Pull i2c updates from Wolfram Sang: "Core: - drivers can now use a GPIO as a side channel for SMBus Alerts using a generic binding - regular stuff like mem leak fix, Makefile maintenance... Host improvements and refactoring: - All controllers using the 'remove_new' callback have been reverted to use the 'remove' callback - Intel SCH controller underwent significant refactoring, this brings love and a modern look to the driver - PIIX4 driver refactored to enable usage by other drivers (e.g., AMD ASF) - iMX/MXC improved message handling to reduce protocol overhead: Refactored DMA/non-DMA read/write and bus polling mechanisms to achieve this. - ACPI documentation for PIIX4 New host features: - i2c-cadence support for atomic transfers - Qualcomm CII support for a 32MHz serial engine clock Deprecated features: - Dropped outdated support for AMD756 S4882 and NFORCE2 S4985. If somebody misses this, Jean will rewrite support using the proper i2c mux framework. New hardware IDs for existing drivers: - Intel Panther Lake - S32G2/S32G3 SoCs - HJMC01 DesignWare ACPI HID - PIC64GX to Microchip Core - Qualcomm SDM670 to Qualcomm CCI New drivers: - AMD ASF - Realtek RTL I2C Controller at24 updates: - add support for the lockable page on ST M24256E" * tag 'i2c-for-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (59 commits) docs: i2c: piix4: Add ACPI section i2c: Add driver for the RTL9300 I2C controller i2c: qcom-cci: Remove unused struct member cci_clk_rate dt-bindings: i2c: Add Realtek RTL I2C Controller i2c: busses: Use *-y instead of *-objs in Makefile i2c: imx: add support for S32G2/S32G3 SoCs dt-bindings: i2c: imx: add SoC specific compatible strings for S32G i2c: qcom-cci: Remove the unused variable cci_clk_rate i2c: Drop legacy muxing pseudo-drivers i2c: imx: prevent rescheduling in non dma mode i2c: imx: separate atomic, dma and non-dma use case i2c: imx: do not poll for bus busy in single master mode i2c: designware: Add a new ACPI HID for HJMC01 I2C controller i2c: qcom-geni: Keep comment why interrupts start disabled dt-bindings: i2c: microchip: corei2c: Add PIC64GX as compatible with driver i2c: designware: constify abort_sources i2c: Switch back to struct platform_driver::remove() i2c: qcom-geni: Support systems with 32MHz serial engine clock i2c: qcom-cci: Stop complaining about DT set clock rate dt-bindings: i2c: qcom-cci: Document SDM670 compatible ...
This commit is contained in:
commit
55ae3eef10
@ -141,6 +141,8 @@ properties:
|
||||
- const: microchip,24aa025e48
|
||||
- items:
|
||||
- const: microchip,24aa025e64
|
||||
- items:
|
||||
- const: st,24256e-wl
|
||||
- pattern: '^atmel,24c(32|64)d-wl$' # Actual vendor is st
|
||||
|
||||
label:
|
||||
|
@ -18,6 +18,7 @@ properties:
|
||||
- const: fsl,imx1-i2c
|
||||
- const: fsl,imx21-i2c
|
||||
- const: fsl,vf610-i2c
|
||||
- const: nxp,s32g2-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,ls1012a-i2c
|
||||
@ -54,6 +55,9 @@ properties:
|
||||
- fsl,imx8mn-i2c
|
||||
- fsl,imx8mp-i2c
|
||||
- const: fsl,imx21-i2c
|
||||
- items:
|
||||
- const: nxp,s32g3-i2c
|
||||
- const: nxp,s32g2-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -16,7 +16,9 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: microchip,mpfs-i2c # Microchip PolarFire SoC compatible SoCs
|
||||
- enum:
|
||||
- microchip,pic64gx-i2c
|
||||
- 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
|
||||
|
||||
|
@ -27,6 +27,7 @@ properties:
|
||||
- enum:
|
||||
- qcom,sc7280-cci
|
||||
- qcom,sc8280xp-cci
|
||||
- qcom,sdm670-cci
|
||||
- qcom,sdm845-cci
|
||||
- qcom,sm6350-cci
|
||||
- qcom,sm8250-cci
|
||||
@ -139,6 +140,24 @@ allOf:
|
||||
- const: cci
|
||||
- const: camss_ahb
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sdm670-cci
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
clock-names:
|
||||
items:
|
||||
- const: camnoc_axi
|
||||
- const: soc_ahb
|
||||
- const: cpas_ahb
|
||||
- const: cci
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -0,0 +1,69 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/realtek,rtl9301-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek RTL I2C Controller
|
||||
|
||||
maintainers:
|
||||
- Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
|
||||
description:
|
||||
The RTL9300 SoC has two I2C controllers. Each of these has an SCL line (which
|
||||
if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
|
||||
assigned to either I2C controller.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- realtek,rtl9302b-i2c
|
||||
- realtek,rtl9302c-i2c
|
||||
- realtek,rtl9303-i2c
|
||||
- const: realtek,rtl9301-i2c
|
||||
- const: realtek,rtl9301-i2c
|
||||
|
||||
reg:
|
||||
description: Register offset and size this I2C controller.
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
'^i2c@[0-7]$':
|
||||
$ref: /schemas/i2c/i2c-controller.yaml
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: The SDA pin associated with the I2C bus.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c@36c {
|
||||
compatible = "realtek,rtl9301-i2c";
|
||||
reg = <0x36c 0x14>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
i2c@2 {
|
||||
reg = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
@ -49,6 +49,7 @@ Supported adapters:
|
||||
* Intel Meteor Lake (SOC and PCH)
|
||||
* Intel Birch Stream (SOC)
|
||||
* Intel Arrow Lake (SOC)
|
||||
* Intel Panther Lake (SOC)
|
||||
|
||||
Datasheets: Publicly available at the Intel website
|
||||
|
||||
|
@ -109,3 +109,66 @@ which can easily get corrupted due to a state machine bug. These are mostly
|
||||
Thinkpad laptops, but desktop systems may also be affected. We have no list
|
||||
of all affected systems, so the only safe solution was to prevent access to
|
||||
the SMBus on all IBM systems (detected using DMI data.)
|
||||
|
||||
|
||||
Description in the ACPI code
|
||||
----------------------------
|
||||
|
||||
Device driver for the PIIX4 chip creates a separate I2C bus for each of its
|
||||
ports::
|
||||
|
||||
$ i2cdetect -l
|
||||
...
|
||||
i2c-7 unknown SMBus PIIX4 adapter port 0 at 0b00 N/A
|
||||
i2c-8 unknown SMBus PIIX4 adapter port 2 at 0b00 N/A
|
||||
i2c-9 unknown SMBus PIIX4 adapter port 1 at 0b20 N/A
|
||||
...
|
||||
|
||||
Therefore if you want to access one of these busses in the ACPI code, port
|
||||
subdevices are needed to be declared inside the PIIX device::
|
||||
|
||||
Scope (\_SB_.PCI0.SMBS)
|
||||
{
|
||||
Name (_ADR, 0x00140000)
|
||||
|
||||
Device (SMB0) {
|
||||
Name (_ADR, 0)
|
||||
}
|
||||
Device (SMB1) {
|
||||
Name (_ADR, 1)
|
||||
}
|
||||
Device (SMB2) {
|
||||
Name (_ADR, 2)
|
||||
}
|
||||
}
|
||||
|
||||
If this is not the case for your UEFI firmware and you don't have access to the
|
||||
source code, you can use ACPI SSDT Overlays to provide the missing parts. Just
|
||||
keep in mind that in this case you would need to load your extra SSDT table
|
||||
before the piix4 driver starts, i.e. you should provide SSDT via initrd or EFI
|
||||
variable methods and not via configfs.
|
||||
|
||||
As an example of usage here is the ACPI snippet code that would assign jc42
|
||||
driver to the 0x1C device on the I2C bus created by the PIIX port 0::
|
||||
|
||||
Device (JC42) {
|
||||
Name (_HID, "PRP0001")
|
||||
Name (_DDN, "JC42 Temperature sensor")
|
||||
Name (_CRS, ResourceTemplate () {
|
||||
I2cSerialBusV2 (
|
||||
0x001c,
|
||||
ControllerInitiated,
|
||||
100000,
|
||||
AddressingMode7Bit,
|
||||
"\\_SB.PCI0.SMBS.SMB0",
|
||||
0
|
||||
)
|
||||
})
|
||||
|
||||
Name (_DSD, Package () {
|
||||
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
|
||||
Package () {
|
||||
Package () { "compatible", Package() { "jedec,jc-42.4-temp" } },
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -31,12 +31,11 @@ driver model device node, and its I2C address.
|
||||
|
||||
::
|
||||
|
||||
static struct i2c_device_id foo_idtable[] = {
|
||||
static const struct i2c_device_id foo_idtable[] = {
|
||||
{ "foo", my_id_for_foo },
|
||||
{ "bar", my_id_for_bar },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, foo_idtable);
|
||||
|
||||
static struct i2c_driver foo_driver = {
|
||||
|
17
MAINTAINERS
17
MAINTAINERS
@ -1115,6 +1115,12 @@ L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-amd-mp2*
|
||||
|
||||
AMD ASF I2C DRIVER
|
||||
M: Shyam Sundar S K <shyam-sundar.s-k@amd.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/i2c/busses/i2c-amd-asf-plat.c
|
||||
|
||||
AMD PDS CORE DRIVER
|
||||
M: Shannon Nelson <shannon.nelson@amd.com>
|
||||
M: Brett Creeley <brett.creeley@amd.com>
|
||||
@ -10750,14 +10756,12 @@ F: Documentation/i2c/busses/i2c-viapro.rst
|
||||
F: drivers/i2c/busses/i2c-ali1535.c
|
||||
F: drivers/i2c/busses/i2c-ali1563.c
|
||||
F: drivers/i2c/busses/i2c-ali15x3.c
|
||||
F: drivers/i2c/busses/i2c-amd756-s4882.c
|
||||
F: drivers/i2c/busses/i2c-amd756.c
|
||||
F: drivers/i2c/busses/i2c-amd8111.c
|
||||
F: drivers/i2c/busses/i2c-i801.c
|
||||
F: drivers/i2c/busses/i2c-isch.c
|
||||
F: drivers/i2c/busses/i2c-nforce2-s4985.c
|
||||
F: drivers/i2c/busses/i2c-nforce2.c
|
||||
F: drivers/i2c/busses/i2c-piix4.c
|
||||
F: drivers/i2c/busses/i2c-piix4.*
|
||||
F: drivers/i2c/busses/i2c-sis5595.c
|
||||
F: drivers/i2c/busses/i2c-sis630.c
|
||||
F: drivers/i2c/busses/i2c-sis96x.c
|
||||
@ -20218,6 +20222,13 @@ S: Maintained
|
||||
T: git https://github.com/pkshih/rtw.git
|
||||
F: drivers/net/wireless/realtek/rtl8xxxu/
|
||||
|
||||
RTL9300 I2C DRIVER (rtl9300-i2c)
|
||||
M: Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
|
||||
F: drivers/i2c/busses/i2c-rtl9300.c
|
||||
|
||||
RTRS TRANSPORT DRIVERS
|
||||
M: Md. Haris Iqbal <haris.iqbal@ionos.com>
|
||||
M: Jack Wang <jinpu.wang@ionos.com>
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
|
||||
obj-$(CONFIG_I2C) += i2c-core.o
|
||||
i2c-core-objs := i2c-core-base.o i2c-core-smbus.o
|
||||
i2c-core-y := i2c-core-base.o i2c-core-smbus.o
|
||||
i2c-core-$(CONFIG_ACPI) += i2c-core-acpi.o
|
||||
i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.o
|
||||
i2c-core-$(CONFIG_OF) += i2c-core-of.o
|
||||
i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.o
|
||||
i2c-core-$(CONFIG_OF) += i2c-core-of.o
|
||||
|
||||
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
|
||||
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
|
||||
|
@ -62,19 +62,6 @@ config I2C_AMD756
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-amd756.
|
||||
|
||||
config I2C_AMD756_S4882
|
||||
tristate "SMBus multiplexing on the Tyan S4882"
|
||||
depends on I2C_AMD756 && X86
|
||||
help
|
||||
Enabling this option will add specific SMBus support for the Tyan
|
||||
S4882 motherboard. On this 4-CPU board, the SMBus is multiplexed
|
||||
over 8 different channels, where the various memory module EEPROMs
|
||||
and temperature sensors live. Saying yes here will give you access
|
||||
to these in addition to the trunk.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-amd756-s4882.
|
||||
|
||||
config I2C_AMD8111
|
||||
tristate "AMD 8111"
|
||||
depends on PCI && HAS_IOPORT
|
||||
@ -95,6 +82,23 @@ config I2C_AMD_MP2
|
||||
This driver can also be built as modules. If so, the modules will
|
||||
be called i2c-amd-mp2-pci and i2c-amd-mp2-plat.
|
||||
|
||||
config I2C_AMD_ASF
|
||||
tristate "AMD ASF I2C Controller Support"
|
||||
depends on I2C_PIIX4
|
||||
select I2C_SLAVE
|
||||
help
|
||||
This option enables support for the AMD ASF (Alert Standard Format)
|
||||
I2C controller. The AMD ASF controller is an SMBus controller with
|
||||
built-in ASF functionality, allowing it to issue generic SMBus
|
||||
packets and communicate with the DASH controller using MCTP over
|
||||
ASF.
|
||||
|
||||
If you have an AMD system with ASF support and want to enable this
|
||||
functionality, say Y or M here. If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called i2c_amd_asf_plat.
|
||||
|
||||
config I2C_HIX5HD2
|
||||
tristate "Hix5hd2 high-speed I2C driver"
|
||||
depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST
|
||||
@ -160,6 +164,7 @@ config I2C_I801
|
||||
Meteor Lake (SOC and PCH)
|
||||
Birch Stream (SOC)
|
||||
Arrow Lake (SOC)
|
||||
Panther Lake (SOC)
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-i801.
|
||||
@ -250,19 +255,6 @@ config I2C_NFORCE2
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-nforce2.
|
||||
|
||||
config I2C_NFORCE2_S4985
|
||||
tristate "SMBus multiplexing on the Tyan S4985"
|
||||
depends on I2C_NFORCE2 && X86
|
||||
help
|
||||
Enabling this option will add specific SMBus support for the Tyan
|
||||
S4985 motherboard. On this 4-CPU board, the SMBus is multiplexed
|
||||
over 4 different channels, where the various memory module EEPROMs
|
||||
live. Saying yes here will give you access to these in addition
|
||||
to the trunk.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-nforce2-s4985.
|
||||
|
||||
config I2C_NVIDIA_GPU
|
||||
tristate "NVIDIA GPU I2C controller"
|
||||
depends on PCI
|
||||
@ -439,7 +431,7 @@ config I2C_AT91
|
||||
are facing this situation, use the i2c-gpio driver.
|
||||
|
||||
config I2C_AT91_SLAVE_EXPERIMENTAL
|
||||
tristate "Microchip AT91 I2C experimental slave mode"
|
||||
bool "Microchip AT91 I2C experimental slave mode"
|
||||
depends on I2C_AT91
|
||||
select I2C_SLAVE
|
||||
help
|
||||
@ -448,7 +440,7 @@ config I2C_AT91_SLAVE_EXPERIMENTAL
|
||||
been tested in a heavy way, help wanted.
|
||||
There are known bugs:
|
||||
- It can hang, on a SAMA5D4, after several transfers.
|
||||
- There are some mismtaches with a SAMA5D4 as slave and a SAMA5D2 as
|
||||
- There are some mismatches with a SAMA5D4 as slave and a SAMA5D2 as
|
||||
master.
|
||||
|
||||
config I2C_AU1550
|
||||
@ -751,13 +743,14 @@ config I2C_IMG
|
||||
|
||||
config I2C_IMX
|
||||
tristate "IMX I2C interface"
|
||||
depends on ARCH_MXC || ARCH_LAYERSCAPE || COLDFIRE || COMPILE_TEST
|
||||
depends on ARCH_MXC || ARCH_LAYERSCAPE || ARCH_S32 || COLDFIRE \
|
||||
|| COMPILE_TEST
|
||||
select I2C_SLAVE
|
||||
help
|
||||
Say Y here if you want to use the IIC bus controller on
|
||||
the Freescale i.MX/MXC, Layerscape or ColdFire processors.
|
||||
the Freescale i.MX/MXC/S32G, Layerscape or ColdFire processors.
|
||||
|
||||
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-imx.
|
||||
|
||||
config I2C_IMX_LPI2C
|
||||
@ -1070,6 +1063,16 @@ config I2C_RK3X
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called i2c-rk3x.
|
||||
|
||||
config I2C_RTL9300
|
||||
tristate "Realtek RTL9300 I2C controller"
|
||||
depends on MACH_REALTEK_RTL || COMPILE_TEST
|
||||
help
|
||||
Say Y here to include support for the I2C controller in Realtek
|
||||
RTL9300 SoCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called i2c-rtl9300.
|
||||
|
||||
config I2C_RZV2M
|
||||
tristate "Renesas RZ/V2M adapter"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
|
@ -14,14 +14,12 @@ obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
|
||||
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
|
||||
obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o
|
||||
obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o
|
||||
obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
|
||||
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
|
||||
obj-$(CONFIG_I2C_CHT_WC) += i2c-cht-wc.o
|
||||
obj-$(CONFIG_I2C_I801) += i2c-i801.o
|
||||
obj-$(CONFIG_I2C_ISCH) += i2c-isch.o
|
||||
obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o
|
||||
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
|
||||
obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o
|
||||
obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o
|
||||
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
|
||||
obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
|
||||
@ -38,12 +36,11 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
|
||||
# Embedded system I2C/SMBus host controller drivers
|
||||
obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
|
||||
obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
|
||||
obj-$(CONFIG_I2C_AMD_ASF) += i2c-amd-asf-plat.o
|
||||
obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
|
||||
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
|
||||
i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o
|
||||
ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y)
|
||||
i2c-at91-objs += i2c-at91-slave.o
|
||||
endif
|
||||
i2c-at91-y := i2c-at91-core.o i2c-at91-master.o
|
||||
i2c-at91-$(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL) += i2c-at91-slave.o
|
||||
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
||||
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
|
||||
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
|
||||
@ -104,6 +101,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o
|
||||
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
|
||||
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
|
||||
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
|
||||
obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o
|
||||
obj-$(CONFIG_I2C_RZV2M) += i2c-rzv2m.o
|
||||
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
|
||||
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
|
||||
@ -112,8 +110,8 @@ obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
|
||||
obj-$(CONFIG_I2C_SPRD) += i2c-sprd.o
|
||||
obj-$(CONFIG_I2C_ST) += i2c-st.o
|
||||
obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
|
||||
i2c-stm32f7-drv-objs := i2c-stm32f7.o i2c-stm32.o
|
||||
obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7-drv.o
|
||||
i2c-stm32f7-drv-y := i2c-stm32f7.o i2c-stm32.o
|
||||
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
|
||||
obj-$(CONFIG_I2C_SYNQUACER) += i2c-synquacer.o
|
||||
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
|
||||
@ -122,10 +120,10 @@ obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
|
||||
obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o
|
||||
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
||||
obj-$(CONFIG_I2C_WMT) += i2c-viai2c-wmt.o i2c-viai2c-common.o
|
||||
i2c-octeon-objs := i2c-octeon-core.o i2c-octeon-platdrv.o
|
||||
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
|
||||
i2c-thunderx-objs := i2c-octeon-core.o i2c-thunderx-pcidrv.o
|
||||
i2c-octeon-y := i2c-octeon-core.o i2c-octeon-platdrv.o
|
||||
obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o
|
||||
i2c-thunderx-y := i2c-octeon-core.o i2c-thunderx-pcidrv.o
|
||||
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
|
||||
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
|
||||
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
|
||||
|
@ -482,7 +482,7 @@ MODULE_DEVICE_TABLE(of, altr_i2c_of_match);
|
||||
|
||||
static struct platform_driver altr_i2c_driver = {
|
||||
.probe = altr_i2c_probe,
|
||||
.remove_new = altr_i2c_remove,
|
||||
.remove = altr_i2c_remove,
|
||||
.driver = {
|
||||
.name = "altera-i2c",
|
||||
.of_match_table = altr_i2c_of_match,
|
||||
|
369
drivers/i2c/busses/i2c-amd-asf-plat.c
Normal file
369
drivers/i2c/busses/i2c-amd-asf-plat.c
Normal file
@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* AMD Alert Standard Format Platform Driver
|
||||
*
|
||||
* Copyright (c) 2024, Advanced Micro Devices, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
* Sanket Goswami <Sanket.Goswami@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sprintf.h>
|
||||
|
||||
#include "i2c-piix4.h"
|
||||
|
||||
/* ASF register bits */
|
||||
#define ASF_SLV_LISTN 0
|
||||
#define ASF_SLV_INTR 1
|
||||
#define ASF_SLV_RST 4
|
||||
#define ASF_PEC_SP 5
|
||||
#define ASF_DATA_EN 7
|
||||
#define ASF_MSTR_EN 16
|
||||
#define ASF_CLK_EN 17
|
||||
|
||||
/* ASF address offsets */
|
||||
#define ASFINDEX (0x07 + piix4_smba)
|
||||
#define ASFLISADDR (0x09 + piix4_smba)
|
||||
#define ASFSTA (0x0A + piix4_smba)
|
||||
#define ASFSLVSTA (0x0D + piix4_smba)
|
||||
#define ASFDATARWPTR (0x11 + piix4_smba)
|
||||
#define ASFSETDATARDPTR (0x12 + piix4_smba)
|
||||
#define ASFDATABNKSEL (0x13 + piix4_smba)
|
||||
#define ASFSLVEN (0x15 + piix4_smba)
|
||||
|
||||
#define ASF_BLOCK_MAX_BYTES 72
|
||||
#define ASF_ERROR_STATUS GENMASK(3, 1)
|
||||
|
||||
struct amd_asf_dev {
|
||||
struct i2c_adapter adap;
|
||||
void __iomem *eoi_base;
|
||||
struct i2c_client *target;
|
||||
struct delayed_work work_buf;
|
||||
struct sb800_mmio_cfg mmio_cfg;
|
||||
struct resource *port_addr;
|
||||
};
|
||||
|
||||
static void amd_asf_process_target(struct work_struct *work)
|
||||
{
|
||||
struct amd_asf_dev *dev = container_of(work, struct amd_asf_dev, work_buf.work);
|
||||
unsigned short piix4_smba = dev->port_addr->start;
|
||||
u8 data[ASF_BLOCK_MAX_BYTES];
|
||||
u8 bank, reg, cmd;
|
||||
u8 len = 0, idx, val;
|
||||
|
||||
/* Read target status register */
|
||||
reg = inb_p(ASFSLVSTA);
|
||||
|
||||
/* Check if no error bits are set in target status register */
|
||||
if (reg & ASF_ERROR_STATUS) {
|
||||
/* Set bank as full */
|
||||
cmd = 0;
|
||||
reg |= GENMASK(3, 2);
|
||||
outb_p(reg, ASFDATABNKSEL);
|
||||
} else {
|
||||
/* Read data bank */
|
||||
reg = inb_p(ASFDATABNKSEL);
|
||||
bank = (reg & BIT(3)) ? 1 : 0;
|
||||
|
||||
/* Set read data bank */
|
||||
if (bank) {
|
||||
reg |= BIT(4);
|
||||
reg &= ~BIT(3);
|
||||
} else {
|
||||
reg &= ~BIT(4);
|
||||
reg &= ~BIT(2);
|
||||
}
|
||||
|
||||
/* Read command register */
|
||||
outb_p(reg, ASFDATABNKSEL);
|
||||
cmd = inb_p(ASFINDEX);
|
||||
len = inb_p(ASFDATARWPTR);
|
||||
for (idx = 0; idx < len; idx++)
|
||||
data[idx] = inb_p(ASFINDEX);
|
||||
|
||||
/* Clear data bank status */
|
||||
if (bank) {
|
||||
reg |= BIT(3);
|
||||
outb_p(reg, ASFDATABNKSEL);
|
||||
} else {
|
||||
reg |= BIT(2);
|
||||
outb_p(reg, ASFDATABNKSEL);
|
||||
}
|
||||
}
|
||||
|
||||
outb_p(0, ASFSETDATARDPTR);
|
||||
if (cmd & BIT(0))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Although i2c_slave_event() returns an appropriate error code, we
|
||||
* don't check it here because we're operating in the workqueue context.
|
||||
*/
|
||||
i2c_slave_event(dev->target, I2C_SLAVE_WRITE_REQUESTED, &val);
|
||||
for (idx = 0; idx < len; idx++) {
|
||||
val = data[idx];
|
||||
i2c_slave_event(dev->target, I2C_SLAVE_WRITE_RECEIVED, &val);
|
||||
}
|
||||
i2c_slave_event(dev->target, I2C_SLAVE_STOP, &val);
|
||||
}
|
||||
|
||||
static void amd_asf_update_ioport_target(unsigned short piix4_smba, u8 bit,
|
||||
unsigned long offset, bool set)
|
||||
{
|
||||
unsigned long reg;
|
||||
|
||||
reg = inb_p(offset);
|
||||
__assign_bit(bit, ®, set);
|
||||
outb_p(reg, offset);
|
||||
}
|
||||
|
||||
static void amd_asf_update_mmio_target(struct amd_asf_dev *dev, u8 bit, bool set)
|
||||
{
|
||||
unsigned long reg;
|
||||
|
||||
reg = ioread32(dev->mmio_cfg.addr);
|
||||
__assign_bit(bit, ®, set);
|
||||
iowrite32(reg, dev->mmio_cfg.addr);
|
||||
}
|
||||
|
||||
static void amd_asf_setup_target(struct amd_asf_dev *dev)
|
||||
{
|
||||
unsigned short piix4_smba = dev->port_addr->start;
|
||||
|
||||
/* Reset both host and target before setting up */
|
||||
outb_p(0, SMBHSTSTS);
|
||||
outb_p(0, ASFSLVSTA);
|
||||
outb_p(0, ASFSTA);
|
||||
|
||||
/* Update target address */
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, true);
|
||||
/* Enable target and set the clock */
|
||||
amd_asf_update_mmio_target(dev, ASF_MSTR_EN, false);
|
||||
amd_asf_update_mmio_target(dev, ASF_CLK_EN, true);
|
||||
/* Enable target interrupt */
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, ASFSLVEN, true);
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, false);
|
||||
/* Enable PEC and PEC append */
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_PEC_SP, SMBHSTCNT, true);
|
||||
}
|
||||
|
||||
static int amd_asf_access(struct i2c_adapter *adap, u16 addr, u8 command, u8 *data)
|
||||
{
|
||||
struct amd_asf_dev *dev = i2c_get_adapdata(adap);
|
||||
unsigned short piix4_smba = dev->port_addr->start;
|
||||
u8 i, len;
|
||||
|
||||
outb_p((addr << 1), SMBHSTADD);
|
||||
outb_p(command, SMBHSTCMD);
|
||||
len = data[0];
|
||||
if (len == 0 || len > ASF_BLOCK_MAX_BYTES)
|
||||
return -EINVAL;
|
||||
|
||||
outb_p(len, SMBHSTDAT0);
|
||||
/* Reset SMBBLKDAT */
|
||||
inb_p(SMBHSTCNT);
|
||||
for (i = 1; i <= len; i++)
|
||||
outb_p(data[i], SMBBLKDAT);
|
||||
|
||||
outb_p(PIIX4_BLOCK_DATA, SMBHSTCNT);
|
||||
/* Enable PEC and PEC append */
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_DATA_EN, SMBHSTCNT, true);
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_PEC_SP, SMBHSTCNT, true);
|
||||
|
||||
return piix4_transaction(adap, piix4_smba);
|
||||
}
|
||||
|
||||
static int amd_asf_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct amd_asf_dev *dev = i2c_get_adapdata(adap);
|
||||
unsigned short piix4_smba = dev->port_addr->start;
|
||||
u8 asf_data[ASF_BLOCK_MAX_BYTES];
|
||||
struct i2c_msg *dev_msgs = msgs;
|
||||
u8 prev_port;
|
||||
int ret;
|
||||
|
||||
if (msgs->flags & I2C_M_RD) {
|
||||
dev_err(&adap->dev, "ASF: Read not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Exclude the receive header and PEC */
|
||||
if (msgs->len > ASF_BLOCK_MAX_BYTES - 3) {
|
||||
dev_warn(&adap->dev, "ASF: max message length exceeded\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
asf_data[0] = dev_msgs->len;
|
||||
memcpy(asf_data + 1, dev_msgs[0].buf, dev_msgs->len);
|
||||
|
||||
ret = piix4_sb800_region_request(&adap->dev, &dev->mmio_cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, true);
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_LISTN, ASFLISADDR, false);
|
||||
/* Clear ASF target status */
|
||||
outb_p(0, ASFSLVSTA);
|
||||
|
||||
/* Enable ASF SMBus controller function */
|
||||
amd_asf_update_mmio_target(dev, ASF_MSTR_EN, true);
|
||||
prev_port = piix4_sb800_port_sel(0, &dev->mmio_cfg);
|
||||
ret = amd_asf_access(adap, msgs->addr, msgs[0].buf[0], asf_data);
|
||||
piix4_sb800_port_sel(prev_port, &dev->mmio_cfg);
|
||||
amd_asf_setup_target(dev);
|
||||
piix4_sb800_region_release(&adap->dev, &dev->mmio_cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int amd_asf_reg_target(struct i2c_client *target)
|
||||
{
|
||||
struct amd_asf_dev *dev = i2c_get_adapdata(target->adapter);
|
||||
unsigned short piix4_smba = dev->port_addr->start;
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
if (dev->target)
|
||||
return -EBUSY;
|
||||
|
||||
ret = piix4_sb800_region_request(&target->dev, &dev->mmio_cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = (target->addr << 1) | I2C_M_RD;
|
||||
outb_p(reg, ASFLISADDR);
|
||||
|
||||
amd_asf_setup_target(dev);
|
||||
dev->target = target;
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_DATA_EN, ASFDATABNKSEL, false);
|
||||
piix4_sb800_region_release(&target->dev, &dev->mmio_cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_asf_unreg_target(struct i2c_client *target)
|
||||
{
|
||||
struct amd_asf_dev *dev = i2c_get_adapdata(target->adapter);
|
||||
unsigned short piix4_smba = dev->port_addr->start;
|
||||
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, ASFSLVEN, false);
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_RST, ASFSLVEN, true);
|
||||
dev->target = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 amd_asf_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
|
||||
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_PEC | I2C_FUNC_SLAVE;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm amd_asf_smbus_algorithm = {
|
||||
.master_xfer = amd_asf_xfer,
|
||||
.reg_slave = amd_asf_reg_target,
|
||||
.unreg_slave = amd_asf_unreg_target,
|
||||
.functionality = amd_asf_func,
|
||||
};
|
||||
|
||||
static irqreturn_t amd_asf_irq_handler(int irq, void *ptr)
|
||||
{
|
||||
struct amd_asf_dev *dev = ptr;
|
||||
unsigned short piix4_smba = dev->port_addr->start;
|
||||
u8 target_int = inb_p(ASFSTA);
|
||||
|
||||
if (target_int & BIT(6)) {
|
||||
/* Target Interrupt */
|
||||
outb_p(target_int | BIT(6), ASFSTA);
|
||||
schedule_delayed_work(&dev->work_buf, HZ);
|
||||
} else {
|
||||
/* Controller Interrupt */
|
||||
amd_asf_update_ioport_target(piix4_smba, ASF_SLV_INTR, SMBHSTSTS, true);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int amd_asf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct amd_asf_dev *asf_dev;
|
||||
struct resource *eoi_addr;
|
||||
int ret, irq;
|
||||
|
||||
asf_dev = devm_kzalloc(dev, sizeof(*asf_dev), GFP_KERNEL);
|
||||
if (!asf_dev)
|
||||
return dev_err_probe(dev, -ENOMEM, "Failed to allocate memory\n");
|
||||
|
||||
asf_dev->mmio_cfg.use_mmio = true;
|
||||
asf_dev->port_addr = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!asf_dev->port_addr)
|
||||
return dev_err_probe(dev, -EINVAL, "missing IO resources\n");
|
||||
|
||||
/*
|
||||
* The resource obtained via ACPI might not belong to the ASF device address space. Instead,
|
||||
* it could be within other IP blocks of the ASIC, which are crucial for generating
|
||||
* subsequent interrupts. Therefore, we avoid using devm_platform_ioremap_resource() and
|
||||
* use platform_get_resource() and devm_ioremap() separately to prevent any address space
|
||||
* conflicts.
|
||||
*/
|
||||
eoi_addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!eoi_addr)
|
||||
return dev_err_probe(dev, -EINVAL, "missing MEM resources\n");
|
||||
|
||||
asf_dev->eoi_base = devm_ioremap(dev, eoi_addr->start, resource_size(eoi_addr));
|
||||
if (!asf_dev->eoi_base)
|
||||
return dev_err_probe(dev, -EBUSY, "failed mapping IO region\n");
|
||||
|
||||
ret = devm_delayed_work_autocancel(dev, &asf_dev->work_buf, amd_asf_process_target);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to create work queue\n");
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, irq, "missing IRQ resources\n");
|
||||
|
||||
ret = devm_request_irq(dev, irq, amd_asf_irq_handler, IRQF_SHARED, "amd_asf", asf_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Unable to request irq: %d for use\n", irq);
|
||||
|
||||
asf_dev->adap.owner = THIS_MODULE;
|
||||
asf_dev->adap.algo = &amd_asf_smbus_algorithm;
|
||||
asf_dev->adap.dev.parent = dev;
|
||||
|
||||
i2c_set_adapdata(&asf_dev->adap, asf_dev);
|
||||
snprintf(asf_dev->adap.name, sizeof(asf_dev->adap.name), "AMD ASF adapter");
|
||||
|
||||
return devm_i2c_add_adapter(dev, &asf_dev->adap);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id amd_asf_acpi_ids[] = {
|
||||
{ "AMDI001A" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, amd_asf_acpi_ids);
|
||||
|
||||
static struct platform_driver amd_asf_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-amd-asf",
|
||||
.acpi_match_table = amd_asf_acpi_ids,
|
||||
},
|
||||
.probe = amd_asf_probe,
|
||||
};
|
||||
module_platform_driver(amd_asf_driver);
|
||||
|
||||
MODULE_IMPORT_NS(PIIX4_SMBUS);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("AMD Alert Standard Format Driver");
|
@ -346,7 +346,7 @@ MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match);
|
||||
|
||||
static struct platform_driver i2c_amd_plat_driver = {
|
||||
.probe = i2c_amd_probe,
|
||||
.remove_new = i2c_amd_remove,
|
||||
.remove = i2c_amd_remove,
|
||||
.driver = {
|
||||
.name = "i2c_amd_mp2",
|
||||
.acpi_match_table = ACPI_PTR(i2c_amd_acpi_match),
|
||||
|
@ -1,245 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
|
||||
*
|
||||
* Copyright (C) 2004, 2008 Jean Delvare <jdelvare@suse.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* We select the channels by sending commands to the Philips
|
||||
* PCA9556 chip at I2C address 0x18. The main adapter is used for
|
||||
* the non-multiplexed part of the bus, and 4 virtual adapters
|
||||
* are defined for the multiplexed addresses: 0x50-0x53 (memory
|
||||
* module EEPROM) located on channels 1-4, and 0x4c (LM63)
|
||||
* located on multiplexed channels 0 and 5-7. We define one
|
||||
* virtual adapter per CPU, which corresponds to two multiplexed
|
||||
* channels:
|
||||
* CPU0: virtual adapter 1, channels 1 and 0
|
||||
* CPU1: virtual adapter 2, channels 2 and 5
|
||||
* CPU2: virtual adapter 3, channels 3 and 6
|
||||
* CPU3: virtual adapter 4, channels 4 and 7
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
extern struct i2c_adapter amd756_smbus;
|
||||
|
||||
static struct i2c_adapter *s4882_adapter;
|
||||
static struct i2c_algorithm *s4882_algo;
|
||||
|
||||
/* Wrapper access functions for multiplexed SMBus */
|
||||
static DEFINE_MUTEX(amd756_lock);
|
||||
|
||||
static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data * data)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* We exclude the multiplexed addresses */
|
||||
if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
|
||||
|| addr == 0x18)
|
||||
return -ENXIO;
|
||||
|
||||
mutex_lock(&amd756_lock);
|
||||
|
||||
error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
|
||||
command, size, data);
|
||||
|
||||
mutex_unlock(&amd756_lock);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* We remember the last used channels combination so as to only switch
|
||||
channels when it is really needed. This greatly reduces the SMBus
|
||||
overhead, but also assumes that nobody will be writing to the PCA9556
|
||||
in our back. */
|
||||
static u8 last_channels;
|
||||
|
||||
static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data * data,
|
||||
u8 channels)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* We exclude the non-multiplexed addresses */
|
||||
if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
|
||||
return -ENXIO;
|
||||
|
||||
mutex_lock(&amd756_lock);
|
||||
|
||||
if (last_channels != channels) {
|
||||
union i2c_smbus_data mplxdata;
|
||||
mplxdata.byte = channels;
|
||||
|
||||
error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
|
||||
I2C_SMBUS_WRITE, 0x01,
|
||||
I2C_SMBUS_BYTE_DATA,
|
||||
&mplxdata);
|
||||
if (error)
|
||||
goto UNLOCK;
|
||||
last_channels = channels;
|
||||
}
|
||||
error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
|
||||
command, size, data);
|
||||
|
||||
UNLOCK:
|
||||
mutex_unlock(&amd756_lock);
|
||||
return error;
|
||||
}
|
||||
|
||||
static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data * data)
|
||||
{
|
||||
/* CPU0: channels 1 and 0 enabled */
|
||||
return amd756_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x03);
|
||||
}
|
||||
|
||||
static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data * data)
|
||||
{
|
||||
/* CPU1: channels 2 and 5 enabled */
|
||||
return amd756_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x24);
|
||||
}
|
||||
|
||||
static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data * data)
|
||||
{
|
||||
/* CPU2: channels 3 and 6 enabled */
|
||||
return amd756_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x48);
|
||||
}
|
||||
|
||||
static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data * data)
|
||||
{
|
||||
/* CPU3: channels 4 and 7 enabled */
|
||||
return amd756_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x90);
|
||||
}
|
||||
|
||||
static int __init amd756_s4882_init(void)
|
||||
{
|
||||
int i, error;
|
||||
union i2c_smbus_data ioconfig;
|
||||
|
||||
if (!amd756_smbus.dev.parent)
|
||||
return -ENODEV;
|
||||
|
||||
/* Configure the PCA9556 multiplexer */
|
||||
ioconfig.byte = 0x00; /* All I/O to output mode */
|
||||
error = i2c_smbus_xfer(&amd756_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
|
||||
I2C_SMBUS_BYTE_DATA, &ioconfig);
|
||||
if (error) {
|
||||
dev_err(&amd756_smbus.dev, "PCA9556 configuration failed\n");
|
||||
error = -EIO;
|
||||
goto ERROR0;
|
||||
}
|
||||
|
||||
/* Unregister physical bus */
|
||||
i2c_del_adapter(&amd756_smbus);
|
||||
|
||||
printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
|
||||
/* Define the 5 virtual adapters and algorithms structures */
|
||||
if (!(s4882_adapter = kcalloc(5, sizeof(struct i2c_adapter),
|
||||
GFP_KERNEL))) {
|
||||
error = -ENOMEM;
|
||||
goto ERROR1;
|
||||
}
|
||||
if (!(s4882_algo = kcalloc(5, sizeof(struct i2c_algorithm),
|
||||
GFP_KERNEL))) {
|
||||
error = -ENOMEM;
|
||||
goto ERROR2;
|
||||
}
|
||||
|
||||
/* Fill in the new structures */
|
||||
s4882_algo[0] = *(amd756_smbus.algo);
|
||||
s4882_algo[0].smbus_xfer = amd756_access_virt0;
|
||||
s4882_adapter[0] = amd756_smbus;
|
||||
s4882_adapter[0].algo = s4882_algo;
|
||||
s4882_adapter[0].dev.parent = amd756_smbus.dev.parent;
|
||||
for (i = 1; i < 5; i++) {
|
||||
s4882_algo[i] = *(amd756_smbus.algo);
|
||||
s4882_adapter[i] = amd756_smbus;
|
||||
snprintf(s4882_adapter[i].name, sizeof(s4882_adapter[i].name),
|
||||
"SMBus 8111 adapter (CPU%d)", i-1);
|
||||
s4882_adapter[i].algo = s4882_algo+i;
|
||||
s4882_adapter[i].dev.parent = amd756_smbus.dev.parent;
|
||||
}
|
||||
s4882_algo[1].smbus_xfer = amd756_access_virt1;
|
||||
s4882_algo[2].smbus_xfer = amd756_access_virt2;
|
||||
s4882_algo[3].smbus_xfer = amd756_access_virt3;
|
||||
s4882_algo[4].smbus_xfer = amd756_access_virt4;
|
||||
|
||||
/* Register virtual adapters */
|
||||
for (i = 0; i < 5; i++) {
|
||||
error = i2c_add_adapter(s4882_adapter+i);
|
||||
if (error) {
|
||||
printk(KERN_ERR "i2c-amd756-s4882: "
|
||||
"Virtual adapter %d registration "
|
||||
"failed, module not inserted\n", i);
|
||||
for (i--; i >= 0; i--)
|
||||
i2c_del_adapter(s4882_adapter+i);
|
||||
goto ERROR3;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERROR3:
|
||||
kfree(s4882_algo);
|
||||
s4882_algo = NULL;
|
||||
ERROR2:
|
||||
kfree(s4882_adapter);
|
||||
s4882_adapter = NULL;
|
||||
ERROR1:
|
||||
/* Restore physical bus */
|
||||
i2c_add_adapter(&amd756_smbus);
|
||||
ERROR0:
|
||||
return error;
|
||||
}
|
||||
|
||||
static void __exit amd756_s4882_exit(void)
|
||||
{
|
||||
if (s4882_adapter) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
i2c_del_adapter(s4882_adapter+i);
|
||||
kfree(s4882_adapter);
|
||||
s4882_adapter = NULL;
|
||||
}
|
||||
kfree(s4882_algo);
|
||||
s4882_algo = NULL;
|
||||
|
||||
/* Restore physical bus */
|
||||
if (i2c_add_adapter(&amd756_smbus))
|
||||
printk(KERN_ERR "i2c-amd756-s4882: "
|
||||
"Physical bus restoration failed\n");
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
||||
MODULE_DESCRIPTION("S4882 SMBus multiplexing");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(amd756_s4882_init);
|
||||
module_exit(amd756_s4882_exit);
|
@ -283,7 +283,7 @@ static const struct i2c_algorithm smbus_algorithm = {
|
||||
.functionality = amd756_func,
|
||||
};
|
||||
|
||||
struct i2c_adapter amd756_smbus = {
|
||||
static struct i2c_adapter amd756_smbus = {
|
||||
.owner = THIS_MODULE,
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.algo = &smbus_algorithm,
|
||||
@ -398,5 +398,3 @@ module_pci_driver(amd756_driver);
|
||||
MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
|
||||
MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
EXPORT_SYMBOL(amd756_smbus);
|
||||
|
@ -1102,7 +1102,7 @@ static void aspeed_i2c_remove_bus(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver aspeed_i2c_bus_driver = {
|
||||
.probe = aspeed_i2c_probe_bus,
|
||||
.remove_new = aspeed_i2c_remove_bus,
|
||||
.remove = aspeed_i2c_remove_bus,
|
||||
.driver = {
|
||||
.name = "aspeed-i2c-bus",
|
||||
.of_match_table = aspeed_i2c_bus_of_table,
|
||||
|
@ -330,7 +330,7 @@ static const struct dev_pm_ops __maybe_unused at91_twi_pm = {
|
||||
|
||||
static struct platform_driver at91_twi_driver = {
|
||||
.probe = at91_twi_probe,
|
||||
.remove_new = at91_twi_remove,
|
||||
.remove = at91_twi_remove,
|
||||
.id_table = at91_twi_devtypes,
|
||||
.driver = {
|
||||
.name = "at91_i2c",
|
||||
|
@ -368,7 +368,7 @@ static struct platform_driver au1xpsc_smbus_driver = {
|
||||
.pm = pm_sleep_ptr(&i2c_au1550_pmops),
|
||||
},
|
||||
.probe = i2c_au1550_probe,
|
||||
.remove_new = i2c_au1550_remove,
|
||||
.remove = i2c_au1550_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(au1xpsc_smbus_driver);
|
||||
|
@ -824,7 +824,7 @@ MODULE_DEVICE_TABLE(of, axxia_i2c_of_match);
|
||||
|
||||
static struct platform_driver axxia_i2c_driver = {
|
||||
.probe = axxia_i2c_probe,
|
||||
.remove_new = axxia_i2c_remove,
|
||||
.remove = axxia_i2c_remove,
|
||||
.driver = {
|
||||
.name = "axxia-i2c",
|
||||
.of_match_table = axxia_i2c_of_match,
|
||||
|
@ -1264,7 +1264,7 @@ static struct platform_driver bcm_iproc_i2c_driver = {
|
||||
.pm = pm_sleep_ptr(&bcm_iproc_i2c_pm_ops),
|
||||
},
|
||||
.probe = bcm_iproc_i2c_probe,
|
||||
.remove_new = bcm_iproc_i2c_remove,
|
||||
.remove = bcm_iproc_i2c_remove,
|
||||
};
|
||||
module_platform_driver(bcm_iproc_i2c_driver);
|
||||
|
||||
|
@ -877,7 +877,7 @@ static struct platform_driver bcm_kona_i2c_driver = {
|
||||
.of_match_table = bcm_kona_i2c_of_match,
|
||||
},
|
||||
.probe = bcm_kona_i2c_probe,
|
||||
.remove_new = bcm_kona_i2c_remove,
|
||||
.remove = bcm_kona_i2c_remove,
|
||||
};
|
||||
module_platform_driver(bcm_kona_i2c_driver);
|
||||
|
||||
|
@ -520,7 +520,7 @@ MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match);
|
||||
|
||||
static struct platform_driver bcm2835_i2c_driver = {
|
||||
.probe = bcm2835_i2c_probe,
|
||||
.remove_new = bcm2835_i2c_remove,
|
||||
.remove = bcm2835_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c-bcm2835",
|
||||
.of_match_table = bcm2835_i2c_of_match,
|
||||
|
@ -744,7 +744,7 @@ static struct platform_driver brcmstb_i2c_driver = {
|
||||
.pm = pm_sleep_ptr(&brcmstb_i2c_pm),
|
||||
},
|
||||
.probe = brcmstb_i2c_probe,
|
||||
.remove_new = brcmstb_i2c_remove,
|
||||
.remove = brcmstb_i2c_remove,
|
||||
};
|
||||
module_platform_driver(brcmstb_i2c_driver);
|
||||
|
||||
|
@ -129,6 +129,7 @@
|
||||
|
||||
#define CDNS_I2C_BROKEN_HOLD_BIT BIT(0)
|
||||
#define CDNS_I2C_POLL_US 100000
|
||||
#define CDNS_I2C_POLL_US_ATOMIC 10
|
||||
#define CDNS_I2C_TIMEOUT_US 500000
|
||||
|
||||
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
|
||||
@ -189,6 +190,8 @@ enum cdns_i2c_slave_state {
|
||||
* @slave_state: I2C Slave state(idle/read/write).
|
||||
* @fifo_depth: The depth of the transfer FIFO
|
||||
* @transfer_size: The maximum number of bytes in one transfer
|
||||
* @atomic: Mode of transfer
|
||||
* @err_status_atomic: Error status in atomic mode
|
||||
*/
|
||||
struct cdns_i2c {
|
||||
struct device *dev;
|
||||
@ -219,6 +222,8 @@ struct cdns_i2c {
|
||||
#endif
|
||||
u32 fifo_depth;
|
||||
unsigned int transfer_size;
|
||||
bool atomic;
|
||||
int err_status_atomic;
|
||||
};
|
||||
|
||||
struct cdns_platform_data {
|
||||
@ -228,6 +233,66 @@ struct cdns_platform_data {
|
||||
#define to_cdns_i2c(_nb) container_of(_nb, struct cdns_i2c, \
|
||||
clk_rate_change_nb)
|
||||
|
||||
/**
|
||||
* cdns_i2c_init - Controller initialisation
|
||||
* @id: Device private data structure
|
||||
*
|
||||
* Initialise the i2c controller.
|
||||
*
|
||||
*/
|
||||
static void cdns_i2c_init(struct cdns_i2c *id)
|
||||
{
|
||||
cdns_i2c_writereg(id->ctrl_reg, CDNS_I2C_CR_OFFSET);
|
||||
/*
|
||||
* Cadence I2C controller has a bug wherein it generates
|
||||
* invalid read transaction after HW timeout in master receiver mode.
|
||||
* HW timeout is not used by this driver and the interrupt is disabled.
|
||||
* But the feature itself cannot be disabled. Hence maximum value
|
||||
* is written to this register to reduce the chances of error.
|
||||
*/
|
||||
cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_runtime_suspend - Runtime suspend method for the driver
|
||||
* @dev: Address of the platform_device structure
|
||||
*
|
||||
* Put the driver into low power mode.
|
||||
*
|
||||
* Return: 0 always
|
||||
*/
|
||||
static int cdns_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(xi2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_runtime_resume - Runtime resume
|
||||
* @dev: Address of the platform_device structure
|
||||
*
|
||||
* Runtime resume callback.
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int cdns_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(xi2c->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
cdns_i2c_init(xi2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_clear_bus_hold - Clear bus hold bit
|
||||
* @id: Pointer to driver data struct
|
||||
@ -561,6 +626,89 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
|
||||
return cdns_i2c_master_isr(ptr);
|
||||
}
|
||||
|
||||
static bool cdns_i2c_error_check(struct cdns_i2c *id)
|
||||
{
|
||||
unsigned int isr_status;
|
||||
|
||||
id->err_status = 0;
|
||||
|
||||
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
|
||||
cdns_i2c_writereg(isr_status & CDNS_I2C_IXR_ERR_INTR_MASK, CDNS_I2C_ISR_OFFSET);
|
||||
|
||||
id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
|
||||
|
||||
return !!id->err_status;
|
||||
}
|
||||
|
||||
static void cdns_i2c_mrecv_atomic(struct cdns_i2c *id)
|
||||
{
|
||||
while (id->recv_count > 0) {
|
||||
bool updatetx;
|
||||
|
||||
/*
|
||||
* Check if transfer size register needs to be updated again for a
|
||||
* large data receive operation.
|
||||
*/
|
||||
updatetx = id->recv_count > id->curr_recv_count;
|
||||
|
||||
while (id->curr_recv_count > 0) {
|
||||
if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_RXDV) {
|
||||
*id->p_recv_buf = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
|
||||
id->p_recv_buf++;
|
||||
id->recv_count--;
|
||||
id->curr_recv_count--;
|
||||
|
||||
/*
|
||||
* Clear the hold bit that was set for FIFO control,
|
||||
* if the remaining RX data is less than or equal to
|
||||
* the FIFO depth, unless a repeated start is selected.
|
||||
*/
|
||||
if (id->recv_count <= id->fifo_depth && !id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
}
|
||||
if (cdns_i2c_error_check(id))
|
||||
return;
|
||||
if (cdns_is_holdquirk(id, updatetx))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The controller sends NACK to the slave/target when transfer size
|
||||
* register reaches zero without considering the HOLD bit.
|
||||
* This workaround is implemented for large data transfers to
|
||||
* maintain transfer size non-zero while performing a large
|
||||
* receive operation.
|
||||
*/
|
||||
if (cdns_is_holdquirk(id, updatetx)) {
|
||||
/* wait while fifo is full */
|
||||
while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
|
||||
(id->curr_recv_count - id->fifo_depth))
|
||||
;
|
||||
|
||||
/*
|
||||
* Check number of bytes to be received against maximum
|
||||
* transfer size and update register accordingly.
|
||||
*/
|
||||
if ((id->recv_count - id->fifo_depth) >
|
||||
id->transfer_size) {
|
||||
cdns_i2c_writereg(id->transfer_size,
|
||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
id->curr_recv_count = id->transfer_size +
|
||||
id->fifo_depth;
|
||||
} else {
|
||||
cdns_i2c_writereg(id->recv_count -
|
||||
id->fifo_depth,
|
||||
CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
id->curr_recv_count = id->recv_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear hold (if not repeated start) */
|
||||
if (!id->recv_count && !id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_mrecv - Prepare and start a master receive operation
|
||||
* @id: pointer to the i2c device structure
|
||||
@ -655,7 +803,34 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
|
||||
cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
|
||||
}
|
||||
|
||||
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
|
||||
if (!id->atomic)
|
||||
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
|
||||
else
|
||||
cdns_i2c_mrecv_atomic(id);
|
||||
}
|
||||
|
||||
static void cdns_i2c_msend_rem_atomic(struct cdns_i2c *id)
|
||||
{
|
||||
while (id->send_count) {
|
||||
unsigned int avail_bytes;
|
||||
unsigned int bytes_to_send;
|
||||
|
||||
avail_bytes = id->fifo_depth - cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
|
||||
if (id->send_count > avail_bytes)
|
||||
bytes_to_send = avail_bytes;
|
||||
else
|
||||
bytes_to_send = id->send_count;
|
||||
|
||||
while (bytes_to_send--) {
|
||||
cdns_i2c_writereg((*id->p_send_buf++), CDNS_I2C_DATA_OFFSET);
|
||||
id->send_count--;
|
||||
}
|
||||
if (cdns_i2c_error_check(id))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!id->send_count && !id->bus_hold_flag)
|
||||
cdns_i2c_clear_bus_hold(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -718,7 +893,10 @@ static void cdns_i2c_msend(struct cdns_i2c *id)
|
||||
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
|
||||
CDNS_I2C_ADDR_OFFSET);
|
||||
|
||||
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
|
||||
if (!id->atomic)
|
||||
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
|
||||
else if (id->send_count > 0)
|
||||
cdns_i2c_msend_rem_atomic(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -758,7 +936,8 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
|
||||
|
||||
id->p_msg = msg;
|
||||
id->err_status = 0;
|
||||
reinit_completion(&id->xfer_done);
|
||||
if (!id->atomic)
|
||||
reinit_completion(&id->xfer_done);
|
||||
|
||||
/* Check for the TEN Bit mode on each msg */
|
||||
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
@ -780,14 +959,31 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
|
||||
|
||||
/* Minimal time to execute this message */
|
||||
msg_timeout = msecs_to_jiffies((1000 * msg->len * BITS_PER_BYTE) / id->i2c_clk);
|
||||
/* Plus some wiggle room */
|
||||
msg_timeout += msecs_to_jiffies(500);
|
||||
|
||||
/*
|
||||
* Plus some wiggle room.
|
||||
* For non-atomic contexts, 500 ms is added to the timeout.
|
||||
* For atomic contexts, 2000 ms is added because transfers happen in polled
|
||||
* mode, requiring more time to account for the polling overhead.
|
||||
*/
|
||||
if (!id->atomic)
|
||||
msg_timeout += msecs_to_jiffies(500);
|
||||
else
|
||||
msg_timeout += msecs_to_jiffies(2000);
|
||||
|
||||
if (msg_timeout < adap->timeout)
|
||||
msg_timeout = adap->timeout;
|
||||
|
||||
/* Wait for the signal of completion */
|
||||
time_left = wait_for_completion_timeout(&id->xfer_done, msg_timeout);
|
||||
if (!id->atomic) {
|
||||
/* Wait for the signal of completion */
|
||||
time_left = wait_for_completion_timeout(&id->xfer_done, msg_timeout);
|
||||
} else {
|
||||
/* 0 is success, -ETIMEDOUT is error */
|
||||
time_left = !readl_poll_timeout_atomic(id->membase + CDNS_I2C_ISR_OFFSET,
|
||||
reg, (reg & CDNS_I2C_IXR_COMP),
|
||||
CDNS_I2C_POLL_US_ATOMIC, msg_timeout);
|
||||
}
|
||||
|
||||
if (time_left == 0) {
|
||||
cdns_i2c_master_reset(adap);
|
||||
return -ETIMEDOUT;
|
||||
@ -806,6 +1002,83 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_i2c_master_common_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
int ret, count;
|
||||
u32 reg;
|
||||
struct cdns_i2c *id = adap->algo_data;
|
||||
bool hold_quirk;
|
||||
|
||||
/* Check if the bus is free */
|
||||
if (!id->atomic)
|
||||
ret = readl_relaxed_poll_timeout(id->membase + CDNS_I2C_SR_OFFSET,
|
||||
reg,
|
||||
!(reg & CDNS_I2C_SR_BA),
|
||||
CDNS_I2C_POLL_US, CDNS_I2C_TIMEOUT_US);
|
||||
else
|
||||
ret = readl_poll_timeout_atomic(id->membase + CDNS_I2C_SR_OFFSET,
|
||||
reg,
|
||||
!(reg & CDNS_I2C_SR_BA),
|
||||
CDNS_I2C_POLL_US_ATOMIC, CDNS_I2C_TIMEOUT_US);
|
||||
if (ret) {
|
||||
ret = -EAGAIN;
|
||||
if (id->adap.bus_recovery_info)
|
||||
i2c_recover_bus(adap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
|
||||
/*
|
||||
* Set the flag to one when multiple messages are to be
|
||||
* processed with a repeated start.
|
||||
*/
|
||||
if (num > 1) {
|
||||
/*
|
||||
* This controller does not give completion interrupt after a
|
||||
* master receive message if HOLD bit is set (repeated start),
|
||||
* resulting in SW timeout. Hence, if a receive message is
|
||||
* followed by any other message, an error is returned
|
||||
* indicating that this sequence is not supported.
|
||||
*/
|
||||
for (count = 0; (count < num - 1 && hold_quirk); count++) {
|
||||
if (msgs[count].flags & I2C_M_RD) {
|
||||
dev_warn(adap->dev.parent,
|
||||
"Can't do repeated start after a receive message\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
id->bus_hold_flag = 1;
|
||||
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
reg |= CDNS_I2C_CR_HOLD;
|
||||
cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
|
||||
} else {
|
||||
id->bus_hold_flag = 0;
|
||||
}
|
||||
|
||||
/* Process the msg one by one */
|
||||
for (count = 0; count < num; count++, msgs++) {
|
||||
if (count == (num - 1))
|
||||
id->bus_hold_flag = 0;
|
||||
|
||||
ret = cdns_i2c_process_msg(id, msgs, adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Report the other error interrupts to application */
|
||||
if (id->err_status || id->err_status_atomic) {
|
||||
cdns_i2c_master_reset(adap);
|
||||
|
||||
if (id->err_status & CDNS_I2C_IXR_NACK)
|
||||
return -ENXIO;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_master_xfer - The main i2c transfer function
|
||||
* @adap: pointer to the i2c adapter driver instance
|
||||
@ -819,10 +1092,8 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
|
||||
static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
int ret, count;
|
||||
u32 reg;
|
||||
int ret;
|
||||
struct cdns_i2c *id = adap->algo_data;
|
||||
bool hold_quirk;
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
bool change_role = false;
|
||||
#endif
|
||||
@ -847,75 +1118,11 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check if the bus is free */
|
||||
|
||||
ret = readl_relaxed_poll_timeout(id->membase + CDNS_I2C_SR_OFFSET,
|
||||
reg,
|
||||
!(reg & CDNS_I2C_SR_BA),
|
||||
CDNS_I2C_POLL_US, CDNS_I2C_TIMEOUT_US);
|
||||
if (ret) {
|
||||
ret = -EAGAIN;
|
||||
if (id->adap.bus_recovery_info)
|
||||
i2c_recover_bus(adap);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
|
||||
/*
|
||||
* Set the flag to one when multiple messages are to be
|
||||
* processed with a repeated start.
|
||||
*/
|
||||
if (num > 1) {
|
||||
/*
|
||||
* This controller does not give completion interrupt after a
|
||||
* master receive message if HOLD bit is set (repeated start),
|
||||
* resulting in SW timeout. Hence, if a receive message is
|
||||
* followed by any other message, an error is returned
|
||||
* indicating that this sequence is not supported.
|
||||
*/
|
||||
for (count = 0; (count < num - 1 && hold_quirk); count++) {
|
||||
if (msgs[count].flags & I2C_M_RD) {
|
||||
dev_warn(adap->dev.parent,
|
||||
"Can't do repeated start after a receive message\n");
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
id->bus_hold_flag = 1;
|
||||
reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
reg |= CDNS_I2C_CR_HOLD;
|
||||
cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);
|
||||
} else {
|
||||
id->bus_hold_flag = 0;
|
||||
}
|
||||
|
||||
/* Process the msg one by one */
|
||||
for (count = 0; count < num; count++, msgs++) {
|
||||
if (count == (num - 1))
|
||||
id->bus_hold_flag = 0;
|
||||
|
||||
ret = cdns_i2c_process_msg(id, msgs, adap);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Report the other error interrupts to application */
|
||||
if (id->err_status) {
|
||||
cdns_i2c_master_reset(adap);
|
||||
|
||||
if (id->err_status & CDNS_I2C_IXR_NACK) {
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = num;
|
||||
|
||||
out:
|
||||
|
||||
ret = cdns_i2c_master_common_xfer(adap, msgs, num);
|
||||
if (!ret)
|
||||
ret = num;
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
out:
|
||||
/* Switch i2c mode to slave */
|
||||
if (change_role)
|
||||
cdns_i2c_set_mode(CDNS_I2C_MODE_SLAVE, id);
|
||||
@ -926,6 +1133,41 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_master_xfer_atomic - The i2c transfer function in atomic mode
|
||||
* @adap: pointer to the i2c adapter driver instance
|
||||
* @msgs: pointer to the i2c message structure
|
||||
* @num: the number of messages to transfer
|
||||
*
|
||||
* Return: number of msgs processed on success, negative error otherwise
|
||||
*/
|
||||
static int cdns_i2c_master_xfer_atomic(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
int ret;
|
||||
struct cdns_i2c *id = adap->algo_data;
|
||||
|
||||
ret = cdns_i2c_runtime_resume(id->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) {
|
||||
dev_warn(id->adap.dev.parent,
|
||||
"Atomic xfer not supported for version 1.0\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
id->atomic = true;
|
||||
ret = cdns_i2c_master_common_xfer(adap, msgs, num);
|
||||
if (!ret)
|
||||
ret = num;
|
||||
|
||||
id->atomic = false;
|
||||
cdns_i2c_runtime_suspend(id->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_func - Returns the supported features of the I2C driver
|
||||
* @adap: pointer to the i2c adapter structure
|
||||
@ -990,6 +1232,7 @@ static int cdns_unreg_slave(struct i2c_client *slave)
|
||||
|
||||
static const struct i2c_algorithm cdns_i2c_algo = {
|
||||
.master_xfer = cdns_i2c_master_xfer,
|
||||
.master_xfer_atomic = cdns_i2c_master_xfer_atomic,
|
||||
.functionality = cdns_i2c_func,
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
.reg_slave = cdns_reg_slave,
|
||||
@ -1158,23 +1401,6 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_runtime_suspend - Runtime suspend method for the driver
|
||||
* @dev: Address of the platform_device structure
|
||||
*
|
||||
* Put the driver into low power mode.
|
||||
*
|
||||
* Return: 0 always
|
||||
*/
|
||||
static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(xi2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused cdns_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
|
||||
@ -1187,49 +1413,6 @@ static int __maybe_unused cdns_i2c_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_init - Controller initialisation
|
||||
* @id: Device private data structure
|
||||
*
|
||||
* Initialise the i2c controller.
|
||||
*
|
||||
*/
|
||||
static void cdns_i2c_init(struct cdns_i2c *id)
|
||||
{
|
||||
cdns_i2c_writereg(id->ctrl_reg, CDNS_I2C_CR_OFFSET);
|
||||
/*
|
||||
* Cadence I2C controller has a bug wherein it generates
|
||||
* invalid read transaction after HW timeout in master receiver mode.
|
||||
* HW timeout is not used by this driver and the interrupt is disabled.
|
||||
* But the feature itself cannot be disabled. Hence maximum value
|
||||
* is written to this register to reduce the chances of error.
|
||||
*/
|
||||
cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_runtime_resume - Runtime resume
|
||||
* @dev: Address of the platform_device structure
|
||||
*
|
||||
* Runtime resume callback.
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(xi2c->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
cdns_i2c_init(xi2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused cdns_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct cdns_i2c *xi2c = dev_get_drvdata(dev);
|
||||
@ -1469,7 +1652,7 @@ static struct platform_driver cdns_i2c_drv = {
|
||||
.pm = &cdns_i2c_dev_pm_ops,
|
||||
},
|
||||
.probe = cdns_i2c_probe,
|
||||
.remove_new = cdns_i2c_remove,
|
||||
.remove = cdns_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(cdns_i2c_drv);
|
||||
|
@ -264,7 +264,7 @@ MODULE_DEVICE_TABLE(of, i2c_cbus_dt_ids);
|
||||
|
||||
static struct platform_driver cbus_i2c_driver = {
|
||||
.probe = cbus_i2c_probe,
|
||||
.remove_new = cbus_i2c_remove,
|
||||
.remove = cbus_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c-cbus-gpio",
|
||||
.of_match_table = of_match_ptr(i2c_cbus_dt_ids),
|
||||
|
@ -546,7 +546,7 @@ MODULE_DEVICE_TABLE(platform, cht_wc_i2c_adap_id_table);
|
||||
|
||||
static struct platform_driver cht_wc_i2c_adap_driver = {
|
||||
.probe = cht_wc_i2c_adap_i2c_probe,
|
||||
.remove_new = cht_wc_i2c_adap_i2c_remove,
|
||||
.remove = cht_wc_i2c_adap_i2c_remove,
|
||||
.driver = {
|
||||
.name = "cht_wcove_ext_chgr",
|
||||
},
|
||||
|
@ -701,7 +701,7 @@ MODULE_DEVICE_TABLE(of, cpm_i2c_match);
|
||||
|
||||
static struct platform_driver cpm_i2c_driver = {
|
||||
.probe = cpm_i2c_probe,
|
||||
.remove_new = cpm_i2c_remove,
|
||||
.remove = cpm_i2c_remove,
|
||||
.driver = {
|
||||
.name = "fsl-i2c-cpm",
|
||||
.of_match_table = cpm_i2c_match,
|
||||
|
@ -304,7 +304,7 @@ MODULE_DEVICE_TABLE(acpi, cros_ec_i2c_tunnel_acpi_id);
|
||||
|
||||
static struct platform_driver ec_i2c_tunnel_driver = {
|
||||
.probe = ec_i2c_probe,
|
||||
.remove_new = ec_i2c_remove,
|
||||
.remove = ec_i2c_remove,
|
||||
.driver = {
|
||||
.name = "cros-ec-i2c-tunnel",
|
||||
.acpi_match_table = ACPI_PTR(cros_ec_i2c_tunnel_acpi_id),
|
||||
|
@ -935,7 +935,7 @@ MODULE_DEVICE_TABLE(platform, davinci_i2c_driver_ids);
|
||||
|
||||
static struct platform_driver davinci_i2c_driver = {
|
||||
.probe = davinci_i2c_probe,
|
||||
.remove_new = davinci_i2c_remove,
|
||||
.remove = davinci_i2c_remove,
|
||||
.id_table = davinci_i2c_driver_ids,
|
||||
.driver = {
|
||||
.name = "i2c_davinci",
|
||||
|
@ -155,7 +155,7 @@ static void psp_release_i2c_bus_deferred(struct work_struct *work)
|
||||
|
||||
/*
|
||||
* If there is any pending transaction, cannot release the bus here.
|
||||
* psp_release_i2c_bus will take care of this later.
|
||||
* psp_release_i2c_bus() will take care of this later.
|
||||
*/
|
||||
if (psp_i2c_access_count)
|
||||
goto cleanup;
|
||||
@ -210,12 +210,12 @@ static void psp_release_i2c_bus(void)
|
||||
{
|
||||
mutex_lock(&psp_i2c_access_mutex);
|
||||
|
||||
/* Return early if mailbox was malfunctional */
|
||||
/* Return early if mailbox was malfunctioned */
|
||||
if (psp_i2c_mbox_fail)
|
||||
goto cleanup;
|
||||
|
||||
/*
|
||||
* If we are last owner of PSP semaphore, need to release aribtration
|
||||
* If we are last owner of PSP semaphore, need to release arbitration
|
||||
* via mailbox.
|
||||
*/
|
||||
psp_i2c_access_count--;
|
||||
@ -235,9 +235,9 @@ cleanup:
|
||||
|
||||
/*
|
||||
* Locking methods are based on the default implementation from
|
||||
* drivers/i2c/i2c-core-base.c, but with psp acquire and release operations
|
||||
* 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
|
||||
* 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,
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
static char *abort_sources[] = {
|
||||
static const char *const abort_sources[] = {
|
||||
[ABRT_7B_ADDR_NOACK] =
|
||||
"slave address not acknowledged (7bit mode)",
|
||||
[ABRT_10ADDR1_NOACK] =
|
||||
@ -127,6 +127,8 @@ static int dw_reg_write_word(void *context, unsigned int reg, unsigned int val)
|
||||
* Autodetects needed register access mode and creates the regmap with
|
||||
* corresponding read/write callbacks. This must be called before doing any
|
||||
* other register access.
|
||||
*
|
||||
* Return: 0 on success, or negative errno otherwise.
|
||||
*/
|
||||
int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
|
||||
{
|
||||
@ -174,7 +176,7 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
|
||||
/*
|
||||
* Note we'll check the return value of the regmap IO accessors only
|
||||
* at the probe stage. The rest of the code won't do this because
|
||||
* basically we have MMIO-based regmap so non of the read/write methods
|
||||
* basically we have MMIO-based regmap, so none of the read/write methods
|
||||
* can fail.
|
||||
*/
|
||||
dev->map = devm_regmap_init(dev->dev, NULL, dev, &map_cfg);
|
||||
@ -336,7 +338,7 @@ static u32 i2c_dw_acpi_round_bus_speed(struct device *device)
|
||||
|
||||
acpi_speed = i2c_acpi_find_bus_speed(device);
|
||||
/*
|
||||
* Some DSTDs use a non standard speed, round down to the lowest
|
||||
* Some DSDTs use a non standard speed, round down to the lowest
|
||||
* standard speed.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(supported_speeds); i++) {
|
||||
@ -407,47 +409,26 @@ static u32 i2c_dw_read_scl_reg(struct dw_i2c_dev *dev, u32 reg)
|
||||
}
|
||||
|
||||
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
u32 tSYMBOL, u32 tf, int offset)
|
||||
{
|
||||
if (!ic_clk)
|
||||
return i2c_dw_read_scl_reg(dev, reg);
|
||||
|
||||
/*
|
||||
* DesignWare I2C core doesn't seem to have solid strategy to meet
|
||||
* the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
|
||||
* will result in violation of the tHD;STA spec.
|
||||
* Conditional expression:
|
||||
*
|
||||
* IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
|
||||
*
|
||||
* This is just experimental rule; the tHD;STA period turned
|
||||
* out to be proportinal to (_HCNT + 3). With this setting,
|
||||
* we could meet both tHIGH and tHD;STA timing specs.
|
||||
*
|
||||
* If unsure, you'd better to take this alternative.
|
||||
*
|
||||
* The reason why we need to take into account "tf" here,
|
||||
* is the same as described in i2c_dw_scl_lcnt().
|
||||
*/
|
||||
if (cond)
|
||||
/*
|
||||
* Conditional expression:
|
||||
*
|
||||
* IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
|
||||
*
|
||||
* This is based on the DW manuals, and represents an ideal
|
||||
* configuration. The resulting I2C bus speed will be
|
||||
* faster than any of the others.
|
||||
*
|
||||
* If your hardware is free from tHD;STA issue, try this one.
|
||||
*/
|
||||
return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * tSYMBOL, MICRO) -
|
||||
8 + offset;
|
||||
else
|
||||
/*
|
||||
* Conditional expression:
|
||||
*
|
||||
* IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
|
||||
*
|
||||
* This is just experimental rule; the tHD;STA period turned
|
||||
* out to be proportinal to (_HCNT + 3). With this setting,
|
||||
* we could meet both tHIGH and tHD;STA timing specs.
|
||||
*
|
||||
* If unsure, you'd better to take this alternative.
|
||||
*
|
||||
* The reason why we need to take into account "tf" here,
|
||||
* is the same as described in i2c_dw_scl_lcnt().
|
||||
*/
|
||||
return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tSYMBOL + tf), MICRO) -
|
||||
3 + offset;
|
||||
return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tSYMBOL + tf), MICRO) - 3 + offset;
|
||||
}
|
||||
|
||||
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
@ -467,8 +448,7 @@ u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
* account the fall time of SCL signal (tf). Default tf value
|
||||
* should be 0.3 us, for safety.
|
||||
*/
|
||||
return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tLOW + tf), MICRO) -
|
||||
1 + offset;
|
||||
return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tLOW + tf), MICRO) - 1 + offset;
|
||||
}
|
||||
|
||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
|
||||
@ -571,7 +551,7 @@ void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||
|
||||
/*
|
||||
* Wait 10 times the signaling period of the highest I2C
|
||||
* transfer supported by the driver (for 400KHz this is
|
||||
* transfer supported by the driver (for 400kHz this is
|
||||
* 25us) as described in the DesignWare I2C databook.
|
||||
*/
|
||||
usleep_range(25, 250);
|
||||
@ -678,10 +658,10 @@ int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
||||
|
||||
if (abort_source & DW_IC_TX_ARB_LOST)
|
||||
return -EAGAIN;
|
||||
else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
|
||||
if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
|
||||
return -EINVAL; /* wrong msgs[] data */
|
||||
else
|
||||
return -EIO;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev)
|
||||
|
@ -143,10 +143,10 @@
|
||||
#define DW_IC_SLAVE 1
|
||||
|
||||
/*
|
||||
* Hardware abort codes from the DW_IC_TX_ABRT_SOURCE register
|
||||
* Hardware abort codes from the DW_IC_TX_ABRT_SOURCE register.
|
||||
*
|
||||
* Only expected abort codes are listed here
|
||||
* refer to the datasheet for the full list
|
||||
* Only expected abort codes are listed here,
|
||||
* refer to the datasheet for the full list.
|
||||
*/
|
||||
#define ABRT_7B_ADDR_NOACK 0
|
||||
#define ABRT_10ADDR1_NOACK 1
|
||||
@ -201,7 +201,7 @@ struct reset_control;
|
||||
* @rst: optional reset for the controller
|
||||
* @slave: represent an I2C slave device
|
||||
* @get_clk_rate_khz: callback to retrieve IP specific bus speed
|
||||
* @cmd_err: run time hadware error code
|
||||
* @cmd_err: run time hardware error code
|
||||
* @msgs: points to an array of messages currently being transferred
|
||||
* @msgs_num: the number of elements in msgs
|
||||
* @msg_write_idx: the element index of the current tx message in the msgs array
|
||||
@ -237,7 +237,7 @@ struct reset_control;
|
||||
* @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 SoC's PUNIT
|
||||
* @init: function to initialize the I2C hardware
|
||||
* @set_sda_hold_time: callback to retrieve IP specific SDA hold timing
|
||||
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
|
||||
@ -329,7 +329,7 @@ struct i2c_dw_semaphore_callbacks {
|
||||
|
||||
int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
|
||||
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||
u32 tSYMBOL, u32 tf, int offset);
|
||||
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tLOW, u32 tf, int offset);
|
||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
|
||||
|
@ -71,7 +71,6 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
ic_clk,
|
||||
4000, /* tHD;STA = tHIGH = 4.0 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
dev->ss_lcnt =
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
@ -105,7 +104,6 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
ic_clk,
|
||||
260, /* tHIGH = 260 ns */
|
||||
sda_falling_time,
|
||||
0, /* DW default */
|
||||
0); /* No offset */
|
||||
dev->fs_lcnt =
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
@ -129,7 +127,6 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
ic_clk,
|
||||
600, /* tHD;STA = tHIGH = 0.6 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
dev->fs_lcnt =
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
@ -161,7 +158,6 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
ic_clk,
|
||||
160, /* tHIGH = 160 ns */
|
||||
sda_falling_time,
|
||||
0, /* DW default */
|
||||
0); /* No offset */
|
||||
dev->hs_lcnt =
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
@ -184,12 +180,14 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_dw_init_master() - Initialize the designware I2C master hardware
|
||||
* i2c_dw_init_master() - Initialize the DesignWare I2C master hardware
|
||||
* @dev: device private data
|
||||
*
|
||||
* This functions configures and enables the I2C master.
|
||||
* This function is called during I2C init function, and in case of timeout at
|
||||
* run time.
|
||||
*
|
||||
* Return: 0 on success, or negative errno otherwise.
|
||||
*/
|
||||
static int i2c_dw_init_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
@ -357,7 +355,7 @@ static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
/*
|
||||
* Initiate the i2c read/write transaction of buffer length,
|
||||
* and poll for bus busy status. For the last message transfer,
|
||||
* update the command with stopbit enable.
|
||||
* update the command with stop bit enable.
|
||||
*/
|
||||
for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) {
|
||||
if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1)
|
||||
@ -402,7 +400,7 @@ static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
|
||||
/*
|
||||
* Initiate (and continue) low level master read/write transaction.
|
||||
* This function is only called from i2c_dw_isr, and pumping i2c_msg
|
||||
* This function is only called from i2c_dw_isr(), and pumping i2c_msg
|
||||
* messages into the tx buffer. Even if the size of i2c_msg data is
|
||||
* longer than the size of the tx buffer, it handles everything.
|
||||
*/
|
||||
@ -440,7 +438,8 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
||||
buf = msgs[dev->msg_write_idx].buf;
|
||||
buf_len = msgs[dev->msg_write_idx].len;
|
||||
|
||||
/* If both IC_EMPTYFIFO_HOLD_MASTER_EN and
|
||||
/*
|
||||
* If both IC_EMPTYFIFO_HOLD_MASTER_EN and
|
||||
* IC_RESTART_EN are set, we must manually
|
||||
* set restart bit between messages.
|
||||
*/
|
||||
@ -971,7 +970,7 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
|
||||
rinfo->unprepare_recovery = i2c_dw_unprepare_recovery;
|
||||
adap->bus_recovery_info = rinfo;
|
||||
|
||||
dev_info(dev->dev, "running with gpio recovery mode! scl%s",
|
||||
dev_info(dev->dev, "running with GPIO recovery mode! scl%s",
|
||||
rinfo->sda_gpiod ? ",sda" : "");
|
||||
|
||||
return 0;
|
||||
|
@ -51,7 +51,7 @@ struct dw_scl_sda_cfg {
|
||||
u16 fs_hcnt;
|
||||
u16 ss_lcnt;
|
||||
u16 fs_lcnt;
|
||||
u32 sda_hold;
|
||||
u32 sda_hold_time;
|
||||
};
|
||||
|
||||
struct dw_pci_controller {
|
||||
@ -76,7 +76,7 @@ static struct dw_scl_sda_cfg byt_config = {
|
||||
.fs_hcnt = 0x55,
|
||||
.ss_lcnt = 0x200,
|
||||
.fs_lcnt = 0x99,
|
||||
.sda_hold = 0x6,
|
||||
.sda_hold_time = 0x6,
|
||||
};
|
||||
|
||||
/* Haswell HCNT/LCNT/SDA hold time */
|
||||
@ -85,14 +85,14 @@ static struct dw_scl_sda_cfg hsw_config = {
|
||||
.fs_hcnt = 0x48,
|
||||
.ss_lcnt = 0x01fb,
|
||||
.fs_lcnt = 0xa0,
|
||||
.sda_hold = 0x9,
|
||||
.sda_hold_time = 0x9,
|
||||
};
|
||||
|
||||
/* NAVI-AMD HCNT/LCNT/SDA hold time */
|
||||
static struct dw_scl_sda_cfg navi_amd_config = {
|
||||
.ss_hcnt = 0x1ae,
|
||||
.ss_lcnt = 0x23a,
|
||||
.sda_hold = 0x9,
|
||||
.sda_hold_time = 0x9,
|
||||
};
|
||||
|
||||
static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
@ -207,6 +207,7 @@ static const struct software_node dgpu_node = {
|
||||
static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct device *device = &pdev->dev;
|
||||
struct dw_i2c_dev *dev;
|
||||
struct i2c_adapter *adap;
|
||||
int r;
|
||||
@ -214,25 +215,22 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
struct dw_scl_sda_cfg *cfg;
|
||||
|
||||
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers))
|
||||
return dev_err_probe(&pdev->dev, -EINVAL,
|
||||
"Invalid driver data %ld\n",
|
||||
return dev_err_probe(device, -EINVAL, "Invalid driver data %ld\n",
|
||||
id->driver_data);
|
||||
|
||||
controller = &dw_pci_controllers[id->driver_data];
|
||||
|
||||
r = pcim_enable_device(pdev);
|
||||
if (r)
|
||||
return dev_err_probe(&pdev->dev, r,
|
||||
"Failed to enable I2C PCI device\n");
|
||||
return dev_err_probe(device, r, "Failed to enable I2C PCI device\n");
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
r = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||
if (r)
|
||||
return dev_err_probe(&pdev->dev, r,
|
||||
"I/O memory remapping failed\n");
|
||||
return dev_err_probe(device, r, "I/O memory remapping failed\n");
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||
dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -242,7 +240,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
|
||||
dev->get_clk_rate_khz = controller->get_clk_rate_khz;
|
||||
dev->base = pcim_iomap_table(pdev)[0];
|
||||
dev->dev = &pdev->dev;
|
||||
dev->dev = device;
|
||||
dev->irq = pci_irq_vector(pdev, 0);
|
||||
dev->flags |= controller->flags;
|
||||
|
||||
@ -266,7 +264,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
dev->fs_hcnt = cfg->fs_hcnt;
|
||||
dev->ss_lcnt = cfg->ss_lcnt;
|
||||
dev->fs_lcnt = cfg->fs_lcnt;
|
||||
dev->sda_hold_time = cfg->sda_hold;
|
||||
dev->sda_hold_time = cfg->sda_hold_time;
|
||||
}
|
||||
|
||||
adap = &dev->adapter;
|
||||
@ -281,14 +279,14 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
|
||||
dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, &dgpu_node);
|
||||
if (IS_ERR(dev->slave))
|
||||
return dev_err_probe(dev->dev, PTR_ERR(dev->slave),
|
||||
return dev_err_probe(device, PTR_ERR(dev->slave),
|
||||
"register UCSI failed\n");
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(device, 1000);
|
||||
pm_runtime_use_autosuspend(device);
|
||||
pm_runtime_put_autosuspend(device);
|
||||
pm_runtime_allow(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -296,11 +294,12 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
static void i2c_dw_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = pci_get_drvdata(pdev);
|
||||
struct device *device = &pdev->dev;
|
||||
|
||||
i2c_dw_disable(dev);
|
||||
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_forbid(device);
|
||||
pm_runtime_get_noresume(device);
|
||||
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ static int bt1_i2c_write(void *context, unsigned int reg, unsigned int val)
|
||||
return ret;
|
||||
|
||||
return regmap_write(dev->sysmap, BT1_I2C_CTL,
|
||||
BT1_I2C_CTL_GO | BT1_I2C_CTL_WR | (reg & BT1_I2C_CTL_ADDR_MASK));
|
||||
BT1_I2C_CTL_GO | BT1_I2C_CTL_WR | (reg & BT1_I2C_CTL_ADDR_MASK));
|
||||
}
|
||||
|
||||
static const struct regmap_config bt1_i2c_cfg = {
|
||||
@ -205,6 +205,7 @@ static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev)
|
||||
|
||||
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *device = &pdev->dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct dw_i2c_dev *dev;
|
||||
int irq, ret;
|
||||
@ -213,15 +214,15 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL);
|
||||
dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->flags = (uintptr_t)device_get_match_data(&pdev->dev);
|
||||
if (device_property_present(&pdev->dev, "wx,i2c-snps-model"))
|
||||
dev->flags = (uintptr_t)device_get_match_data(device);
|
||||
if (device_property_present(device, "wx,i2c-snps-model"))
|
||||
dev->flags = MODEL_WANGXUN_SP | ACCESS_POLLING;
|
||||
|
||||
dev->dev = &pdev->dev;
|
||||
dev->dev = device;
|
||||
dev->irq = irq;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
@ -229,7 +230,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||
dev->rst = devm_reset_control_get_optional_exclusive(device, NULL);
|
||||
if (IS_ERR(dev->rst))
|
||||
return PTR_ERR(dev->rst);
|
||||
|
||||
@ -246,13 +247,13 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
i2c_dw_configure(dev);
|
||||
|
||||
/* Optional interface clock */
|
||||
dev->pclk = devm_clk_get_optional(&pdev->dev, "pclk");
|
||||
dev->pclk = devm_clk_get_optional(device, "pclk");
|
||||
if (IS_ERR(dev->pclk)) {
|
||||
ret = PTR_ERR(dev->pclk);
|
||||
goto exit_reset;
|
||||
}
|
||||
|
||||
dev->clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
dev->clk = devm_clk_get_optional(device, NULL);
|
||||
if (IS_ERR(dev->clk)) {
|
||||
ret = PTR_ERR(dev->clk);
|
||||
goto exit_reset;
|
||||
@ -277,31 +278,27 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
adap = &dev->adapter;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = dmi_check_system(dw_i2c_hwmon_class_dmi) ?
|
||||
I2C_CLASS_HWMON : I2C_CLASS_DEPRECATED;
|
||||
I2C_CLASS_HWMON : I2C_CLASS_DEPRECATED;
|
||||
adap->nr = -1;
|
||||
|
||||
if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
|
||||
dev_pm_set_driver_flags(&pdev->dev,
|
||||
DPM_FLAG_SMART_PREPARE);
|
||||
} else {
|
||||
dev_pm_set_driver_flags(&pdev->dev,
|
||||
DPM_FLAG_SMART_PREPARE |
|
||||
DPM_FLAG_SMART_SUSPEND);
|
||||
}
|
||||
if (dev->flags & ACCESS_NO_IRQ_SUSPEND)
|
||||
dev_pm_set_driver_flags(device, DPM_FLAG_SMART_PREPARE);
|
||||
else
|
||||
dev_pm_set_driver_flags(device, DPM_FLAG_SMART_PREPARE | DPM_FLAG_SMART_SUSPEND);
|
||||
|
||||
device_enable_async_suspend(&pdev->dev);
|
||||
device_enable_async_suspend(device);
|
||||
|
||||
/* The code below assumes runtime PM to be disabled. */
|
||||
WARN_ON(pm_runtime_enabled(&pdev->dev));
|
||||
WARN_ON(pm_runtime_enabled(device));
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(device, 1000);
|
||||
pm_runtime_use_autosuspend(device);
|
||||
pm_runtime_set_active(device);
|
||||
|
||||
if (dev->shared_with_punit)
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_get_noresume(device);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_enable(device);
|
||||
|
||||
ret = i2c_dw_probe(dev);
|
||||
if (ret)
|
||||
@ -319,15 +316,16 @@ exit_reset:
|
||||
static void dw_i2c_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||
struct device *device = &pdev->dev;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_get_sync(device);
|
||||
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
|
||||
i2c_dw_disable(dev);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(device);
|
||||
pm_runtime_put_sync(device);
|
||||
dw_i2c_plat_pm_cleanup(dev);
|
||||
|
||||
i2c_dw_remove_lock_support(dev);
|
||||
@ -354,6 +352,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
||||
{ "HISI02A1", 0 },
|
||||
{ "HISI02A2", 0 },
|
||||
{ "HISI02A3", 0 },
|
||||
{ "HJMC3001", 0 },
|
||||
{ "HYGO0010", ACCESS_INTR_MASK },
|
||||
{ "INT33C2", 0 },
|
||||
{ "INT33C3", 0 },
|
||||
@ -372,7 +371,7 @@ MODULE_DEVICE_TABLE(platform, dw_i2c_platform_ids);
|
||||
|
||||
static struct platform_driver dw_i2c_driver = {
|
||||
.probe = dw_i2c_plat_probe,
|
||||
.remove_new = dw_i2c_plat_remove,
|
||||
.remove = dw_i2c_plat_remove,
|
||||
.driver = {
|
||||
.name = "i2c_designware",
|
||||
.of_match_table = dw_i2c_of_match,
|
||||
|
@ -32,12 +32,14 @@ static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_dw_init_slave() - Initialize the designware i2c slave hardware
|
||||
* i2c_dw_init_slave() - Initialize the DesignWare i2c slave hardware
|
||||
* @dev: device private data
|
||||
*
|
||||
* This function configures and enables the I2C in slave mode.
|
||||
* This function is called during I2C init function, and in case of timeout at
|
||||
* run time.
|
||||
*
|
||||
* Return: 0 on success, or negative errno otherwise.
|
||||
*/
|
||||
static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
|
||||
{
|
||||
@ -264,7 +266,7 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
|
||||
ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr_slave,
|
||||
IRQF_SHARED, dev_name(dev->dev), dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failure requesting irq %i: %d\n",
|
||||
dev_err(dev->dev, "failure requesting IRQ %i: %d\n",
|
||||
dev->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -363,7 +363,7 @@ MODULE_DEVICE_TABLE(of, dc_i2c_match);
|
||||
|
||||
static struct platform_driver dc_i2c_driver = {
|
||||
.probe = dc_i2c_probe,
|
||||
.remove_new = dc_i2c_remove,
|
||||
.remove = dc_i2c_remove,
|
||||
.driver = {
|
||||
.name = "digicolor-i2c",
|
||||
.of_match_table = dc_i2c_match,
|
||||
|
@ -245,7 +245,7 @@ static void dln2_i2c_remove(struct platform_device *pdev)
|
||||
static struct platform_driver dln2_i2c_driver = {
|
||||
.driver.name = "dln2-i2c",
|
||||
.probe = dln2_i2c_probe,
|
||||
.remove_new = dln2_i2c_remove,
|
||||
.remove = dln2_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(dln2_i2c_driver);
|
||||
|
@ -425,7 +425,7 @@ static const struct of_device_id em_i2c_ids[] = {
|
||||
|
||||
static struct platform_driver em_i2c_driver = {
|
||||
.probe = em_i2c_probe,
|
||||
.remove_new = em_i2c_remove,
|
||||
.remove = em_i2c_remove,
|
||||
.driver = {
|
||||
.name = "em-i2c",
|
||||
.of_match_table = em_i2c_ids,
|
||||
|
@ -1009,7 +1009,7 @@ static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
|
||||
|
||||
static struct platform_driver exynos5_i2c_driver = {
|
||||
.probe = exynos5_i2c_probe,
|
||||
.remove_new = exynos5_i2c_remove,
|
||||
.remove = exynos5_i2c_remove,
|
||||
.driver = {
|
||||
.name = "exynos5-hsi2c",
|
||||
.pm = pm_sleep_ptr(&exynos5_i2c_dev_pm_ops),
|
||||
|
@ -481,7 +481,7 @@ static struct platform_driver i2c_gpio_driver = {
|
||||
.acpi_match_table = i2c_gpio_acpi_match,
|
||||
},
|
||||
.probe = i2c_gpio_probe,
|
||||
.remove_new = i2c_gpio_remove,
|
||||
.remove = i2c_gpio_remove,
|
||||
};
|
||||
|
||||
static int __init i2c_gpio_init(void)
|
||||
|
@ -595,7 +595,7 @@ MODULE_DEVICE_TABLE(of, gxp_i2c_of_match);
|
||||
|
||||
static struct platform_driver gxp_i2c_driver = {
|
||||
.probe = gxp_i2c_probe,
|
||||
.remove_new = gxp_i2c_remove,
|
||||
.remove = gxp_i2c_remove,
|
||||
.driver = {
|
||||
.name = "gxp-i2c",
|
||||
.of_match_table = gxp_i2c_of_match,
|
||||
|
@ -454,7 +454,7 @@ static struct platform_driver highlander_i2c_driver = {
|
||||
},
|
||||
|
||||
.probe = highlander_i2c_probe,
|
||||
.remove_new = highlander_i2c_remove,
|
||||
.remove = highlander_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(highlander_i2c_driver);
|
||||
|
@ -508,7 +508,7 @@ MODULE_DEVICE_TABLE(of, hix5hd2_i2c_match);
|
||||
|
||||
static struct platform_driver hix5hd2_i2c_driver = {
|
||||
.probe = hix5hd2_i2c_probe,
|
||||
.remove_new = hix5hd2_i2c_remove,
|
||||
.remove = hix5hd2_i2c_remove,
|
||||
.driver = {
|
||||
.name = "hix5hd2-i2c",
|
||||
.pm = pm_ptr(&hix5hd2_i2c_pm_ops),
|
||||
|
@ -81,6 +81,8 @@
|
||||
* Meteor Lake PCH-S (PCH) 0x7f23 32 hard yes yes yes
|
||||
* Birch Stream (SOC) 0x5796 32 hard yes yes yes
|
||||
* Arrow Lake-H (SOC) 0x7722 32 hard yes yes yes
|
||||
* Panther Lake-H (SOC) 0xe322 32 hard yes yes yes
|
||||
* Panther Lake-P (SOC) 0xe422 32 hard yes yes yes
|
||||
*
|
||||
* Features supported by this driver:
|
||||
* Software PEC no
|
||||
@ -261,6 +263,8 @@
|
||||
#define PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS 0xa323
|
||||
#define PCI_DEVICE_ID_INTEL_COMETLAKE_V_SMBUS 0xa3a3
|
||||
#define PCI_DEVICE_ID_INTEL_METEOR_LAKE_SOC_S_SMBUS 0xae22
|
||||
#define PCI_DEVICE_ID_INTEL_PANTHER_LAKE_H_SMBUS 0xe322
|
||||
#define PCI_DEVICE_ID_INTEL_PANTHER_LAKE_P_SMBUS 0xe422
|
||||
|
||||
struct i801_mux_config {
|
||||
char *gpio_chip;
|
||||
@ -1055,6 +1059,8 @@ static const struct pci_device_id i801_ids[] = {
|
||||
{ PCI_DEVICE_DATA(INTEL, METEOR_LAKE_PCH_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, BIRCH_STREAM_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, ARROW_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
|
@ -790,7 +790,7 @@ static struct platform_driver ibm_iic_driver = {
|
||||
.of_match_table = ibm_iic_match,
|
||||
},
|
||||
.probe = iic_probe,
|
||||
.remove_new = iic_remove,
|
||||
.remove = iic_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ibm_iic_driver);
|
||||
|
@ -1497,7 +1497,7 @@ static struct platform_driver img_scb_i2c_driver = {
|
||||
.pm = pm_ptr(&img_i2c_pm),
|
||||
},
|
||||
.probe = img_i2c_probe,
|
||||
.remove_new = img_i2c_remove,
|
||||
.remove = img_i2c_remove,
|
||||
};
|
||||
module_platform_driver(img_scb_i2c_driver);
|
||||
|
||||
|
@ -703,7 +703,7 @@ static const struct dev_pm_ops lpi2c_pm_ops = {
|
||||
|
||||
static struct platform_driver lpi2c_imx_driver = {
|
||||
.probe = lpi2c_imx_probe,
|
||||
.remove_new = lpi2c_imx_remove,
|
||||
.remove = lpi2c_imx_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = lpi2c_imx_of_match,
|
||||
|
@ -17,7 +17,7 @@
|
||||
* Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
|
||||
*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
* Copyright 2020 NXP
|
||||
* Copyright 2020, 2024 NXP
|
||||
*
|
||||
*/
|
||||
|
||||
@ -84,6 +84,7 @@
|
||||
|
||||
#define IMX_I2C_REGSHIFT 2
|
||||
#define VF610_I2C_REGSHIFT 0
|
||||
#define S32G_I2C_REGSHIFT 0
|
||||
|
||||
/* Bits of IMX I2C registers */
|
||||
#define I2SR_RXAK 0x01
|
||||
@ -165,9 +166,34 @@ static struct imx_i2c_clk_pair vf610_i2c_clk_div[] = {
|
||||
{ 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E },
|
||||
};
|
||||
|
||||
/* S32G2/S32G3 clock divider, register value pairs */
|
||||
static struct imx_i2c_clk_pair s32g2_i2c_clk_div[] = {
|
||||
{ 34, 0x00 }, { 36, 0x01 }, { 38, 0x02 }, { 40, 0x03 },
|
||||
{ 42, 0x04 }, { 44, 0x05 }, { 46, 0x06 }, { 48, 0x09 },
|
||||
{ 52, 0x0A }, { 54, 0x07 }, { 56, 0x0B }, { 60, 0x0C },
|
||||
{ 64, 0x0D }, { 68, 0x40 }, { 72, 0x0E }, { 76, 0x42 },
|
||||
{ 80, 0x12 }, { 84, 0x0F }, { 88, 0x13 }, { 96, 0x14 },
|
||||
{ 104, 0x15 }, { 108, 0x47 }, { 112, 0x19 }, { 120, 0x16 },
|
||||
{ 128, 0x1A }, { 136, 0x80 }, { 144, 0x17 }, { 152, 0x82 },
|
||||
{ 160, 0x1C }, { 168, 0x84 }, { 176, 0x1D }, { 192, 0x21 },
|
||||
{ 208, 0x1E }, { 216, 0x87 }, { 224, 0x22 }, { 240, 0x56 },
|
||||
{ 256, 0x1F }, { 288, 0x24 }, { 320, 0x25 }, { 336, 0x8F },
|
||||
{ 352, 0x93 }, { 356, 0x5D }, { 358, 0x98 }, { 384, 0x26 },
|
||||
{ 416, 0x56 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B },
|
||||
{ 576, 0x2C }, { 640, 0x2D }, { 704, 0x9D }, { 768, 0x2E },
|
||||
{ 832, 0x9D }, { 896, 0x32 }, { 960, 0x2F }, { 1024, 0x33 },
|
||||
{ 1152, 0x34 }, { 1280, 0x35 }, { 1536, 0x36 }, { 1792, 0x3A },
|
||||
{ 1920, 0x37 }, { 2048, 0x3B }, { 2304, 0x74 }, { 2560, 0x3D },
|
||||
{ 3072, 0x3E }, { 3584, 0x7A }, { 3840, 0x3F }, { 4096, 0x7B },
|
||||
{ 4608, 0x7C }, { 5120, 0x7D }, { 6144, 0x7E }, { 7168, 0xBA },
|
||||
{ 7680, 0x7F }, { 8192, 0xBB }, { 9216, 0xBC }, { 10240, 0xBD },
|
||||
{ 12288, 0xBE }, { 15360, 0xBF },
|
||||
};
|
||||
|
||||
enum imx_i2c_type {
|
||||
IMX1_I2C,
|
||||
IMX21_I2C,
|
||||
S32G_I2C,
|
||||
VF610_I2C,
|
||||
};
|
||||
|
||||
@ -197,6 +223,17 @@ struct imx_i2c_dma {
|
||||
enum dma_data_direction dma_data_dir;
|
||||
};
|
||||
|
||||
enum imx_i2c_state {
|
||||
IMX_I2C_STATE_DONE,
|
||||
IMX_I2C_STATE_FAILED,
|
||||
IMX_I2C_STATE_WRITE,
|
||||
IMX_I2C_STATE_DMA,
|
||||
IMX_I2C_STATE_READ,
|
||||
IMX_I2C_STATE_READ_CONTINUE,
|
||||
IMX_I2C_STATE_READ_BLOCK_DATA,
|
||||
IMX_I2C_STATE_READ_BLOCK_DATA_LEN,
|
||||
};
|
||||
|
||||
struct imx_i2c_struct {
|
||||
struct i2c_adapter adapter;
|
||||
struct clk *clk;
|
||||
@ -216,6 +253,14 @@ struct imx_i2c_struct {
|
||||
struct i2c_client *slave;
|
||||
enum i2c_slave_event last_slave_event;
|
||||
|
||||
struct i2c_msg *msg;
|
||||
unsigned int msg_buf_idx;
|
||||
int isr_result;
|
||||
bool is_lastmsg;
|
||||
enum imx_i2c_state state;
|
||||
|
||||
bool multi_master;
|
||||
|
||||
/* For checking slave events. */
|
||||
spinlock_t slave_lock;
|
||||
struct hrtimer slave_timer;
|
||||
@ -258,7 +303,15 @@ static struct imx_i2c_hwdata vf610_i2c_hwdata = {
|
||||
.ndivs = ARRAY_SIZE(vf610_i2c_clk_div),
|
||||
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C,
|
||||
.i2cr_ien_opcode = I2CR_IEN_OPCODE_0,
|
||||
};
|
||||
|
||||
static const struct imx_i2c_hwdata s32g2_i2c_hwdata = {
|
||||
.devtype = S32G_I2C,
|
||||
.regshift = S32G_I2C_REGSHIFT,
|
||||
.clk_div = s32g2_i2c_clk_div,
|
||||
.ndivs = ARRAY_SIZE(s32g2_i2c_clk_div),
|
||||
.i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C,
|
||||
.i2cr_ien_opcode = I2CR_IEN_OPCODE_0,
|
||||
};
|
||||
|
||||
static const struct platform_device_id imx_i2c_devtype[] = {
|
||||
@ -288,6 +341,7 @@ static const struct of_device_id i2c_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8mp-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,imx8mq-i2c", .data = &imx6_i2c_hwdata, },
|
||||
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
|
||||
{ .compatible = "nxp,s32g2-i2c", .data = &s32g2_i2c_hwdata, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
|
||||
@ -481,6 +535,9 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool a
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
unsigned int temp;
|
||||
|
||||
if (!i2c_imx->multi_master)
|
||||
return 0;
|
||||
|
||||
while (1) {
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
|
||||
|
||||
@ -540,8 +597,8 @@ static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* check for arbitration lost */
|
||||
if (i2c_imx->i2csr & I2SR_IAL) {
|
||||
/* In multi-master mode check for arbitration lost */
|
||||
if (i2c_imx->multi_master && (i2c_imx->i2csr & I2SR_IAL)) {
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> Arbitration lost\n", __func__);
|
||||
i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
|
||||
|
||||
@ -903,11 +960,156 @@ static int i2c_imx_unreg_slave(struct i2c_client *client)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int i2c_imx_isr_acked(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
i2c_imx->isr_result = 0;
|
||||
|
||||
if (imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR) & I2SR_RXAK) {
|
||||
i2c_imx->state = IMX_I2C_STATE_FAILED;
|
||||
i2c_imx->isr_result = -ENXIO;
|
||||
wake_up(&i2c_imx->queue);
|
||||
}
|
||||
|
||||
return i2c_imx->isr_result;
|
||||
}
|
||||
|
||||
static inline int i2c_imx_isr_write(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = i2c_imx_isr_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (i2c_imx->msg->len == i2c_imx->msg_buf_idx)
|
||||
return 0;
|
||||
|
||||
imx_i2c_write_reg(i2c_imx->msg->buf[i2c_imx->msg_buf_idx++], i2c_imx, IMX_I2C_I2DR);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int i2c_imx_isr_read(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
int result;
|
||||
unsigned int temp;
|
||||
|
||||
result = i2c_imx_isr_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* setup bus to read data */
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp &= ~I2CR_MTX;
|
||||
if (i2c_imx->msg->len - 1)
|
||||
temp &= ~I2CR_TXAK;
|
||||
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void i2c_imx_isr_read_continue(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
unsigned int temp;
|
||||
|
||||
if ((i2c_imx->msg->len - 1) == i2c_imx->msg_buf_idx) {
|
||||
if (i2c_imx->is_lastmsg) {
|
||||
/*
|
||||
* It must generate STOP before read I2DR to prevent
|
||||
* controller from generating another clock cycle
|
||||
*/
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
if (!(temp & I2CR_MSTA))
|
||||
i2c_imx->stopped = 1;
|
||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
} else {
|
||||
/*
|
||||
* For i2c master receiver repeat restart operation like:
|
||||
* read -> repeat MSTA -> read/write
|
||||
* The controller must set MTX before read the last byte in
|
||||
* the first read operation, otherwise the first read cost
|
||||
* one extra clock cycle.
|
||||
*/
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp |= I2CR_MTX;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
}
|
||||
} else if (i2c_imx->msg_buf_idx == (i2c_imx->msg->len - 2)) {
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp |= I2CR_TXAK;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
}
|
||||
|
||||
i2c_imx->msg->buf[i2c_imx->msg_buf_idx++] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
}
|
||||
|
||||
static inline void i2c_imx_isr_read_block_data_len(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
u8 len = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
|
||||
|
||||
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) {
|
||||
i2c_imx->isr_result = -EPROTO;
|
||||
i2c_imx->state = IMX_I2C_STATE_FAILED;
|
||||
wake_up(&i2c_imx->queue);
|
||||
}
|
||||
i2c_imx->msg->len += len;
|
||||
}
|
||||
|
||||
static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx, unsigned int status)
|
||||
{
|
||||
/* save status register */
|
||||
i2c_imx->i2csr = status;
|
||||
wake_up(&i2c_imx->queue);
|
||||
/*
|
||||
* This state machine handles I2C reception and transmission in non-DMA
|
||||
* mode. We must process all the data in the ISR to reduce the delay
|
||||
* between two consecutive messages. If the data is not processed in
|
||||
* the ISR, SMBus devices may timeout, leading to a bus error.
|
||||
*/
|
||||
switch (i2c_imx->state) {
|
||||
case IMX_I2C_STATE_DMA:
|
||||
i2c_imx->i2csr = status;
|
||||
wake_up(&i2c_imx->queue);
|
||||
break;
|
||||
|
||||
case IMX_I2C_STATE_READ:
|
||||
if (i2c_imx_isr_read(i2c_imx))
|
||||
break;
|
||||
i2c_imx->state = IMX_I2C_STATE_READ_CONTINUE;
|
||||
break;
|
||||
|
||||
case IMX_I2C_STATE_READ_CONTINUE:
|
||||
i2c_imx_isr_read_continue(i2c_imx);
|
||||
if (i2c_imx->msg_buf_idx == i2c_imx->msg->len) {
|
||||
i2c_imx->state = IMX_I2C_STATE_DONE;
|
||||
wake_up(&i2c_imx->queue);
|
||||
}
|
||||
break;
|
||||
|
||||
case IMX_I2C_STATE_READ_BLOCK_DATA:
|
||||
if (i2c_imx_isr_read(i2c_imx))
|
||||
break;
|
||||
i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA_LEN;
|
||||
break;
|
||||
|
||||
case IMX_I2C_STATE_READ_BLOCK_DATA_LEN:
|
||||
i2c_imx_isr_read_block_data_len(i2c_imx);
|
||||
i2c_imx->state = IMX_I2C_STATE_READ_CONTINUE;
|
||||
break;
|
||||
|
||||
case IMX_I2C_STATE_WRITE:
|
||||
if (i2c_imx_isr_write(i2c_imx))
|
||||
break;
|
||||
i2c_imx->state = IMX_I2C_STATE_DONE;
|
||||
wake_up(&i2c_imx->queue);
|
||||
break;
|
||||
|
||||
default:
|
||||
i2c_imx->i2csr = status;
|
||||
i2c_imx->state = IMX_I2C_STATE_FAILED;
|
||||
i2c_imx->isr_result = -EINVAL;
|
||||
wake_up(&i2c_imx->queue);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -954,6 +1156,8 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
||||
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||
struct device *dev = &i2c_imx->adapter.dev;
|
||||
|
||||
i2c_imx->state = IMX_I2C_STATE_DMA;
|
||||
|
||||
dma->chan_using = dma->chan_tx;
|
||||
dma->dma_transfer_dir = DMA_MEM_TO_DEV;
|
||||
dma->dma_data_dir = DMA_TO_DEVICE;
|
||||
@ -1006,6 +1210,42 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
||||
return i2c_imx_acked(i2c_imx);
|
||||
}
|
||||
|
||||
static int i2c_imx_prepare_read(struct imx_i2c_struct *i2c_imx,
|
||||
struct i2c_msg *msgs, bool use_dma)
|
||||
{
|
||||
int result;
|
||||
unsigned int temp = 0;
|
||||
|
||||
/* write slave address */
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, !use_dma);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
|
||||
|
||||
/* setup bus to read data */
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp &= ~I2CR_MTX;
|
||||
|
||||
/*
|
||||
* Reset the I2CR_TXAK flag initially for SMBus block read since the
|
||||
* length is unknown
|
||||
*/
|
||||
if (msgs->len - 1)
|
||||
temp &= ~I2CR_TXAK;
|
||||
if (use_dma)
|
||||
temp |= I2CR_DMAEN;
|
||||
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||
struct i2c_msg *msgs, bool is_lastmsg)
|
||||
{
|
||||
@ -1016,6 +1256,13 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||
struct device *dev = &i2c_imx->adapter.dev;
|
||||
|
||||
i2c_imx->state = IMX_I2C_STATE_DMA;
|
||||
|
||||
result = i2c_imx_prepare_read(i2c_imx, msgs, true);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
||||
|
||||
dma->chan_using = dma->chan_rx;
|
||||
dma->dma_transfer_dir = DMA_DEV_TO_MEM;
|
||||
@ -1092,8 +1339,8 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
bool atomic)
|
||||
static int i2c_imx_atomic_write(struct imx_i2c_struct *i2c_imx,
|
||||
struct i2c_msg *msgs)
|
||||
{
|
||||
int i, result;
|
||||
|
||||
@ -1102,7 +1349,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
|
||||
/* write slave address */
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, atomic);
|
||||
result = i2c_imx_trx_complete(i2c_imx, true);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
@ -1116,7 +1363,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
"<%s> write byte: B%d=0x%X\n",
|
||||
__func__, i, msgs->buf[i]);
|
||||
imx_i2c_write_reg(msgs->buf[i], i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, atomic);
|
||||
result = i2c_imx_trx_complete(i2c_imx, true);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
@ -1126,55 +1373,54 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
bool is_lastmsg, bool atomic)
|
||||
static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
||||
{
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
|
||||
__func__, i2c_8bit_addr_from_msg(msgs));
|
||||
|
||||
i2c_imx->state = IMX_I2C_STATE_WRITE;
|
||||
i2c_imx->msg = msgs;
|
||||
i2c_imx->msg_buf_idx = 0;
|
||||
|
||||
/*
|
||||
* By writing the device address we start the state machine in the ISR.
|
||||
* The ISR will report when it is done or when it fails.
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
wait_event_timeout(i2c_imx->queue,
|
||||
i2c_imx->state == IMX_I2C_STATE_DONE ||
|
||||
i2c_imx->state == IMX_I2C_STATE_FAILED,
|
||||
(msgs->len + 1) * HZ / 10);
|
||||
if (i2c_imx->state == IMX_I2C_STATE_FAILED) {
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> write failed with %d\n",
|
||||
__func__, i2c_imx->isr_result);
|
||||
return i2c_imx->isr_result;
|
||||
}
|
||||
if (i2c_imx->state != IMX_I2C_STATE_DONE) {
|
||||
dev_err(&i2c_imx->adapter.dev, "<%s> write timedout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_atomic_read(struct imx_i2c_struct *i2c_imx,
|
||||
struct i2c_msg *msgs, bool is_lastmsg)
|
||||
{
|
||||
int i, result;
|
||||
unsigned int temp;
|
||||
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
||||
int use_dma = i2c_imx->dma && msgs->flags & I2C_M_DMA_SAFE &&
|
||||
msgs->len >= DMA_THRESHOLD && !block_data;
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%s> write slave address: addr=0x%x\n",
|
||||
__func__, i2c_8bit_addr_from_msg(msgs));
|
||||
|
||||
/* write slave address */
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
result = i2c_imx_trx_complete(i2c_imx, atomic);
|
||||
result = i2c_imx_prepare_read(i2c_imx, msgs, false);
|
||||
if (result)
|
||||
return result;
|
||||
result = i2c_imx_acked(i2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
|
||||
|
||||
/* setup bus to read data */
|
||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||
temp &= ~I2CR_MTX;
|
||||
|
||||
/*
|
||||
* Reset the I2CR_TXAK flag initially for SMBus block read since the
|
||||
* length is unknown
|
||||
*/
|
||||
if ((msgs->len - 1) || block_data)
|
||||
temp &= ~I2CR_TXAK;
|
||||
if (use_dma)
|
||||
temp |= I2CR_DMAEN;
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
||||
|
||||
if (use_dma)
|
||||
return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
|
||||
|
||||
/* read data */
|
||||
for (i = 0; i < msgs->len; i++) {
|
||||
u8 len = 0;
|
||||
|
||||
result = i2c_imx_trx_complete(i2c_imx, atomic);
|
||||
result = i2c_imx_trx_complete(i2c_imx, true);
|
||||
if (result)
|
||||
return result;
|
||||
/*
|
||||
@ -1205,7 +1451,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||
if (!i2c_imx->stopped)
|
||||
i2c_imx_bus_busy(i2c_imx, 0, atomic);
|
||||
i2c_imx_bus_busy(i2c_imx, 0, true);
|
||||
} else {
|
||||
/*
|
||||
* For i2c master receiver repeat restart operation like:
|
||||
@ -1236,6 +1482,48 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
|
||||
bool is_lastmsg)
|
||||
{
|
||||
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
||||
|
||||
dev_dbg(&i2c_imx->adapter.dev,
|
||||
"<%s> write slave address: addr=0x%x\n",
|
||||
__func__, i2c_8bit_addr_from_msg(msgs));
|
||||
|
||||
i2c_imx->is_lastmsg = is_lastmsg;
|
||||
|
||||
if (block_data)
|
||||
i2c_imx->state = IMX_I2C_STATE_READ_BLOCK_DATA;
|
||||
else
|
||||
i2c_imx->state = IMX_I2C_STATE_READ;
|
||||
i2c_imx->msg = msgs;
|
||||
i2c_imx->msg_buf_idx = 0;
|
||||
|
||||
/*
|
||||
* By writing the device address we start the state machine in the ISR.
|
||||
* The ISR will report when it is done or when it fails.
|
||||
*/
|
||||
imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR);
|
||||
wait_event_timeout(i2c_imx->queue,
|
||||
i2c_imx->state == IMX_I2C_STATE_DONE ||
|
||||
i2c_imx->state == IMX_I2C_STATE_FAILED,
|
||||
(msgs->len + 1) * HZ / 10);
|
||||
if (i2c_imx->state == IMX_I2C_STATE_FAILED) {
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> read failed with %d\n",
|
||||
__func__, i2c_imx->isr_result);
|
||||
return i2c_imx->isr_result;
|
||||
}
|
||||
if (i2c_imx->state != IMX_I2C_STATE_DONE) {
|
||||
dev_err(&i2c_imx->adapter.dev, "<%s> read timedout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (!i2c_imx->stopped)
|
||||
return i2c_imx_bus_busy(i2c_imx, 0, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs, int num, bool atomic)
|
||||
{
|
||||
@ -1243,6 +1531,7 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
||||
int result;
|
||||
bool is_lastmsg = false;
|
||||
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
||||
int use_dma = 0;
|
||||
|
||||
/* Start I2C transfer */
|
||||
result = i2c_imx_start(i2c_imx, atomic);
|
||||
@ -1295,15 +1584,25 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
|
||||
(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
|
||||
(temp & I2SR_RXAK ? 1 : 0));
|
||||
#endif
|
||||
|
||||
use_dma = i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD &&
|
||||
msgs[i].flags & I2C_M_DMA_SAFE;
|
||||
if (msgs[i].flags & I2C_M_RD) {
|
||||
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg, atomic);
|
||||
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
||||
|
||||
if (atomic)
|
||||
result = i2c_imx_atomic_read(i2c_imx, &msgs[i], is_lastmsg);
|
||||
else if (use_dma && !block_data)
|
||||
result = i2c_imx_dma_read(i2c_imx, &msgs[i], is_lastmsg);
|
||||
else
|
||||
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
|
||||
} else {
|
||||
if (!atomic &&
|
||||
i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD &&
|
||||
msgs[i].flags & I2C_M_DMA_SAFE)
|
||||
if (atomic)
|
||||
result = i2c_imx_atomic_write(i2c_imx, &msgs[i]);
|
||||
else if (use_dma)
|
||||
result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
|
||||
else
|
||||
result = i2c_imx_write(i2c_imx, &msgs[i], atomic);
|
||||
result = i2c_imx_write(i2c_imx, &msgs[i]);
|
||||
}
|
||||
if (result)
|
||||
goto fail0;
|
||||
@ -1468,6 +1767,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||
goto rpm_disable;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use the single-master property for backward compatibility.
|
||||
* By default multi master mode is enabled.
|
||||
*/
|
||||
i2c_imx->multi_master = !of_property_read_bool(pdev->dev.of_node, "single-master");
|
||||
|
||||
/* Set up clock divider */
|
||||
i2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
@ -1576,7 +1881,7 @@ static const struct dev_pm_ops i2c_imx_pm_ops = {
|
||||
|
||||
static struct platform_driver i2c_imx_driver = {
|
||||
.probe = i2c_imx_probe,
|
||||
.remove_new = i2c_imx_remove,
|
||||
.remove = i2c_imx_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = pm_ptr(&i2c_imx_pm_ops),
|
||||
|
@ -524,7 +524,7 @@ MODULE_DEVICE_TABLE(of, i2c_iop3xx_match);
|
||||
|
||||
static struct platform_driver iop3xx_i2c_driver = {
|
||||
.probe = iop3xx_i2c_probe,
|
||||
.remove_new = iop3xx_i2c_remove,
|
||||
.remove = iop3xx_i2c_remove,
|
||||
.driver = {
|
||||
.name = "IOP3xx-I2C",
|
||||
.of_match_table = i2c_iop3xx_match,
|
||||
|
@ -1,41 +1,39 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
i2c-isch.c - Linux kernel driver for Intel SCH chipset SMBus
|
||||
- Based on i2c-piix4.c
|
||||
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
|
||||
Philip Edelbrock <phil@netroedge.com>
|
||||
- Intel SCH support
|
||||
Copyright (c) 2007 - 2008 Jacob Jun Pan <jacob.jun.pan@intel.com>
|
||||
* Linux kernel driver for Intel SCH chipset SMBus
|
||||
* - Based on i2c-piix4.c
|
||||
* Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
|
||||
* Philip Edelbrock <phil@netroedge.com>
|
||||
* - Intel SCH support
|
||||
* Copyright (c) 2007 - 2008 Jacob Jun Pan <jacob.jun.pan@intel.com>
|
||||
*/
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Supports:
|
||||
Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L)
|
||||
Note: we assume there can only be one device, with one SMBus interface.
|
||||
*/
|
||||
/* Supports: Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L) */
|
||||
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* SCH SMBus address offsets */
|
||||
#define SMBHSTCNT (0 + sch_smba)
|
||||
#define SMBHSTSTS (1 + sch_smba)
|
||||
#define SMBHSTCLK (2 + sch_smba)
|
||||
#define SMBHSTADD (4 + sch_smba) /* TSA */
|
||||
#define SMBHSTCMD (5 + sch_smba)
|
||||
#define SMBHSTDAT0 (6 + sch_smba)
|
||||
#define SMBHSTDAT1 (7 + sch_smba)
|
||||
#define SMBBLKDAT (0x20 + sch_smba)
|
||||
|
||||
/* Other settings */
|
||||
#define MAX_RETRIES 5000
|
||||
#define SMBHSTCNT 0x00
|
||||
#define SMBHSTSTS 0x01
|
||||
#define SMBHSTCLK 0x02
|
||||
#define SMBHSTADD 0x04 /* TSA */
|
||||
#define SMBHSTCMD 0x05
|
||||
#define SMBHSTDAT0 0x06
|
||||
#define SMBHSTDAT1 0x07
|
||||
#define SMBBLKDAT 0x20
|
||||
|
||||
/* I2C constants */
|
||||
#define SCH_QUICK 0x00
|
||||
@ -44,109 +42,134 @@
|
||||
#define SCH_WORD_DATA 0x03
|
||||
#define SCH_BLOCK_DATA 0x05
|
||||
|
||||
static unsigned short sch_smba;
|
||||
static struct i2c_adapter sch_adapter;
|
||||
struct sch_i2c {
|
||||
struct i2c_adapter adapter;
|
||||
void __iomem *smba;
|
||||
};
|
||||
|
||||
static int backbone_speed = 33000; /* backbone speed in kHz */
|
||||
module_param(backbone_speed, int, S_IRUSR | S_IWUSR);
|
||||
module_param(backbone_speed, int, 0600);
|
||||
MODULE_PARM_DESC(backbone_speed, "Backbone speed in kHz, (default = 33000)");
|
||||
|
||||
/*
|
||||
* Start the i2c transaction -- the i2c_access will prepare the transaction
|
||||
* and this function will execute it.
|
||||
* return 0 for success and others for failure.
|
||||
*/
|
||||
static int sch_transaction(void)
|
||||
static inline u8 sch_io_rd8(struct sch_i2c *priv, unsigned int offset)
|
||||
{
|
||||
int temp;
|
||||
int result = 0;
|
||||
int retries = 0;
|
||||
return ioread8(priv->smba + offset);
|
||||
}
|
||||
|
||||
dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
|
||||
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
|
||||
inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0),
|
||||
inb(SMBHSTDAT1));
|
||||
static inline void sch_io_wr8(struct sch_i2c *priv, unsigned int offset, u8 value)
|
||||
{
|
||||
iowrite8(value, priv->smba + offset);
|
||||
}
|
||||
|
||||
static inline u16 sch_io_rd16(struct sch_i2c *priv, unsigned int offset)
|
||||
{
|
||||
return ioread16(priv->smba + offset);
|
||||
}
|
||||
|
||||
static inline void sch_io_wr16(struct sch_i2c *priv, unsigned int offset, u16 value)
|
||||
{
|
||||
iowrite16(value, priv->smba + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* sch_transaction - Start the i2c transaction
|
||||
* @adap: the i2c adapter pointer
|
||||
*
|
||||
* The sch_access() will prepare the transaction and
|
||||
* this function will execute it.
|
||||
*
|
||||
* Return: 0 for success and others for failure.
|
||||
*/
|
||||
static int sch_transaction(struct i2c_adapter *adap)
|
||||
{
|
||||
struct sch_i2c *priv = container_of(adap, struct sch_i2c, adapter);
|
||||
int temp;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&adap->dev,
|
||||
"Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
|
||||
sch_io_rd8(priv, SMBHSTCNT), sch_io_rd8(priv, SMBHSTCMD),
|
||||
sch_io_rd8(priv, SMBHSTADD),
|
||||
sch_io_rd8(priv, SMBHSTDAT0), sch_io_rd8(priv, SMBHSTDAT1));
|
||||
|
||||
/* Make sure the SMBus host is ready to start transmitting */
|
||||
temp = inb(SMBHSTSTS) & 0x0f;
|
||||
temp = sch_io_rd8(priv, SMBHSTSTS) & 0x0f;
|
||||
if (temp) {
|
||||
/* Can not be busy since we checked it in sch_access */
|
||||
if (temp & 0x01) {
|
||||
dev_dbg(&sch_adapter.dev, "Completion (%02x). "
|
||||
"Clear...\n", temp);
|
||||
}
|
||||
if (temp & 0x06) {
|
||||
dev_dbg(&sch_adapter.dev, "SMBus error (%02x). "
|
||||
"Resetting...\n", temp);
|
||||
}
|
||||
outb(temp, SMBHSTSTS);
|
||||
temp = inb(SMBHSTSTS) & 0x0f;
|
||||
if (temp & 0x01)
|
||||
dev_dbg(&adap->dev, "Completion (%02x). Clear...\n", temp);
|
||||
if (temp & 0x06)
|
||||
dev_dbg(&adap->dev, "SMBus error (%02x). Resetting...\n", temp);
|
||||
sch_io_wr8(priv, SMBHSTSTS, temp);
|
||||
temp = sch_io_rd8(priv, SMBHSTSTS) & 0x0f;
|
||||
if (temp) {
|
||||
dev_err(&sch_adapter.dev,
|
||||
"SMBus is not ready: (%02x)\n", temp);
|
||||
dev_err(&adap->dev, "SMBus is not ready: (%02x)\n", temp);
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
/* start the transaction by setting bit 4 */
|
||||
outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT);
|
||||
|
||||
do {
|
||||
usleep_range(100, 200);
|
||||
temp = inb(SMBHSTSTS) & 0x0f;
|
||||
} while ((temp & 0x08) && (retries++ < MAX_RETRIES));
|
||||
/* Start the transaction by setting bit 4 */
|
||||
temp = sch_io_rd8(priv, SMBHSTCNT);
|
||||
temp |= 0x10;
|
||||
sch_io_wr8(priv, SMBHSTCNT, temp);
|
||||
|
||||
rc = read_poll_timeout(sch_io_rd8, temp, !(temp & 0x08), 200, 500000, true, priv, SMBHSTSTS);
|
||||
/* If the SMBus is still busy, we give up */
|
||||
if (retries > MAX_RETRIES) {
|
||||
dev_err(&sch_adapter.dev, "SMBus Timeout!\n");
|
||||
result = -ETIMEDOUT;
|
||||
if (rc) {
|
||||
dev_err(&adap->dev, "SMBus Timeout!\n");
|
||||
} else if (temp & 0x04) {
|
||||
result = -EIO;
|
||||
dev_dbg(&sch_adapter.dev, "Bus collision! SMBus may be "
|
||||
"locked until next hard reset. (sorry!)\n");
|
||||
rc = -EIO;
|
||||
dev_dbg(&adap->dev, "Bus collision! SMBus may be locked until next hard reset. (sorry!)\n");
|
||||
/* Clock stops and target is stuck in mid-transmission */
|
||||
} else if (temp & 0x02) {
|
||||
result = -EIO;
|
||||
dev_err(&sch_adapter.dev, "Error: no response!\n");
|
||||
rc = -EIO;
|
||||
dev_err(&adap->dev, "Error: no response!\n");
|
||||
} else if (temp & 0x01) {
|
||||
dev_dbg(&sch_adapter.dev, "Post complete!\n");
|
||||
outb(temp, SMBHSTSTS);
|
||||
temp = inb(SMBHSTSTS) & 0x07;
|
||||
dev_dbg(&adap->dev, "Post complete!\n");
|
||||
sch_io_wr8(priv, SMBHSTSTS, temp & 0x0f);
|
||||
temp = sch_io_rd8(priv, SMBHSTSTS) & 0x07;
|
||||
if (temp & 0x06) {
|
||||
/* Completion clear failed */
|
||||
dev_dbg(&sch_adapter.dev, "Failed reset at end of "
|
||||
"transaction (%02x), Bus error!\n", temp);
|
||||
dev_dbg(&adap->dev,
|
||||
"Failed reset at end of transaction (%02x), Bus error!\n", temp);
|
||||
}
|
||||
} else {
|
||||
result = -ENXIO;
|
||||
dev_dbg(&sch_adapter.dev, "No such address.\n");
|
||||
rc = -ENXIO;
|
||||
dev_dbg(&adap->dev, "No such address.\n");
|
||||
}
|
||||
dev_dbg(&sch_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
|
||||
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
|
||||
inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0),
|
||||
inb(SMBHSTDAT1));
|
||||
return result;
|
||||
dev_dbg(&adap->dev, "Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
|
||||
sch_io_rd8(priv, SMBHSTCNT), sch_io_rd8(priv, SMBHSTCMD),
|
||||
sch_io_rd8(priv, SMBHSTADD),
|
||||
sch_io_rd8(priv, SMBHSTDAT0), sch_io_rd8(priv, SMBHSTDAT1));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the main access entry for i2c-sch access
|
||||
* adap is i2c_adapter pointer, addr is the i2c device bus address, read_write
|
||||
* (0 for read and 1 for write), size is i2c transaction type and data is the
|
||||
* union of transaction for data to be transferred or data read from bus.
|
||||
* return 0 for success and others for failure.
|
||||
/**
|
||||
* sch_access - the main access entry for i2c-sch access
|
||||
* @adap: the i2c adapter pointer
|
||||
* @addr: the i2c device bus address
|
||||
* @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
|
||||
* @read_write: 0 for read and 1 for write
|
||||
* @command: Byte interpreted by slave, for protocols which use such bytes
|
||||
* @size: the i2c transaction type
|
||||
* @data: the union of transaction for data to be transferred or data read from bus
|
||||
*
|
||||
* Return: 0 for success and others for failure.
|
||||
*/
|
||||
static s32 sch_access(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size, union i2c_smbus_data *data)
|
||||
{
|
||||
struct sch_i2c *priv = container_of(adap, struct sch_i2c, adapter);
|
||||
int i, len, temp, rc;
|
||||
|
||||
/* Make sure the SMBus host is not busy */
|
||||
temp = inb(SMBHSTSTS) & 0x0f;
|
||||
temp = sch_io_rd8(priv, SMBHSTSTS) & 0x0f;
|
||||
if (temp & 0x08) {
|
||||
dev_dbg(&sch_adapter.dev, "SMBus busy (%02x)\n", temp);
|
||||
dev_dbg(&adap->dev, "SMBus busy (%02x)\n", temp);
|
||||
return -EAGAIN;
|
||||
}
|
||||
temp = inw(SMBHSTCLK);
|
||||
temp = sch_io_rd16(priv, SMBHSTCLK);
|
||||
if (!temp) {
|
||||
/*
|
||||
* We can't determine if we have 33 or 25 MHz clock for
|
||||
@ -154,50 +177,48 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr,
|
||||
* 100 kHz. If we actually run at 25 MHz the bus will be
|
||||
* run ~75 kHz instead which should do no harm.
|
||||
*/
|
||||
dev_notice(&sch_adapter.dev,
|
||||
"Clock divider uninitialized. Setting defaults\n");
|
||||
outw(backbone_speed / (4 * 100), SMBHSTCLK);
|
||||
dev_notice(&adap->dev, "Clock divider uninitialized. Setting defaults\n");
|
||||
sch_io_wr16(priv, SMBHSTCLK, backbone_speed / (4 * 100));
|
||||
}
|
||||
|
||||
dev_dbg(&sch_adapter.dev, "access size: %d %s\n", size,
|
||||
(read_write)?"READ":"WRITE");
|
||||
dev_dbg(&adap->dev, "access size: %d %s\n", size, str_read_write(read_write));
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
outb((addr << 1) | read_write, SMBHSTADD);
|
||||
sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
|
||||
size = SCH_QUICK;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE:
|
||||
outb((addr << 1) | read_write, SMBHSTADD);
|
||||
sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
outb(command, SMBHSTCMD);
|
||||
sch_io_wr8(priv, SMBHSTCMD, command);
|
||||
size = SCH_BYTE;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
outb((addr << 1) | read_write, SMBHSTADD);
|
||||
outb(command, SMBHSTCMD);
|
||||
sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
|
||||
sch_io_wr8(priv, SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
outb(data->byte, SMBHSTDAT0);
|
||||
sch_io_wr8(priv, SMBHSTDAT0, data->byte);
|
||||
size = SCH_BYTE_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
outb((addr << 1) | read_write, SMBHSTADD);
|
||||
outb(command, SMBHSTCMD);
|
||||
sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
|
||||
sch_io_wr8(priv, SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
outb(data->word & 0xff, SMBHSTDAT0);
|
||||
outb((data->word & 0xff00) >> 8, SMBHSTDAT1);
|
||||
sch_io_wr8(priv, SMBHSTDAT0, data->word >> 0);
|
||||
sch_io_wr8(priv, SMBHSTDAT1, data->word >> 8);
|
||||
}
|
||||
size = SCH_WORD_DATA;
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
outb((addr << 1) | read_write, SMBHSTADD);
|
||||
outb(command, SMBHSTCMD);
|
||||
sch_io_wr8(priv, SMBHSTADD, (addr << 1) | read_write);
|
||||
sch_io_wr8(priv, SMBHSTCMD, command);
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
len = data->block[0];
|
||||
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EINVAL;
|
||||
outb(len, SMBHSTDAT0);
|
||||
sch_io_wr8(priv, SMBHSTDAT0, len);
|
||||
for (i = 1; i <= len; i++)
|
||||
outb(data->block[i], SMBBLKDAT+i-1);
|
||||
sch_io_wr8(priv, SMBBLKDAT + i - 1, data->block[i]);
|
||||
}
|
||||
size = SCH_BLOCK_DATA;
|
||||
break;
|
||||
@ -205,10 +226,13 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr,
|
||||
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
dev_dbg(&sch_adapter.dev, "write size %d to 0x%04x\n", size, SMBHSTCNT);
|
||||
outb((inb(SMBHSTCNT) & 0xb0) | (size & 0x7), SMBHSTCNT);
|
||||
dev_dbg(&adap->dev, "write size %d to 0x%04x\n", size, SMBHSTCNT);
|
||||
|
||||
rc = sch_transaction();
|
||||
temp = sch_io_rd8(priv, SMBHSTCNT);
|
||||
temp = (temp & 0xb0) | (size & 0x7);
|
||||
sch_io_wr8(priv, SMBHSTCNT, temp);
|
||||
|
||||
rc = sch_transaction(adap);
|
||||
if (rc) /* Error in transaction */
|
||||
return rc;
|
||||
|
||||
@ -218,17 +242,18 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr,
|
||||
switch (size) {
|
||||
case SCH_BYTE:
|
||||
case SCH_BYTE_DATA:
|
||||
data->byte = inb(SMBHSTDAT0);
|
||||
data->byte = sch_io_rd8(priv, SMBHSTDAT0);
|
||||
break;
|
||||
case SCH_WORD_DATA:
|
||||
data->word = inb(SMBHSTDAT0) + (inb(SMBHSTDAT1) << 8);
|
||||
data->word = (sch_io_rd8(priv, SMBHSTDAT0) << 0) +
|
||||
(sch_io_rd8(priv, SMBHSTDAT1) << 8);
|
||||
break;
|
||||
case SCH_BLOCK_DATA:
|
||||
data->block[0] = inb(SMBHSTDAT0);
|
||||
data->block[0] = sch_io_rd8(priv, SMBHSTDAT0);
|
||||
if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EPROTO;
|
||||
for (i = 1; i <= data->block[0]; i++)
|
||||
data->block[i] = inb(SMBBLKDAT+i-1);
|
||||
data->block[i] = sch_io_rd8(priv, SMBBLKDAT + i - 1);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
@ -246,51 +271,34 @@ static const struct i2c_algorithm smbus_algorithm = {
|
||||
.functionality = sch_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter sch_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.algo = &smbus_algorithm,
|
||||
};
|
||||
|
||||
static int smbus_sch_probe(struct platform_device *dev)
|
||||
static int smbus_sch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sch_i2c *priv;
|
||||
struct resource *res;
|
||||
int retval;
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_IO, 0);
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
return -EBUSY;
|
||||
|
||||
if (!devm_request_region(&dev->dev, res->start, resource_size(res),
|
||||
dev->name)) {
|
||||
dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
|
||||
sch_smba);
|
||||
return -EBUSY;
|
||||
}
|
||||
priv->smba = devm_ioport_map(dev, res->start, resource_size(res));
|
||||
if (!priv->smba)
|
||||
return dev_err_probe(dev, -EBUSY, "SMBus region %pR already in use!\n", res);
|
||||
|
||||
sch_smba = res->start;
|
||||
/* Set up the sysfs linkage to our parent device */
|
||||
priv->adapter.dev.parent = dev;
|
||||
priv->adapter.owner = THIS_MODULE,
|
||||
priv->adapter.class = I2C_CLASS_HWMON,
|
||||
priv->adapter.algo = &smbus_algorithm,
|
||||
|
||||
dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
|
||||
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
|
||||
"SMBus SCH adapter at %04x", (unsigned short)res->start);
|
||||
|
||||
/* set up the sysfs linkage to our parent device */
|
||||
sch_adapter.dev.parent = &dev->dev;
|
||||
|
||||
snprintf(sch_adapter.name, sizeof(sch_adapter.name),
|
||||
"SMBus SCH adapter at %04x", sch_smba);
|
||||
|
||||
retval = i2c_add_adapter(&sch_adapter);
|
||||
if (retval)
|
||||
sch_smba = 0;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void smbus_sch_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (sch_smba) {
|
||||
i2c_del_adapter(&sch_adapter);
|
||||
sch_smba = 0;
|
||||
}
|
||||
return devm_i2c_add_adapter(dev, &priv->adapter);
|
||||
}
|
||||
|
||||
static struct platform_driver smbus_sch_driver = {
|
||||
@ -298,7 +306,6 @@ static struct platform_driver smbus_sch_driver = {
|
||||
.name = "isch_smbus",
|
||||
},
|
||||
.probe = smbus_sch_probe,
|
||||
.remove_new = smbus_sch_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(smbus_sch_driver);
|
||||
|
@ -847,7 +847,7 @@ static void jz4780_i2c_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver jz4780_i2c_driver = {
|
||||
.probe = jz4780_i2c_probe,
|
||||
.remove_new = jz4780_i2c_remove,
|
||||
.remove = jz4780_i2c_remove,
|
||||
.driver = {
|
||||
.name = "jz4780-i2c",
|
||||
.of_match_table = jz4780_i2c_of_matches,
|
||||
|
@ -385,7 +385,7 @@ static struct platform_driver kempld_i2c_driver = {
|
||||
.pm = pm_sleep_ptr(&kempld_i2c_pm_ops),
|
||||
},
|
||||
.probe = kempld_i2c_probe,
|
||||
.remove_new = kempld_i2c_remove,
|
||||
.remove = kempld_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(kempld_i2c_driver);
|
||||
|
@ -462,7 +462,7 @@ MODULE_DEVICE_TABLE(of, lpc2k_i2c_match);
|
||||
|
||||
static struct platform_driver i2c_lpc2k_driver = {
|
||||
.probe = i2c_lpc2k_probe,
|
||||
.remove_new = i2c_lpc2k_remove,
|
||||
.remove = i2c_lpc2k_remove,
|
||||
.driver = {
|
||||
.name = "lpc2k-i2c",
|
||||
.pm = pm_sleep_ptr(&i2c_lpc2k_dev_pm_ops),
|
||||
|
@ -565,7 +565,7 @@ MODULE_DEVICE_TABLE(of, meson_i2c_match);
|
||||
|
||||
static struct platform_driver meson_i2c_driver = {
|
||||
.probe = meson_i2c_probe,
|
||||
.remove_new = meson_i2c_remove,
|
||||
.remove = meson_i2c_remove,
|
||||
.driver = {
|
||||
.name = "meson-i2c",
|
||||
.of_match_table = meson_i2c_match,
|
||||
|
@ -462,7 +462,7 @@ MODULE_DEVICE_TABLE(of, mchp_corei2c_of_match);
|
||||
|
||||
static struct platform_driver mchp_corei2c_driver = {
|
||||
.probe = mchp_corei2c_probe,
|
||||
.remove_new = mchp_corei2c_remove,
|
||||
.remove = mchp_corei2c_remove,
|
||||
.driver = {
|
||||
.name = "microchip-corei2c",
|
||||
.of_match_table = mchp_corei2c_of_match,
|
||||
|
@ -2456,7 +2456,7 @@ static void mlxbf_i2c_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver mlxbf_i2c_driver = {
|
||||
.probe = mlxbf_i2c_probe,
|
||||
.remove_new = mlxbf_i2c_remove,
|
||||
.remove = mlxbf_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c-mlxbf",
|
||||
.acpi_match_table = ACPI_PTR(mlxbf_i2c_acpi_ids),
|
||||
|
@ -591,7 +591,7 @@ static void mlxcpld_i2c_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver mlxcpld_i2c_driver = {
|
||||
.probe = mlxcpld_i2c_probe,
|
||||
.remove_new = mlxcpld_i2c_remove,
|
||||
.remove = mlxcpld_i2c_remove,
|
||||
.driver = {
|
||||
.name = MLXCPLD_I2C_DEVICE_NAME,
|
||||
},
|
||||
|
@ -938,7 +938,7 @@ MODULE_DEVICE_TABLE(of, mpc_i2c_of_match);
|
||||
/* Structure for a device driver */
|
||||
static struct platform_driver mpc_i2c_driver = {
|
||||
.probe = fsl_i2c_probe,
|
||||
.remove_new = fsl_i2c_remove,
|
||||
.remove = fsl_i2c_remove,
|
||||
.driver = {
|
||||
.name = "mpc-i2c",
|
||||
.of_match_table = mpc_i2c_of_match,
|
||||
|
@ -1550,7 +1550,7 @@ static const struct dev_pm_ops mtk_i2c_pm = {
|
||||
|
||||
static struct platform_driver mtk_i2c_driver = {
|
||||
.probe = mtk_i2c_probe,
|
||||
.remove_new = mtk_i2c_remove,
|
||||
.remove = mtk_i2c_remove,
|
||||
.driver = {
|
||||
.name = I2C_DRV_NAME,
|
||||
.pm = pm_sleep_ptr(&mtk_i2c_pm),
|
||||
|
@ -331,7 +331,7 @@ static void mtk_i2c_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver mtk_i2c_driver = {
|
||||
.probe = mtk_i2c_probe,
|
||||
.remove_new = mtk_i2c_remove,
|
||||
.remove = mtk_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c-mt7621",
|
||||
.of_match_table = i2c_mtk_dt_ids,
|
||||
|
@ -1104,7 +1104,7 @@ static const struct dev_pm_ops mv64xxx_i2c_pm_ops = {
|
||||
|
||||
static struct platform_driver mv64xxx_i2c_driver = {
|
||||
.probe = mv64xxx_i2c_probe,
|
||||
.remove_new = mv64xxx_i2c_remove,
|
||||
.remove = mv64xxx_i2c_remove,
|
||||
.driver = {
|
||||
.name = MV64XXX_I2C_CTLR_NAME,
|
||||
.pm = &mv64xxx_i2c_pm_ops,
|
||||
|
@ -881,7 +881,7 @@ static struct platform_driver mxs_i2c_driver = {
|
||||
.of_match_table = mxs_i2c_dt_ids,
|
||||
},
|
||||
.probe = mxs_i2c_probe,
|
||||
.remove_new = mxs_i2c_remove,
|
||||
.remove = mxs_i2c_remove,
|
||||
};
|
||||
|
||||
static int __init mxs_i2c_init(void)
|
||||
|
@ -1,240 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard
|
||||
*
|
||||
* Copyright (C) 2008 Jean Delvare <jdelvare@suse.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* We select the channels by sending commands to the Philips
|
||||
* PCA9556 chip at I2C address 0x18. The main adapter is used for
|
||||
* the non-multiplexed part of the bus, and 4 virtual adapters
|
||||
* are defined for the multiplexed addresses: 0x50-0x53 (memory
|
||||
* module EEPROM) located on channels 1-4. We define one virtual
|
||||
* adapter per CPU, which corresponds to one multiplexed channel:
|
||||
* CPU0: virtual adapter 1, channel 1
|
||||
* CPU1: virtual adapter 2, channel 2
|
||||
* CPU2: virtual adapter 3, channel 3
|
||||
* CPU3: virtual adapter 4, channel 4
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
extern struct i2c_adapter *nforce2_smbus;
|
||||
|
||||
static struct i2c_adapter *s4985_adapter;
|
||||
static struct i2c_algorithm *s4985_algo;
|
||||
|
||||
/* Wrapper access functions for multiplexed SMBus */
|
||||
static DEFINE_MUTEX(nforce2_lock);
|
||||
|
||||
static s32 nforce2_access_virt0(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* We exclude the multiplexed addresses */
|
||||
if ((addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
|
||||
|| addr == 0x18)
|
||||
return -ENXIO;
|
||||
|
||||
mutex_lock(&nforce2_lock);
|
||||
error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
|
||||
command, size, data);
|
||||
mutex_unlock(&nforce2_lock);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* We remember the last used channels combination so as to only switch
|
||||
channels when it is really needed. This greatly reduces the SMBus
|
||||
overhead, but also assumes that nobody will be writing to the PCA9556
|
||||
in our back. */
|
||||
static u8 last_channels;
|
||||
|
||||
static inline s32 nforce2_access_channel(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data *data,
|
||||
u8 channels)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* We exclude the non-multiplexed addresses */
|
||||
if ((addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
|
||||
return -ENXIO;
|
||||
|
||||
mutex_lock(&nforce2_lock);
|
||||
if (last_channels != channels) {
|
||||
union i2c_smbus_data mplxdata;
|
||||
mplxdata.byte = channels;
|
||||
|
||||
error = nforce2_smbus->algo->smbus_xfer(adap, 0x18, 0,
|
||||
I2C_SMBUS_WRITE, 0x01,
|
||||
I2C_SMBUS_BYTE_DATA,
|
||||
&mplxdata);
|
||||
if (error)
|
||||
goto UNLOCK;
|
||||
last_channels = channels;
|
||||
}
|
||||
error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
|
||||
command, size, data);
|
||||
|
||||
UNLOCK:
|
||||
mutex_unlock(&nforce2_lock);
|
||||
return error;
|
||||
}
|
||||
|
||||
static s32 nforce2_access_virt1(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
/* CPU0: channel 1 enabled */
|
||||
return nforce2_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x02);
|
||||
}
|
||||
|
||||
static s32 nforce2_access_virt2(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
/* CPU1: channel 2 enabled */
|
||||
return nforce2_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x04);
|
||||
}
|
||||
|
||||
static s32 nforce2_access_virt3(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
/* CPU2: channel 3 enabled */
|
||||
return nforce2_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x08);
|
||||
}
|
||||
|
||||
static s32 nforce2_access_virt4(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
/* CPU3: channel 4 enabled */
|
||||
return nforce2_access_channel(adap, addr, flags, read_write, command,
|
||||
size, data, 0x10);
|
||||
}
|
||||
|
||||
static int __init nforce2_s4985_init(void)
|
||||
{
|
||||
int i, error;
|
||||
union i2c_smbus_data ioconfig;
|
||||
|
||||
if (!nforce2_smbus)
|
||||
return -ENODEV;
|
||||
|
||||
/* Configure the PCA9556 multiplexer */
|
||||
ioconfig.byte = 0x00; /* All I/O to output mode */
|
||||
error = i2c_smbus_xfer(nforce2_smbus, 0x18, 0, I2C_SMBUS_WRITE, 0x03,
|
||||
I2C_SMBUS_BYTE_DATA, &ioconfig);
|
||||
if (error) {
|
||||
dev_err(&nforce2_smbus->dev, "PCA9556 configuration failed\n");
|
||||
error = -EIO;
|
||||
goto ERROR0;
|
||||
}
|
||||
|
||||
/* Unregister physical bus */
|
||||
i2c_del_adapter(nforce2_smbus);
|
||||
|
||||
printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n");
|
||||
/* Define the 5 virtual adapters and algorithms structures */
|
||||
s4985_adapter = kcalloc(5, sizeof(struct i2c_adapter), GFP_KERNEL);
|
||||
if (!s4985_adapter) {
|
||||
error = -ENOMEM;
|
||||
goto ERROR1;
|
||||
}
|
||||
s4985_algo = kcalloc(5, sizeof(struct i2c_algorithm), GFP_KERNEL);
|
||||
if (!s4985_algo) {
|
||||
error = -ENOMEM;
|
||||
goto ERROR2;
|
||||
}
|
||||
|
||||
/* Fill in the new structures */
|
||||
s4985_algo[0] = *(nforce2_smbus->algo);
|
||||
s4985_algo[0].smbus_xfer = nforce2_access_virt0;
|
||||
s4985_adapter[0] = *nforce2_smbus;
|
||||
s4985_adapter[0].algo = s4985_algo;
|
||||
s4985_adapter[0].dev.parent = nforce2_smbus->dev.parent;
|
||||
for (i = 1; i < 5; i++) {
|
||||
s4985_algo[i] = *(nforce2_smbus->algo);
|
||||
s4985_adapter[i] = *nforce2_smbus;
|
||||
snprintf(s4985_adapter[i].name, sizeof(s4985_adapter[i].name),
|
||||
"SMBus nForce2 adapter (CPU%d)", i - 1);
|
||||
s4985_adapter[i].algo = s4985_algo + i;
|
||||
s4985_adapter[i].dev.parent = nforce2_smbus->dev.parent;
|
||||
}
|
||||
s4985_algo[1].smbus_xfer = nforce2_access_virt1;
|
||||
s4985_algo[2].smbus_xfer = nforce2_access_virt2;
|
||||
s4985_algo[3].smbus_xfer = nforce2_access_virt3;
|
||||
s4985_algo[4].smbus_xfer = nforce2_access_virt4;
|
||||
|
||||
/* Register virtual adapters */
|
||||
for (i = 0; i < 5; i++) {
|
||||
error = i2c_add_adapter(s4985_adapter + i);
|
||||
if (error) {
|
||||
printk(KERN_ERR "i2c-nforce2-s4985: "
|
||||
"Virtual adapter %d registration "
|
||||
"failed, module not inserted\n", i);
|
||||
for (i--; i >= 0; i--)
|
||||
i2c_del_adapter(s4985_adapter + i);
|
||||
goto ERROR3;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERROR3:
|
||||
kfree(s4985_algo);
|
||||
s4985_algo = NULL;
|
||||
ERROR2:
|
||||
kfree(s4985_adapter);
|
||||
s4985_adapter = NULL;
|
||||
ERROR1:
|
||||
/* Restore physical bus */
|
||||
i2c_add_adapter(nforce2_smbus);
|
||||
ERROR0:
|
||||
return error;
|
||||
}
|
||||
|
||||
static void __exit nforce2_s4985_exit(void)
|
||||
{
|
||||
if (s4985_adapter) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
i2c_del_adapter(s4985_adapter+i);
|
||||
kfree(s4985_adapter);
|
||||
s4985_adapter = NULL;
|
||||
}
|
||||
kfree(s4985_algo);
|
||||
s4985_algo = NULL;
|
||||
|
||||
/* Restore physical bus */
|
||||
if (i2c_add_adapter(nforce2_smbus))
|
||||
printk(KERN_ERR "i2c-nforce2-s4985: "
|
||||
"Physical bus restoration failed\n");
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
||||
MODULE_DESCRIPTION("S4985 SMBus multiplexing");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(nforce2_s4985_init);
|
||||
module_exit(nforce2_s4985_exit);
|
@ -117,20 +117,6 @@ static const struct dmi_system_id nforce2_dmi_blacklist2[] = {
|
||||
|
||||
static struct pci_driver nforce2_driver;
|
||||
|
||||
/* For multiplexing support, we need a global reference to the 1st
|
||||
SMBus channel */
|
||||
#if IS_ENABLED(CONFIG_I2C_NFORCE2_S4985)
|
||||
struct i2c_adapter *nforce2_smbus;
|
||||
EXPORT_SYMBOL_GPL(nforce2_smbus);
|
||||
|
||||
static void nforce2_set_reference(struct i2c_adapter *adap)
|
||||
{
|
||||
nforce2_smbus = adap;
|
||||
}
|
||||
#else
|
||||
static inline void nforce2_set_reference(struct i2c_adapter *adap) { }
|
||||
#endif
|
||||
|
||||
static void nforce2_abort(struct i2c_adapter *adap)
|
||||
{
|
||||
struct nforce2_smbus *smbus = adap->algo_data;
|
||||
@ -411,7 +397,6 @@ static int nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
nforce2_set_reference(&smbuses[0].adapter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -420,7 +405,6 @@ static void nforce2_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct nforce2_smbus *smbuses = pci_get_drvdata(dev);
|
||||
|
||||
nforce2_set_reference(NULL);
|
||||
if (smbuses[0].base) {
|
||||
i2c_del_adapter(&smbuses[0].adapter);
|
||||
release_region(smbuses[0].base, smbuses[0].size);
|
||||
|
@ -334,6 +334,7 @@ struct npcm_i2c {
|
||||
u64 nack_cnt;
|
||||
u64 timeout_cnt;
|
||||
u64 tx_complete_cnt;
|
||||
bool ber_state; /* Indicate the bus error state */
|
||||
};
|
||||
|
||||
static inline void npcm_i2c_select_bank(struct npcm_i2c *bus,
|
||||
@ -1521,6 +1522,7 @@ static void npcm_i2c_irq_handle_ber(struct npcm_i2c *bus)
|
||||
if (npcm_i2c_is_master(bus)) {
|
||||
npcm_i2c_master_abort(bus);
|
||||
} else {
|
||||
bus->ber_state = true;
|
||||
npcm_i2c_clear_master_status(bus);
|
||||
|
||||
/* Clear BB (BUS BUSY) bit */
|
||||
@ -1628,13 +1630,10 @@ static void npcm_i2c_irq_handle_sda(struct npcm_i2c *bus, u8 i2cst)
|
||||
npcm_i2c_wr_byte(bus, bus->dest_addr | BIT(0));
|
||||
/* SDA interrupt, after start\restart */
|
||||
} else {
|
||||
if (NPCM_I2CST_XMIT & i2cst) {
|
||||
bus->operation = I2C_WRITE_OPER;
|
||||
if (bus->operation == I2C_WRITE_OPER)
|
||||
npcm_i2c_irq_master_handler_write(bus);
|
||||
} else {
|
||||
bus->operation = I2C_READ_OPER;
|
||||
else if (bus->operation == I2C_READ_OPER)
|
||||
npcm_i2c_irq_master_handler_read(bus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1702,6 +1701,7 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
|
||||
dev_dbg(bus->dev, "bus%d-0x%x recovery skipped, bus not stuck",
|
||||
bus->num, bus->dest_addr);
|
||||
npcm_i2c_reset(bus);
|
||||
bus->ber_state = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1766,6 +1766,7 @@ static int npcm_i2c_recovery_tgclk(struct i2c_adapter *_adap)
|
||||
if (bus->rec_succ_cnt < ULLONG_MAX)
|
||||
bus->rec_succ_cnt++;
|
||||
}
|
||||
bus->ber_state = false;
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -2161,7 +2162,16 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
|
||||
} while (time_is_after_jiffies(time_left) && bus_busy);
|
||||
|
||||
if (bus_busy) {
|
||||
/*
|
||||
* Check the BER (bus error) state, when ber_state is true, it means that the module
|
||||
* detects the bus error which is caused by some factor like that the electricity
|
||||
* noise occurs on the bus. Under this condition, the module is reset and the bus
|
||||
* gets recovered.
|
||||
*
|
||||
* While ber_state is false, the module reset and bus recovery also get done as the
|
||||
* bus is busy.
|
||||
*/
|
||||
if (bus_busy || bus->ber_state) {
|
||||
iowrite8(NPCM_I2CCST_BB, bus->reg + NPCM_I2CCST);
|
||||
npcm_i2c_reset(bus);
|
||||
i2c_recover_bus(adap);
|
||||
@ -2363,7 +2373,7 @@ MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table);
|
||||
|
||||
static struct platform_driver npcm_i2c_bus_driver = {
|
||||
.probe = npcm_i2c_probe_bus,
|
||||
.remove_new = npcm_i2c_remove_bus,
|
||||
.remove = npcm_i2c_remove_bus,
|
||||
.driver = {
|
||||
.name = "nuvoton-i2c",
|
||||
.of_match_table = npcm_i2c_bus_of_table,
|
||||
|
@ -769,7 +769,7 @@ static DEFINE_NOIRQ_DEV_PM_OPS(ocores_i2c_pm,
|
||||
|
||||
static struct platform_driver ocores_i2c_driver = {
|
||||
.probe = ocores_i2c_probe,
|
||||
.remove_new = ocores_i2c_remove,
|
||||
.remove = ocores_i2c_remove,
|
||||
.driver = {
|
||||
.name = "ocores-i2c",
|
||||
.of_match_table = ocores_i2c_match,
|
||||
|
@ -269,7 +269,7 @@ MODULE_DEVICE_TABLE(of, octeon_i2c_match);
|
||||
|
||||
static struct platform_driver octeon_i2c_driver = {
|
||||
.probe = octeon_i2c_probe,
|
||||
.remove_new = octeon_i2c_remove,
|
||||
.remove = octeon_i2c_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = octeon_i2c_match,
|
||||
|
@ -1605,7 +1605,7 @@ static const struct dev_pm_ops omap_i2c_pm_ops = {
|
||||
|
||||
static struct platform_driver omap_i2c_driver = {
|
||||
.probe = omap_i2c_probe,
|
||||
.remove_new = omap_i2c_remove,
|
||||
.remove = omap_i2c_remove,
|
||||
.driver = {
|
||||
.name = "omap_i2c",
|
||||
.pm = pm_ptr(&omap_i2c_pm_ops),
|
||||
|
@ -249,7 +249,7 @@ MODULE_DEVICE_TABLE(of, i2c_opal_of_match);
|
||||
|
||||
static struct platform_driver i2c_opal_driver = {
|
||||
.probe = i2c_opal_probe,
|
||||
.remove_new = i2c_opal_remove,
|
||||
.remove = i2c_opal_remove,
|
||||
.driver = {
|
||||
.name = "i2c-opal",
|
||||
.of_match_table = i2c_opal_of_match,
|
||||
|
@ -104,7 +104,7 @@ static struct platform_driver pasemi_platform_i2c_driver = {
|
||||
.of_match_table = pasemi_platform_i2c_of_match,
|
||||
},
|
||||
.probe = pasemi_platform_i2c_probe,
|
||||
.remove_new = pasemi_platform_i2c_remove,
|
||||
.remove = pasemi_platform_i2c_remove,
|
||||
};
|
||||
module_platform_driver(pasemi_platform_i2c_driver);
|
||||
|
||||
|
@ -238,7 +238,7 @@ MODULE_DEVICE_TABLE(of, i2c_pca_of_match_table);
|
||||
|
||||
static struct platform_driver i2c_pca_pf_driver = {
|
||||
.probe = i2c_pca_pf_probe,
|
||||
.remove_new = i2c_pca_pf_remove,
|
||||
.remove = i2c_pca_pf_remove,
|
||||
.driver = {
|
||||
.name = "i2c-pca-platform",
|
||||
.of_match_table = of_match_ptr(i2c_pca_of_match_table),
|
||||
|
@ -35,20 +35,7 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
|
||||
/* PIIX4 SMBus address offsets */
|
||||
#define SMBHSTSTS (0 + piix4_smba)
|
||||
#define SMBHSLVSTS (1 + piix4_smba)
|
||||
#define SMBHSTCNT (2 + piix4_smba)
|
||||
#define SMBHSTCMD (3 + piix4_smba)
|
||||
#define SMBHSTADD (4 + piix4_smba)
|
||||
#define SMBHSTDAT0 (5 + piix4_smba)
|
||||
#define SMBHSTDAT1 (6 + piix4_smba)
|
||||
#define SMBBLKDAT (7 + piix4_smba)
|
||||
#define SMBSLVCNT (8 + piix4_smba)
|
||||
#define SMBSHDWCMD (9 + piix4_smba)
|
||||
#define SMBSLVEVT (0xA + piix4_smba)
|
||||
#define SMBSLVDAT (0xC + piix4_smba)
|
||||
#include "i2c-piix4.h"
|
||||
|
||||
/* count for request_region */
|
||||
#define SMBIOSIZE 9
|
||||
@ -70,7 +57,6 @@
|
||||
#define PIIX4_BYTE 0x04
|
||||
#define PIIX4_BYTE_DATA 0x08
|
||||
#define PIIX4_WORD_DATA 0x0C
|
||||
#define PIIX4_BLOCK_DATA 0x14
|
||||
|
||||
/* Multi-port constants */
|
||||
#define PIIX4_MAX_ADAPTERS 4
|
||||
@ -101,6 +87,7 @@
|
||||
|
||||
#define SB800_PIIX4_FCH_PM_ADDR 0xFED80300
|
||||
#define SB800_PIIX4_FCH_PM_SIZE 8
|
||||
#define SB800_ASF_ACPI_PATH "\\_SB.ASFC"
|
||||
|
||||
/* insmod parameters */
|
||||
|
||||
@ -160,11 +147,6 @@ static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = {
|
||||
};
|
||||
static const char *piix4_aux_port_name_sb800 = " port 1";
|
||||
|
||||
struct sb800_mmio_cfg {
|
||||
void __iomem *addr;
|
||||
bool use_mmio;
|
||||
};
|
||||
|
||||
struct i2c_piix4_adapdata {
|
||||
unsigned short smba;
|
||||
|
||||
@ -175,8 +157,7 @@ struct i2c_piix4_adapdata {
|
||||
struct sb800_mmio_cfg mmio_cfg;
|
||||
};
|
||||
|
||||
static int piix4_sb800_region_request(struct device *dev,
|
||||
struct sb800_mmio_cfg *mmio_cfg)
|
||||
int piix4_sb800_region_request(struct device *dev, struct sb800_mmio_cfg *mmio_cfg)
|
||||
{
|
||||
if (mmio_cfg->use_mmio) {
|
||||
void __iomem *addr;
|
||||
@ -214,9 +195,9 @@ static int piix4_sb800_region_request(struct device *dev,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(piix4_sb800_region_request, PIIX4_SMBUS);
|
||||
|
||||
static void piix4_sb800_region_release(struct device *dev,
|
||||
struct sb800_mmio_cfg *mmio_cfg)
|
||||
void piix4_sb800_region_release(struct device *dev, struct sb800_mmio_cfg *mmio_cfg)
|
||||
{
|
||||
if (mmio_cfg->use_mmio) {
|
||||
iounmap(mmio_cfg->addr);
|
||||
@ -227,6 +208,7 @@ static void piix4_sb800_region_release(struct device *dev,
|
||||
|
||||
release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(piix4_sb800_region_release, PIIX4_SMBUS);
|
||||
|
||||
static bool piix4_sb800_use_mmio(struct pci_dev *PIIX4_dev)
|
||||
{
|
||||
@ -536,10 +518,8 @@ static int piix4_setup_aux(struct pci_dev *PIIX4_dev,
|
||||
return piix4_smba;
|
||||
}
|
||||
|
||||
static int piix4_transaction(struct i2c_adapter *piix4_adapter)
|
||||
int piix4_transaction(struct i2c_adapter *piix4_adapter, unsigned short piix4_smba)
|
||||
{
|
||||
struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter);
|
||||
unsigned short piix4_smba = adapdata->smba;
|
||||
int temp;
|
||||
int result = 0;
|
||||
int timeout = 0;
|
||||
@ -611,6 +591,7 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
|
||||
inb_p(SMBHSTDAT1));
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(piix4_transaction, PIIX4_SMBUS);
|
||||
|
||||
/* Return negative errno on error. */
|
||||
static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
|
||||
@ -675,7 +656,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
|
||||
|
||||
outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
|
||||
|
||||
status = piix4_transaction(adap);
|
||||
status = piix4_transaction(adap, piix4_smba);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
@ -764,7 +745,7 @@ static void piix4_imc_wakeup(void)
|
||||
release_region(KERNCZ_IMC_IDX, 2);
|
||||
}
|
||||
|
||||
static int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg)
|
||||
int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg)
|
||||
{
|
||||
u8 smba_en_lo, val;
|
||||
|
||||
@ -786,6 +767,7 @@ static int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg)
|
||||
|
||||
return (smba_en_lo & piix4_port_mask_sb800);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(piix4_sb800_port_sel, PIIX4_SMBUS);
|
||||
|
||||
/*
|
||||
* Handles access to multiple SMBus ports on the SB800.
|
||||
@ -1043,6 +1025,9 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
bool is_sb800 = false;
|
||||
bool is_asf = false;
|
||||
acpi_status status;
|
||||
acpi_handle handle;
|
||||
|
||||
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
|
||||
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||
@ -1105,10 +1090,16 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
}
|
||||
}
|
||||
|
||||
status = acpi_get_handle(NULL, (acpi_string)SB800_ASF_ACPI_PATH, &handle);
|
||||
if (ACPI_SUCCESS(status))
|
||||
is_asf = true;
|
||||
|
||||
if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
(dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS ||
|
||||
dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) {
|
||||
retval = piix4_setup_sb800(dev, id, 1);
|
||||
/* Do not setup AUX port if ASF is enabled */
|
||||
if (!is_asf)
|
||||
retval = piix4_setup_sb800(dev, id, 1);
|
||||
}
|
||||
|
||||
if (retval > 0) {
|
||||
|
44
drivers/i2c/busses/i2c-piix4.h
Normal file
44
drivers/i2c/busses/i2c-piix4.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* PIIX4/SB800 SMBus Interfaces
|
||||
*
|
||||
* Copyright (c) 2024, Advanced Micro Devices, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
* Sanket Goswami <Sanket.Goswami@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef I2C_PIIX4_H
|
||||
#define I2C_PIIX4_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* PIIX4 SMBus address offsets */
|
||||
#define SMBHSTSTS (0x00 + piix4_smba)
|
||||
#define SMBHSLVSTS (0x01 + piix4_smba)
|
||||
#define SMBHSTCNT (0x02 + piix4_smba)
|
||||
#define SMBHSTCMD (0x03 + piix4_smba)
|
||||
#define SMBHSTADD (0x04 + piix4_smba)
|
||||
#define SMBHSTDAT0 (0x05 + piix4_smba)
|
||||
#define SMBHSTDAT1 (0x06 + piix4_smba)
|
||||
#define SMBBLKDAT (0x07 + piix4_smba)
|
||||
#define SMBSLVCNT (0x08 + piix4_smba)
|
||||
#define SMBSHDWCMD (0x09 + piix4_smba)
|
||||
#define SMBSLVEVT (0x0A + piix4_smba)
|
||||
#define SMBSLVDAT (0x0C + piix4_smba)
|
||||
|
||||
/* PIIX4 constants */
|
||||
#define PIIX4_BLOCK_DATA 0x14
|
||||
|
||||
struct sb800_mmio_cfg {
|
||||
void __iomem *addr;
|
||||
bool use_mmio;
|
||||
};
|
||||
|
||||
int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg);
|
||||
int piix4_transaction(struct i2c_adapter *piix4_adapter, unsigned short piix4_smba);
|
||||
int piix4_sb800_region_request(struct device *dev, struct sb800_mmio_cfg *mmio_cfg);
|
||||
void piix4_sb800_region_release(struct device *dev, struct sb800_mmio_cfg *mmio_cfg);
|
||||
|
||||
#endif /* I2C_PIIX4_H */
|
@ -733,7 +733,7 @@ static struct platform_driver i2c_pnx_driver = {
|
||||
.pm = pm_sleep_ptr(&i2c_pnx_pm),
|
||||
},
|
||||
.probe = i2c_pnx_probe,
|
||||
.remove_new = i2c_pnx_remove,
|
||||
.remove = i2c_pnx_remove,
|
||||
};
|
||||
|
||||
static int __init i2c_adap_pnx_init(void)
|
||||
|
@ -437,7 +437,7 @@ static int i2c_powermac_probe(struct platform_device *dev)
|
||||
|
||||
static struct platform_driver i2c_powermac_driver = {
|
||||
.probe = i2c_powermac_probe,
|
||||
.remove_new = i2c_powermac_remove,
|
||||
.remove = i2c_powermac_remove,
|
||||
.driver = {
|
||||
.name = "i2c-powermac",
|
||||
.bus = &platform_bus_type,
|
||||
|
@ -1574,7 +1574,7 @@ static const struct dev_pm_ops i2c_pxa_dev_pm_ops = {
|
||||
|
||||
static struct platform_driver i2c_pxa_driver = {
|
||||
.probe = i2c_pxa_probe,
|
||||
.remove_new = i2c_pxa_remove,
|
||||
.remove = i2c_pxa_remove,
|
||||
.driver = {
|
||||
.name = "pxa2xx-i2c",
|
||||
.pm = pm_sleep_ptr(&i2c_pxa_dev_pm_ops),
|
||||
|
@ -120,7 +120,6 @@ struct cci_data {
|
||||
unsigned int num_masters;
|
||||
struct i2c_adapter_quirks quirks;
|
||||
u16 queue_size[NUM_QUEUES];
|
||||
unsigned long cci_clk_rate;
|
||||
struct hw_params params[3];
|
||||
};
|
||||
|
||||
@ -523,7 +522,6 @@ static const struct dev_pm_ops qcom_cci_pm = {
|
||||
static int cci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
unsigned long cci_clk_rate = 0;
|
||||
struct device_node *child;
|
||||
struct resource *r;
|
||||
struct cci *cci;
|
||||
@ -594,22 +592,6 @@ static int cci_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, -EINVAL, "not enough clocks in DT\n");
|
||||
cci->nclocks = ret;
|
||||
|
||||
/* Retrieve CCI clock rate */
|
||||
for (i = 0; i < cci->nclocks; i++) {
|
||||
if (!strcmp(cci->clocks[i].id, "cci")) {
|
||||
cci_clk_rate = clk_get_rate(cci->clocks[i].clk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cci_clk_rate != cci->data->cci_clk_rate) {
|
||||
/* cci clock set by the bootloader or via assigned clock rate
|
||||
* in DT.
|
||||
*/
|
||||
dev_warn(dev, "Found %lu cci clk rate while %lu was expected\n",
|
||||
cci_clk_rate, cci->data->cci_clk_rate);
|
||||
}
|
||||
|
||||
ret = cci_enable_clocks(cci);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -699,7 +681,6 @@ static const struct cci_data cci_v1_data = {
|
||||
.max_write_len = 10,
|
||||
.max_read_len = 12,
|
||||
},
|
||||
.cci_clk_rate = 19200000,
|
||||
.params[I2C_MODE_STANDARD] = {
|
||||
.thigh = 78,
|
||||
.tlow = 114,
|
||||
@ -733,7 +714,6 @@ static const struct cci_data cci_v1_5_data = {
|
||||
.max_write_len = 10,
|
||||
.max_read_len = 12,
|
||||
},
|
||||
.cci_clk_rate = 19200000,
|
||||
.params[I2C_MODE_STANDARD] = {
|
||||
.thigh = 78,
|
||||
.tlow = 114,
|
||||
@ -767,7 +747,6 @@ static const struct cci_data cci_v2_data = {
|
||||
.max_write_len = 11,
|
||||
.max_read_len = 12,
|
||||
},
|
||||
.cci_clk_rate = 37500000,
|
||||
.params[I2C_MODE_STANDARD] = {
|
||||
.thigh = 201,
|
||||
.tlow = 174,
|
||||
@ -826,7 +805,7 @@ MODULE_DEVICE_TABLE(of, cci_dt_match);
|
||||
|
||||
static struct platform_driver qcom_cci_driver = {
|
||||
.probe = cci_probe,
|
||||
.remove_new = cci_remove,
|
||||
.remove = cci_remove,
|
||||
.driver = {
|
||||
.name = "i2c-qcom-cci",
|
||||
.of_match_table = cci_dt_match,
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/soc/qcom/geni-se.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#define SE_I2C_TX_TRANS_LEN 0x26c
|
||||
#define SE_I2C_RX_TRANS_LEN 0x270
|
||||
@ -146,22 +147,36 @@ struct geni_i2c_clk_fld {
|
||||
* clk_freq_out = t / t_cycle
|
||||
* source_clock = 19.2 MHz
|
||||
*/
|
||||
static const struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
|
||||
static const struct geni_i2c_clk_fld geni_i2c_clk_map_19p2mhz[] = {
|
||||
{KHZ(100), 7, 10, 11, 26},
|
||||
{KHZ(400), 2, 5, 12, 24},
|
||||
{KHZ(1000), 1, 3, 9, 18},
|
||||
{},
|
||||
};
|
||||
|
||||
/* source_clock = 32 MHz */
|
||||
static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
|
||||
{KHZ(100), 8, 14, 18, 40},
|
||||
{KHZ(400), 4, 3, 11, 20},
|
||||
{KHZ(1000), 2, 3, 6, 15},
|
||||
{},
|
||||
};
|
||||
|
||||
static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
int i;
|
||||
const struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
|
||||
const struct geni_i2c_clk_fld *itr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
|
||||
if (clk_get_rate(gi2c->se.clk) == 32 * HZ_PER_MHZ)
|
||||
itr = geni_i2c_clk_map_32mhz;
|
||||
else
|
||||
itr = geni_i2c_clk_map_19p2mhz;
|
||||
|
||||
while (itr->clk_freq_out != 0) {
|
||||
if (itr->clk_freq_out == gi2c->clk_freq_out) {
|
||||
gi2c->clk_fld = itr;
|
||||
return 0;
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -818,6 +833,8 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
||||
init_completion(&gi2c->done);
|
||||
spin_lock_init(&gi2c->lock);
|
||||
platform_set_drvdata(pdev, gi2c);
|
||||
|
||||
/* Keep interrupts disabled initially to allow for low-power modes */
|
||||
ret = devm_request_irq(dev, gi2c->irq, geni_i2c_irq, IRQF_NO_AUTOEN,
|
||||
dev_name(dev), gi2c);
|
||||
if (ret) {
|
||||
@ -1049,7 +1066,7 @@ MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
|
||||
|
||||
static struct platform_driver geni_i2c_driver = {
|
||||
.probe = geni_i2c_probe,
|
||||
.remove_new = geni_i2c_remove,
|
||||
.remove = geni_i2c_remove,
|
||||
.shutdown = geni_i2c_shutdown,
|
||||
.driver = {
|
||||
.name = "geni_i2c",
|
||||
|
@ -1974,7 +1974,7 @@ MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
|
||||
|
||||
static struct platform_driver qup_i2c_driver = {
|
||||
.probe = qup_i2c_probe,
|
||||
.remove_new = qup_i2c_remove,
|
||||
.remove = qup_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c_qup",
|
||||
.pm = pm_ptr(&qup_i2c_qup_pm_ops),
|
||||
|
@ -1271,7 +1271,7 @@ static struct platform_driver rcar_i2c_driver = {
|
||||
.pm = pm_sleep_ptr(&rcar_i2c_pm_ops),
|
||||
},
|
||||
.probe = rcar_i2c_probe,
|
||||
.remove_new = rcar_i2c_remove,
|
||||
.remove = rcar_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_i2c_driver);
|
||||
|
@ -632,7 +632,7 @@ static const struct of_device_id riic_i2c_dt_ids[] = {
|
||||
|
||||
static struct platform_driver riic_i2c_driver = {
|
||||
.probe = riic_i2c_probe,
|
||||
.remove_new = riic_i2c_remove,
|
||||
.remove = riic_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c-riic",
|
||||
.of_match_table = riic_i2c_dt_ids,
|
||||
|
@ -1398,7 +1398,7 @@ static SIMPLE_DEV_PM_OPS(rk3x_i2c_pm_ops, NULL, rk3x_i2c_resume);
|
||||
|
||||
static struct platform_driver rk3x_i2c_driver = {
|
||||
.probe = rk3x_i2c_probe,
|
||||
.remove_new = rk3x_i2c_remove,
|
||||
.remove = rk3x_i2c_remove,
|
||||
.driver = {
|
||||
.name = "rk3x-i2c",
|
||||
.of_match_table = rk3x_i2c_match,
|
||||
|
423
drivers/i2c/busses/i2c-rtl9300.c
Normal file
423
drivers/i2c/busses/i2c-rtl9300.c
Normal file
@ -0,0 +1,423 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
enum rtl9300_bus_freq {
|
||||
RTL9300_I2C_STD_FREQ,
|
||||
RTL9300_I2C_FAST_FREQ,
|
||||
};
|
||||
|
||||
struct rtl9300_i2c;
|
||||
|
||||
struct rtl9300_i2c_chan {
|
||||
struct i2c_adapter adap;
|
||||
struct rtl9300_i2c *i2c;
|
||||
enum rtl9300_bus_freq bus_freq;
|
||||
u8 sda_pin;
|
||||
};
|
||||
|
||||
#define RTL9300_I2C_MUX_NCHAN 8
|
||||
|
||||
struct rtl9300_i2c {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
|
||||
u32 reg_base;
|
||||
u8 sda_pin;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
#define RTL9300_I2C_MST_CTRL1 0x0
|
||||
#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS 8
|
||||
#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK GENMASK(31, 8)
|
||||
#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS 4
|
||||
#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK GENMASK(6, 4)
|
||||
#define RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL BIT(3)
|
||||
#define RTL9300_I2C_MST_CTRL1_RWOP BIT(2)
|
||||
#define RTL9300_I2C_MST_CTRL1_I2C_FAIL BIT(1)
|
||||
#define RTL9300_I2C_MST_CTRL1_I2C_TRIG BIT(0)
|
||||
#define RTL9300_I2C_MST_CTRL2 0x4
|
||||
#define RTL9300_I2C_MST_CTRL2_RD_MODE BIT(15)
|
||||
#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS 8
|
||||
#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK GENMASK(14, 8)
|
||||
#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS 4
|
||||
#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK GENMASK(7, 4)
|
||||
#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS 2
|
||||
#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK GENMASK(3, 2)
|
||||
#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS 0
|
||||
#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK GENMASK(1, 0)
|
||||
#define RTL9300_I2C_MST_DATA_WORD0 0x8
|
||||
#define RTL9300_I2C_MST_DATA_WORD1 0xc
|
||||
#define RTL9300_I2C_MST_DATA_WORD2 0x10
|
||||
#define RTL9300_I2C_MST_DATA_WORD3 0x14
|
||||
|
||||
#define RTL9300_I2C_MST_GLB_CTRL 0x384
|
||||
|
||||
static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
|
||||
{
|
||||
u32 val, mask;
|
||||
int ret;
|
||||
|
||||
val = len << RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS;
|
||||
mask = RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK;
|
||||
|
||||
ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = reg << RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS;
|
||||
mask = RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK;
|
||||
|
||||
return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
|
||||
}
|
||||
|
||||
static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
|
||||
{
|
||||
int ret;
|
||||
u32 val, mask;
|
||||
|
||||
ret = regmap_update_bits(i2c->regmap, RTL9300_I2C_MST_GLB_CTRL, BIT(sda_pin), BIT(sda_pin));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = (sda_pin << RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS) |
|
||||
RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
|
||||
mask = RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK | RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
|
||||
|
||||
return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
|
||||
}
|
||||
|
||||
static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
|
||||
u16 addr, u16 len)
|
||||
{
|
||||
u32 val, mask;
|
||||
|
||||
val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
|
||||
mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
|
||||
|
||||
val |= addr << RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS;
|
||||
mask |= RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK;
|
||||
|
||||
val |= ((len - 1) & 0xf) << RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS;
|
||||
mask |= RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK;
|
||||
|
||||
mask |= RTL9300_I2C_MST_CTRL2_RD_MODE;
|
||||
|
||||
return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
|
||||
}
|
||||
|
||||
static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
|
||||
{
|
||||
u32 vals[4] = {};
|
||||
int i, ret;
|
||||
|
||||
if (len > 16)
|
||||
return -EIO;
|
||||
|
||||
ret = regmap_bulk_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
|
||||
vals, ARRAY_SIZE(vals));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = vals[i/4] & 0xff;
|
||||
vals[i/4] >>= 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
|
||||
{
|
||||
u32 vals[4] = {};
|
||||
int i;
|
||||
|
||||
if (len > 16)
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i % 4 == 0)
|
||||
vals[i/4] = 0;
|
||||
vals[i/4] <<= 8;
|
||||
vals[i/4] |= buf[i];
|
||||
}
|
||||
|
||||
return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
|
||||
vals, ARRAY_SIZE(vals));
|
||||
}
|
||||
|
||||
static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
|
||||
{
|
||||
return regmap_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, data);
|
||||
}
|
||||
|
||||
static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
|
||||
int size, union i2c_smbus_data *data, int len)
|
||||
{
|
||||
u32 val, mask;
|
||||
int ret;
|
||||
|
||||
val = read_write == I2C_SMBUS_WRITE ? RTL9300_I2C_MST_CTRL1_RWOP : 0;
|
||||
mask = RTL9300_I2C_MST_CTRL1_RWOP;
|
||||
|
||||
val |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
|
||||
mask |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
|
||||
|
||||
ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
|
||||
val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 2000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & RTL9300_I2C_MST_CTRL1_I2C_FAIL)
|
||||
return -EIO;
|
||||
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) {
|
||||
ret = regmap_read(i2c->regmap,
|
||||
i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->byte = val & 0xff;
|
||||
} else if (size == I2C_SMBUS_WORD_DATA) {
|
||||
ret = regmap_read(i2c->regmap,
|
||||
i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->word = val & 0xffff;
|
||||
} else {
|
||||
ret = rtl9300_i2c_read(i2c, &data->block[0], len);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
||||
char read_write, u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
|
||||
struct rtl9300_i2c *i2c = chan->i2c;
|
||||
int len = 0, ret;
|
||||
|
||||
mutex_lock(&i2c->lock);
|
||||
if (chan->sda_pin != i2c->sda_pin) {
|
||||
ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
i2c->sda_pin = chan->sda_pin;
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BYTE:
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
} else {
|
||||
ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
ret = rtl9300_i2c_writel(i2c, data->byte);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 2);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
ret = rtl9300_i2c_writel(i2c, data->word);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
len = data->block[0];
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = rtl9300_i2c_execute_xfer(i2c, read_write, size, data, len);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&i2c->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 rtl9300_i2c_func(struct i2c_adapter *a)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm rtl9300_i2c_algo = {
|
||||
.smbus_xfer = rtl9300_i2c_smbus_xfer,
|
||||
.functionality = rtl9300_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter_quirks rtl9300_i2c_quirks = {
|
||||
.flags = I2C_AQ_NO_CLK_STRETCH,
|
||||
.max_read_len = 16,
|
||||
.max_write_len = 16,
|
||||
};
|
||||
|
||||
static int rtl9300_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rtl9300_i2c *i2c;
|
||||
u32 clock_freq, sda_pin;
|
||||
int ret, i = 0;
|
||||
struct fwnode_handle *child;
|
||||
|
||||
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c->regmap = syscon_node_to_regmap(dev->parent->of_node);
|
||||
if (IS_ERR(i2c->regmap))
|
||||
return PTR_ERR(i2c->regmap);
|
||||
i2c->dev = dev;
|
||||
|
||||
mutex_init(&i2c->lock);
|
||||
|
||||
ret = device_property_read_u32(dev, "reg", &i2c->reg_base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
|
||||
return dev_err_probe(dev, -EINVAL, "Too many channels\n");
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
struct rtl9300_i2c_chan *chan = &i2c->chans[i];
|
||||
struct i2c_adapter *adap = &chan->adap;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", &sda_pin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "clock-frequency", &clock_freq);
|
||||
if (ret)
|
||||
clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
|
||||
switch (clock_freq) {
|
||||
case I2C_MAX_STANDARD_MODE_FREQ:
|
||||
chan->bus_freq = RTL9300_I2C_STD_FREQ;
|
||||
break;
|
||||
|
||||
case I2C_MAX_FAST_MODE_FREQ:
|
||||
chan->bus_freq = RTL9300_I2C_FAST_FREQ;
|
||||
break;
|
||||
default:
|
||||
dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
|
||||
sda_pin, clock_freq);
|
||||
break;
|
||||
}
|
||||
|
||||
chan->sda_pin = sda_pin;
|
||||
chan->i2c = i2c;
|
||||
adap = &i2c->chans[i].adap;
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->algo = &rtl9300_i2c_algo;
|
||||
adap->quirks = &rtl9300_i2c_quirks;
|
||||
adap->retries = 3;
|
||||
adap->dev.parent = dev;
|
||||
i2c_set_adapdata(adap, chan);
|
||||
adap->dev.of_node = to_of_node(child);
|
||||
snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_pin);
|
||||
i++;
|
||||
|
||||
ret = devm_i2c_add_adapter(dev, adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
i2c->sda_pin = 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_rtl9300_dt_ids[] = {
|
||||
{ .compatible = "realtek,rtl9301-i2c" },
|
||||
{ .compatible = "realtek,rtl9302b-i2c" },
|
||||
{ .compatible = "realtek,rtl9302c-i2c" },
|
||||
{ .compatible = "realtek,rtl9303-i2c" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
|
||||
|
||||
static struct platform_driver rtl9300_i2c_driver = {
|
||||
.probe = rtl9300_i2c_probe,
|
||||
.driver = {
|
||||
.name = "i2c-rtl9300",
|
||||
.of_match_table = i2c_rtl9300_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rtl9300_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("RTL9300 I2C controller driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -536,7 +536,7 @@ static struct platform_driver rzv2m_i2c_driver = {
|
||||
.pm = pm_sleep_ptr(&rzv2m_i2c_pm_ops),
|
||||
},
|
||||
.probe = rzv2m_i2c_probe,
|
||||
.remove_new = rzv2m_i2c_remove,
|
||||
.remove = rzv2m_i2c_remove,
|
||||
};
|
||||
module_platform_driver(rzv2m_i2c_driver);
|
||||
|
||||
|
@ -1176,7 +1176,7 @@ static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
|
||||
|
||||
static struct platform_driver s3c24xx_i2c_driver = {
|
||||
.probe = s3c24xx_i2c_probe,
|
||||
.remove_new = s3c24xx_i2c_remove,
|
||||
.remove = s3c24xx_i2c_remove,
|
||||
.id_table = s3c24xx_driver_ids,
|
||||
.driver = {
|
||||
.name = "s3c-i2c",
|
||||
|
@ -411,7 +411,7 @@ static void smbus_cmi_remove(struct platform_device *device)
|
||||
|
||||
static struct platform_driver smbus_cmi_driver = {
|
||||
.probe = smbus_cmi_probe,
|
||||
.remove_new = smbus_cmi_remove,
|
||||
.remove = smbus_cmi_remove,
|
||||
.driver = {
|
||||
.name = "smbus_cmi",
|
||||
.acpi_match_table = acpi_smbus_cmi_ids,
|
||||
|
@ -552,7 +552,7 @@ static struct platform_driver sh7760_i2c_drv = {
|
||||
.name = SH7760_I2C_DEVNAME,
|
||||
},
|
||||
.probe = sh7760_i2c_probe,
|
||||
.remove_new = sh7760_i2c_remove,
|
||||
.remove = sh7760_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sh7760_i2c_drv);
|
||||
|
@ -983,7 +983,7 @@ static struct platform_driver sh_mobile_i2c_driver = {
|
||||
.pm = pm_sleep_ptr(&sh_mobile_i2c_pm_ops),
|
||||
},
|
||||
.probe = sh_mobile_i2c_probe,
|
||||
.remove_new = sh_mobile_i2c_remove,
|
||||
.remove = sh_mobile_i2c_remove,
|
||||
};
|
||||
|
||||
static int __init sh_mobile_i2c_adap_init(void)
|
||||
|
@ -144,7 +144,7 @@ static struct platform_driver simtec_i2c_driver = {
|
||||
.name = "simtec-i2c",
|
||||
},
|
||||
.probe = simtec_i2c_probe,
|
||||
.remove_new = simtec_i2c_remove,
|
||||
.remove = simtec_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(simtec_i2c_driver);
|
||||
|
@ -643,7 +643,7 @@ MODULE_DEVICE_TABLE(of, sprd_i2c_of_match);
|
||||
|
||||
static struct platform_driver sprd_i2c_driver = {
|
||||
.probe = sprd_i2c_probe,
|
||||
.remove_new = sprd_i2c_remove,
|
||||
.remove = sprd_i2c_remove,
|
||||
.driver = {
|
||||
.name = "sprd-i2c",
|
||||
.of_match_table = sprd_i2c_of_match,
|
||||
|
@ -893,7 +893,7 @@ static struct platform_driver st_i2c_driver = {
|
||||
.pm = pm_sleep_ptr(&st_i2c_pm),
|
||||
},
|
||||
.probe = st_i2c_probe,
|
||||
.remove_new = st_i2c_remove,
|
||||
.remove = st_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(st_i2c_driver);
|
||||
|
@ -869,7 +869,7 @@ static struct platform_driver stm32f4_i2c_driver = {
|
||||
.of_match_table = stm32f4_i2c_match,
|
||||
},
|
||||
.probe = stm32f4_i2c_probe,
|
||||
.remove_new = stm32f4_i2c_remove,
|
||||
.remove = stm32f4_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(stm32f4_i2c_driver);
|
||||
|
@ -2532,7 +2532,7 @@ static struct platform_driver stm32f7_i2c_driver = {
|
||||
.pm = &stm32f7_i2c_pm_ops,
|
||||
},
|
||||
.probe = stm32f7_i2c_probe,
|
||||
.remove_new = stm32f7_i2c_remove,
|
||||
.remove = stm32f7_i2c_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(stm32f7_i2c_driver);
|
||||
|
@ -319,7 +319,7 @@ static void p2wi_remove(struct platform_device *dev)
|
||||
|
||||
static struct platform_driver p2wi_driver = {
|
||||
.probe = p2wi_probe,
|
||||
.remove_new = p2wi_remove,
|
||||
.remove = p2wi_remove,
|
||||
.driver = {
|
||||
.name = "i2c-sunxi-p2wi",
|
||||
.of_match_table = p2wi_of_match_table,
|
||||
|
@ -629,7 +629,7 @@ MODULE_DEVICE_TABLE(acpi, synquacer_i2c_acpi_ids);
|
||||
|
||||
static struct platform_driver synquacer_i2c_driver = {
|
||||
.probe = synquacer_i2c_probe,
|
||||
.remove_new = synquacer_i2c_remove,
|
||||
.remove = synquacer_i2c_remove,
|
||||
.driver = {
|
||||
.name = "synquacer_i2c",
|
||||
.of_match_table = of_match_ptr(synquacer_i2c_dt_ids),
|
||||
|
@ -335,7 +335,7 @@ static struct platform_driver tegra_bpmp_i2c_driver = {
|
||||
.of_match_table = tegra_bpmp_i2c_of_match,
|
||||
},
|
||||
.probe = tegra_bpmp_i2c_probe,
|
||||
.remove_new = tegra_bpmp_i2c_remove,
|
||||
.remove = tegra_bpmp_i2c_remove,
|
||||
};
|
||||
module_platform_driver(tegra_bpmp_i2c_driver);
|
||||
|
||||
|
@ -1964,7 +1964,7 @@ MODULE_DEVICE_TABLE(acpi, tegra_i2c_acpi_match);
|
||||
|
||||
static struct platform_driver tegra_i2c_driver = {
|
||||
.probe = tegra_i2c_probe,
|
||||
.remove_new = tegra_i2c_remove,
|
||||
.remove = tegra_i2c_remove,
|
||||
.driver = {
|
||||
.name = "tegra-i2c",
|
||||
.of_match_table = tegra_i2c_of_match,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user