Merge branch 'i2c/for-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - the core has now a lockless variant of i2c_smbus_xfer. Some open coded versions of this got removed in drivers. This also enables proper SCCB support in regmap. - locking got a more precise naming. i2c_{un}lock_adapter() had to go, and we know use i2c_lock_bus() consistently with flags like I2C_LOCK_ROOT_ADAPTER and I2C_LOCK_SEGMENT to avoid ambiguity. - the gpio fault injector got a new delicate testcase - the bus recovery procedure got fixed to handle the new testcase correctly - a new quirk flag for controllers not able to handle zero length messages together with driver updates to use it - new drivers: FSI bus attached I2C masters, GENI I2C controller, Owl family S900 - and a good set of driver improvements and bugfixes * 'i2c/for-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (77 commits) i2c: rcar: implement STOP and REP_START according to docs i2c: rcar: refactor private flags i2c: core: ACPI: Make acpi_gsb_i2c_read_bytes() check i2c_transfer return value i2c: core: ACPI: Properly set status byte to 0 for multi-byte writes dt-bindings: i2c: rcar: Add r8a774a1 support dt-bindings: i2c: sh_mobile: Add r8a774a1 support i2c: imx: Simplify stopped state tracking i2c: imx: Fix race condition in dma read i2c: pasemi: remove hardcoded bus numbers on smbus i2c: designware: Add SPDX license tag i2c: designware: Convert to use struct i2c_timings i2c: core: Parse SDA hold time from firmware i2c: designware-pcidrv: Mark expected switch fall-through i2c: amd8111: Mark expected switch fall-through i2c: sh_mobile: use core to detect 'no zero length read' quirk i2c: xlr: use core to detect 'no zero length' quirk i2c: rcar: use core to detect 'no zero length' quirk i2c: stu300: use core to detect 'no zero length' quirk i2c: pmcmsp: use core to detect 'no zero length' quirk i2c: mxs: use core to detect 'no zero length' quirk ...
This commit is contained in:
commit
99cc7ad46b
|
@ -72,6 +72,8 @@ Optional properties:
|
||||||
|
|
||||||
- wp-gpios: GPIO to which the write-protect pin of the chip is connected.
|
- wp-gpios: GPIO to which the write-protect pin of the chip is connected.
|
||||||
|
|
||||||
|
- address-width: number of address bits (one of 8, 16).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
eeprom@52 {
|
eeprom@52 {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
Device-tree bindings for FSI-attached I2C master and busses
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible = "ibm,i2c-fsi";
|
||||||
|
- reg = < address size >; : The FSI CFAM address and address
|
||||||
|
space size.
|
||||||
|
- #address-cells = <1>; : Number of address cells in child
|
||||||
|
nodes.
|
||||||
|
- #size-cells = <0>; : Number of size cells in child nodes.
|
||||||
|
- child nodes : Nodes to describe busses off the I2C
|
||||||
|
master.
|
||||||
|
|
||||||
|
Child node required properties:
|
||||||
|
- reg = < port number > : The port number on the I2C master.
|
||||||
|
|
||||||
|
Child node optional properties:
|
||||||
|
- child nodes : Nodes to describe devices on the I2C
|
||||||
|
bus.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
i2c@1800 {
|
||||||
|
compatible = "ibm,i2c-fsi";
|
||||||
|
reg = < 0x1800 0x400 >;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
i2c-bus@0 {
|
||||||
|
reg = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c-bus@1 {
|
||||||
|
reg = <1>;
|
||||||
|
|
||||||
|
eeprom@50 {
|
||||||
|
compatible = "vendor,dev-name";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
Actions Semiconductor Owl I2C controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible : Should be "actions,s900-i2c".
|
||||||
|
- reg : Offset and length of the register set for the device.
|
||||||
|
- #address-cells : Should be 1.
|
||||||
|
- #size-cells : Should be 0.
|
||||||
|
- interrupts : A single interrupt specifier.
|
||||||
|
- clocks : Phandle of the clock feeding the I2C controller.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
- clock-frequency : Desired I2C bus clock frequency in Hz. As only Normal and
|
||||||
|
Fast modes are supported, possible values are 100000 and
|
||||||
|
400000.
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
i2c0: i2c@e0170000 {
|
||||||
|
compatible = "actions,s900-i2c";
|
||||||
|
reg = <0 0xe0170000 0 0x1000>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&clock CLK_I2C0>;
|
||||||
|
clock-frequency = <100000>;
|
||||||
|
};
|
|
@ -4,6 +4,7 @@ Required properties:
|
||||||
- compatible:
|
- compatible:
|
||||||
"renesas,i2c-r8a7743" if the device is a part of a R8A7743 SoC.
|
"renesas,i2c-r8a7743" if the device is a part of a R8A7743 SoC.
|
||||||
"renesas,i2c-r8a7745" if the device is a part of a R8A7745 SoC.
|
"renesas,i2c-r8a7745" if the device is a part of a R8A7745 SoC.
|
||||||
|
"renesas,i2c-r8a774a1" if the device is a part of a R8A774A1 SoC.
|
||||||
"renesas,i2c-r8a7778" if the device is a part of a R8A7778 SoC.
|
"renesas,i2c-r8a7778" if the device is a part of a R8A7778 SoC.
|
||||||
"renesas,i2c-r8a7779" if the device is a part of a R8A7779 SoC.
|
"renesas,i2c-r8a7779" if the device is a part of a R8A7779 SoC.
|
||||||
"renesas,i2c-r8a7790" if the device is a part of a R8A7790 SoC.
|
"renesas,i2c-r8a7790" if the device is a part of a R8A7790 SoC.
|
||||||
|
@ -16,11 +17,13 @@ Required properties:
|
||||||
"renesas,i2c-r8a77965" if the device is a part of a R8A77965 SoC.
|
"renesas,i2c-r8a77965" if the device is a part of a R8A77965 SoC.
|
||||||
"renesas,i2c-r8a77970" if the device is a part of a R8A77970 SoC.
|
"renesas,i2c-r8a77970" if the device is a part of a R8A77970 SoC.
|
||||||
"renesas,i2c-r8a77980" if the device is a part of a R8A77980 SoC.
|
"renesas,i2c-r8a77980" if the device is a part of a R8A77980 SoC.
|
||||||
|
"renesas,i2c-r8a77990" if the device is a part of a R8A77990 SoC.
|
||||||
"renesas,i2c-r8a77995" if the device is a part of a R8A77995 SoC.
|
"renesas,i2c-r8a77995" if the device is a part of a R8A77995 SoC.
|
||||||
"renesas,rcar-gen1-i2c" for a generic R-Car Gen1 compatible device.
|
"renesas,rcar-gen1-i2c" for a generic R-Car Gen1 compatible device.
|
||||||
"renesas,rcar-gen2-i2c" for a generic R-Car Gen2 or RZ/G1 compatible
|
"renesas,rcar-gen2-i2c" for a generic R-Car Gen2 or RZ/G1 compatible
|
||||||
device.
|
device.
|
||||||
"renesas,rcar-gen3-i2c" for a generic R-Car Gen3 compatible device.
|
"renesas,rcar-gen3-i2c" for a generic R-Car Gen3 or RZ/G2 compatible
|
||||||
|
device.
|
||||||
"renesas,i2c-rcar" (deprecated)
|
"renesas,i2c-rcar" (deprecated)
|
||||||
|
|
||||||
When compatible with the generic version, nodes must list the
|
When compatible with the generic version, nodes must list the
|
||||||
|
|
|
@ -6,6 +6,7 @@ Required properties:
|
||||||
- "renesas,iic-r8a7740" (R-Mobile A1)
|
- "renesas,iic-r8a7740" (R-Mobile A1)
|
||||||
- "renesas,iic-r8a7743" (RZ/G1M)
|
- "renesas,iic-r8a7743" (RZ/G1M)
|
||||||
- "renesas,iic-r8a7745" (RZ/G1E)
|
- "renesas,iic-r8a7745" (RZ/G1E)
|
||||||
|
- "renesas,iic-r8a774a1" (RZ/G2M)
|
||||||
- "renesas,iic-r8a7790" (R-Car H2)
|
- "renesas,iic-r8a7790" (R-Car H2)
|
||||||
- "renesas,iic-r8a7791" (R-Car M2-W)
|
- "renesas,iic-r8a7791" (R-Car M2-W)
|
||||||
- "renesas,iic-r8a7792" (R-Car V2H)
|
- "renesas,iic-r8a7792" (R-Car V2H)
|
||||||
|
@ -17,7 +18,8 @@ Required properties:
|
||||||
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
||||||
- "renesas,rcar-gen2-iic" (generic R-Car Gen2 or RZ/G1
|
- "renesas,rcar-gen2-iic" (generic R-Car Gen2 or RZ/G1
|
||||||
compatible device)
|
compatible device)
|
||||||
- "renesas,rcar-gen3-iic" (generic R-Car Gen3 compatible device)
|
- "renesas,rcar-gen3-iic" (generic R-Car Gen3 or RZ/G2
|
||||||
|
compatible device)
|
||||||
- "renesas,rmobile-iic" (generic device)
|
- "renesas,rmobile-iic" (generic device)
|
||||||
|
|
||||||
When compatible with a generic R-Car version, nodes
|
When compatible with a generic R-Car version, nodes
|
||||||
|
|
|
@ -21,24 +21,21 @@ Supported adapters:
|
||||||
* Intel DH89xxCC (PCH)
|
* Intel DH89xxCC (PCH)
|
||||||
* Intel Panther Point (PCH)
|
* Intel Panther Point (PCH)
|
||||||
* Intel Lynx Point (PCH)
|
* Intel Lynx Point (PCH)
|
||||||
* Intel Lynx Point-LP (PCH)
|
|
||||||
* Intel Avoton (SOC)
|
* Intel Avoton (SOC)
|
||||||
* Intel Wellsburg (PCH)
|
* Intel Wellsburg (PCH)
|
||||||
* Intel Coleto Creek (PCH)
|
* Intel Coleto Creek (PCH)
|
||||||
* Intel Wildcat Point (PCH)
|
* Intel Wildcat Point (PCH)
|
||||||
* Intel Wildcat Point-LP (PCH)
|
|
||||||
* Intel BayTrail (SOC)
|
* Intel BayTrail (SOC)
|
||||||
* Intel Braswell (SOC)
|
* Intel Braswell (SOC)
|
||||||
* Intel Sunrise Point-H (PCH)
|
* Intel Sunrise Point (PCH)
|
||||||
* Intel Sunrise Point-LP (PCH)
|
* Intel Kaby Lake (PCH)
|
||||||
* Intel Kaby Lake-H (PCH)
|
|
||||||
* Intel DNV (SOC)
|
* Intel DNV (SOC)
|
||||||
* Intel Broxton (SOC)
|
* Intel Broxton (SOC)
|
||||||
* Intel Lewisburg (PCH)
|
* Intel Lewisburg (PCH)
|
||||||
* Intel Gemini Lake (SOC)
|
* Intel Gemini Lake (SOC)
|
||||||
* Intel Cannon Lake-H (PCH)
|
* Intel Cannon Lake (PCH)
|
||||||
* Intel Cannon Lake-LP (PCH)
|
|
||||||
* Intel Cedar Fork (PCH)
|
* Intel Cedar Fork (PCH)
|
||||||
|
* Intel Ice Lake (PCH)
|
||||||
Datasheets: Publicly available at the Intel website
|
Datasheets: Publicly available at the Intel website
|
||||||
|
|
||||||
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
On Intel Patsburg and later chipsets, both the normal host SMBus controller
|
||||||
|
|
|
@ -34,21 +34,48 @@ I2C specification version 4, section 3.1.16) using the helpers of the Linux I2C
|
||||||
core (see 'struct bus_recovery_info'). However, the bus recovery will not
|
core (see 'struct bus_recovery_info'). However, the bus recovery will not
|
||||||
succeed because SDA is still pinned low until you manually release it again
|
succeed because SDA is still pinned low until you manually release it again
|
||||||
with "echo 1 > sda". A test with an automatic release can be done with the
|
with "echo 1 > sda". A test with an automatic release can be done with the
|
||||||
'incomplete_transfer' file.
|
following class of fault injectors.
|
||||||
|
|
||||||
"incomplete_transfer"
|
Introduction to incomplete transfers
|
||||||
---------------------
|
------------------------------------
|
||||||
|
|
||||||
|
The following fault injectors create situations where SDA will be held low by a
|
||||||
|
device. Bus recovery should be able to fix these situations. But please note:
|
||||||
|
there are I2C client devices which detect a stuck SDA on their side and release
|
||||||
|
it on their own after a few milliseconds. Also, there might be an external
|
||||||
|
device deglitching and monitoring the I2C bus. It could also detect a stuck SDA
|
||||||
|
and will init a bus recovery on its own. If you want to implement bus recovery
|
||||||
|
in a bus master driver, make sure you checked your hardware setup for such
|
||||||
|
devices before. And always verify with a scope or logic analyzer!
|
||||||
|
|
||||||
|
"incomplete_address_phase"
|
||||||
|
--------------------------
|
||||||
|
|
||||||
This file is write only and you need to write the address of an existing I2C
|
This file is write only and you need to write the address of an existing I2C
|
||||||
client device to it. Then, a transfer to this device will be started, but it
|
client device to it. Then, a read transfer to this device will be started, but
|
||||||
will stop at the ACK phase after the address of the client has been
|
it will stop at the ACK phase after the address of the client has been
|
||||||
transmitted. Because the device will ACK its presence, this results in SDA
|
transmitted. Because the device will ACK its presence, this results in SDA
|
||||||
being pulled low by the device while SCL is high. So, similar to the "sda" file
|
being pulled low by the device while SCL is high. So, similar to the "sda" file
|
||||||
above, the bus master under test should detect this condition and try a bus
|
above, the bus master under test should detect this condition and try a bus
|
||||||
recovery. This time, however, it should succeed and the device should release
|
recovery. This time, however, it should succeed and the device should release
|
||||||
SDA after toggling SCL. Please note: there are I2C client devices which detect
|
SDA after toggling SCL.
|
||||||
a stuck SDA on their side and release it on their own after a few milliseconds.
|
|
||||||
Also, there are external devices deglitching and monitoring the I2C bus. They
|
"incomplete_write_byte"
|
||||||
can also detect a stuck SDA and will init a bus recovery on their own. If you
|
-----------------------
|
||||||
want to implement bus recovery in a bus master driver, make sure you checked
|
|
||||||
your hardware setup carefully before.
|
Similar to above, this file is write only and you need to write the address of
|
||||||
|
an existing I2C client device to it.
|
||||||
|
|
||||||
|
The injector will again stop at one ACK phase, so the device will keep SDA low
|
||||||
|
because it acknowledges data. However, there are two differences compared to
|
||||||
|
'incomplete_address_phase':
|
||||||
|
|
||||||
|
a) the message sent out will be a write message
|
||||||
|
b) after the address byte, a 0x00 byte will be transferred. Then, stop at ACK.
|
||||||
|
|
||||||
|
This is a highly delicate state, the device is set up to write any data to
|
||||||
|
register 0x00 (if it has registers) when further clock pulses happen on SCL.
|
||||||
|
This is why bus recovery (up to 9 clock pulses) must either check SDA or send
|
||||||
|
additional STOP conditions to ensure the bus has been released. Otherwise
|
||||||
|
random data will be written to a device!
|
||||||
|
|
||||||
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -5961,6 +5961,14 @@ F: fs/crypto/
|
||||||
F: include/linux/fscrypt*.h
|
F: include/linux/fscrypt*.h
|
||||||
F: Documentation/filesystems/fscrypt.rst
|
F: Documentation/filesystems/fscrypt.rst
|
||||||
|
|
||||||
|
FSI-ATTACHED I2C DRIVER
|
||||||
|
M: Eddie James <eajames@linux.vnet.ibm.com>
|
||||||
|
L: linux-i2c@vger.kernel.org
|
||||||
|
L: openbmc@lists.ozlabs.org (moderated for non-subscribers)
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/i2c/busses/i2c-fsi.c
|
||||||
|
F: Documentation/devicetree/bindings/i2c/i2c-fsi.txt
|
||||||
|
|
||||||
FSNOTIFY: FILESYSTEM NOTIFICATION INFRASTRUCTURE
|
FSNOTIFY: FILESYSTEM NOTIFICATION INFRASTRUCTURE
|
||||||
M: Jan Kara <jack@suse.cz>
|
M: Jan Kara <jack@suse.cz>
|
||||||
R: Amir Goldstein <amir73il@gmail.com>
|
R: Amir Goldstein <amir73il@gmail.com>
|
||||||
|
@ -12014,6 +12022,14 @@ L: netdev@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/net/ethernet/qualcomm/emac/
|
F: drivers/net/ethernet/qualcomm/emac/
|
||||||
|
|
||||||
|
QUALCOMM GENERIC INTERFACE I2C DRIVER
|
||||||
|
M: Alok Chauhan <alokc@codeaurora.org>
|
||||||
|
M: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
|
||||||
|
L: linux-i2c@vger.kernel.org
|
||||||
|
L: linux-arm-msm@vger.kernel.org
|
||||||
|
S: Supported
|
||||||
|
F: drivers/i2c/busses/i2c-qcom-geni.c
|
||||||
|
|
||||||
QUALCOMM HEXAGON ARCHITECTURE
|
QUALCOMM HEXAGON ARCHITECTURE
|
||||||
M: Richard Kuo <rkuo@codeaurora.org>
|
M: Richard Kuo <rkuo@codeaurora.org>
|
||||||
L: linux-hexagon@vger.kernel.org
|
L: linux-hexagon@vger.kernel.org
|
||||||
|
|
|
@ -116,24 +116,21 @@ config I2C_I801
|
||||||
DH89xxCC (PCH)
|
DH89xxCC (PCH)
|
||||||
Panther Point (PCH)
|
Panther Point (PCH)
|
||||||
Lynx Point (PCH)
|
Lynx Point (PCH)
|
||||||
Lynx Point-LP (PCH)
|
|
||||||
Avoton (SOC)
|
Avoton (SOC)
|
||||||
Wellsburg (PCH)
|
Wellsburg (PCH)
|
||||||
Coleto Creek (PCH)
|
Coleto Creek (PCH)
|
||||||
Wildcat Point (PCH)
|
Wildcat Point (PCH)
|
||||||
Wildcat Point-LP (PCH)
|
|
||||||
BayTrail (SOC)
|
BayTrail (SOC)
|
||||||
Braswell (SOC)
|
Braswell (SOC)
|
||||||
Sunrise Point-H (PCH)
|
Sunrise Point (PCH)
|
||||||
Sunrise Point-LP (PCH)
|
Kaby Lake (PCH)
|
||||||
Kaby Lake-H (PCH)
|
|
||||||
DNV (SOC)
|
DNV (SOC)
|
||||||
Broxton (SOC)
|
Broxton (SOC)
|
||||||
Lewisburg (PCH)
|
Lewisburg (PCH)
|
||||||
Gemini Lake (SOC)
|
Gemini Lake (SOC)
|
||||||
Cannon Lake-H (PCH)
|
Cannon Lake (PCH)
|
||||||
Cannon Lake-LP (PCH)
|
|
||||||
Cedar Fork (PCH)
|
Cedar Fork (PCH)
|
||||||
|
Ice Lake (PCH)
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-i801.
|
will be called i2c-i801.
|
||||||
|
@ -762,6 +759,13 @@ config I2C_OMAP
|
||||||
Like OMAP1510/1610/1710/5912 and OMAP242x.
|
Like OMAP1510/1610/1710/5912 and OMAP242x.
|
||||||
For details see http://www.ti.com/omap.
|
For details see http://www.ti.com/omap.
|
||||||
|
|
||||||
|
config I2C_OWL
|
||||||
|
tristate "Actions Semiconductor Owl I2C Controller"
|
||||||
|
depends on ARCH_ACTIONS || COMPILE_TEST
|
||||||
|
help
|
||||||
|
Say Y here if you want to use the I2C bus controller on
|
||||||
|
the Actions Semiconductor Owl SoC's.
|
||||||
|
|
||||||
config I2C_PASEMI
|
config I2C_PASEMI
|
||||||
tristate "PA Semi SMBus interface"
|
tristate "PA Semi SMBus interface"
|
||||||
depends on PPC_PASEMI && PCI
|
depends on PPC_PASEMI && PCI
|
||||||
|
@ -828,6 +832,19 @@ config I2C_PXA_SLAVE
|
||||||
is necessary for systems where the PXA may be a target on the
|
is necessary for systems where the PXA may be a target on the
|
||||||
I2C bus.
|
I2C bus.
|
||||||
|
|
||||||
|
config I2C_QCOM_GENI
|
||||||
|
tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
|
||||||
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
|
depends on QCOM_GENI_SE
|
||||||
|
help
|
||||||
|
This driver supports GENI serial engine based I2C controller in
|
||||||
|
master mode on the Qualcomm Technologies Inc.'s SoCs. If you say
|
||||||
|
yes to this option, support will be included for the built-in I2C
|
||||||
|
interface on the Qualcomm Technologies Inc.'s SoCs.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called i2c-qcom-geni.
|
||||||
|
|
||||||
config I2C_QUP
|
config I2C_QUP
|
||||||
tristate "Qualcomm QUP based I2C controller"
|
tristate "Qualcomm QUP based I2C controller"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM
|
||||||
|
@ -1330,4 +1347,15 @@ config I2C_ZX2967
|
||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
called i2c-zx2967.
|
called i2c-zx2967.
|
||||||
|
|
||||||
|
config I2C_FSI
|
||||||
|
tristate "FSI I2C driver"
|
||||||
|
depends on FSI
|
||||||
|
help
|
||||||
|
Driver for FSI bus attached I2C masters. These are I2C masters that
|
||||||
|
are connected to the system over an FSI bus, instead of the more
|
||||||
|
common PCI or MMIO interface.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will be
|
||||||
|
called as i2c-fsi.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -76,6 +76,7 @@ obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
|
||||||
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
|
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
|
||||||
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
|
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
|
||||||
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
|
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
|
||||||
|
obj-$(CONFIG_I2C_OWL) += i2c-owl.o
|
||||||
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
|
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
|
||||||
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
|
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
|
||||||
obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
|
obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
|
||||||
|
@ -83,6 +84,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
|
||||||
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
|
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
|
||||||
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
|
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
|
||||||
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
|
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
|
||||||
|
obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o
|
||||||
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
|
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
|
||||||
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
|
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
|
||||||
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
|
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
|
||||||
|
@ -137,5 +139,6 @@ obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
||||||
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
|
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
|
||||||
obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
|
obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
|
||||||
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
|
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
|
||||||
|
obj-$(CONFIG_I2C_FSI) += i2c-fsi.o
|
||||||
|
|
||||||
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
||||||
|
|
|
@ -384,6 +384,7 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
|
||||||
if (status)
|
if (status)
|
||||||
return status;
|
return status;
|
||||||
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
|
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
|
||||||
|
/* fall through */
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
status = amd_ec_read(smbus, AMD_SMB_DATA + i,
|
status = amd_ec_read(smbus, AMD_SMB_DATA + i,
|
||||||
|
|
|
@ -111,22 +111,22 @@
|
||||||
#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
|
#define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0)
|
||||||
|
|
||||||
enum aspeed_i2c_master_state {
|
enum aspeed_i2c_master_state {
|
||||||
|
ASPEED_I2C_MASTER_INACTIVE,
|
||||||
ASPEED_I2C_MASTER_START,
|
ASPEED_I2C_MASTER_START,
|
||||||
ASPEED_I2C_MASTER_TX_FIRST,
|
ASPEED_I2C_MASTER_TX_FIRST,
|
||||||
ASPEED_I2C_MASTER_TX,
|
ASPEED_I2C_MASTER_TX,
|
||||||
ASPEED_I2C_MASTER_RX_FIRST,
|
ASPEED_I2C_MASTER_RX_FIRST,
|
||||||
ASPEED_I2C_MASTER_RX,
|
ASPEED_I2C_MASTER_RX,
|
||||||
ASPEED_I2C_MASTER_STOP,
|
ASPEED_I2C_MASTER_STOP,
|
||||||
ASPEED_I2C_MASTER_INACTIVE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum aspeed_i2c_slave_state {
|
enum aspeed_i2c_slave_state {
|
||||||
|
ASPEED_I2C_SLAVE_STOP,
|
||||||
ASPEED_I2C_SLAVE_START,
|
ASPEED_I2C_SLAVE_START,
|
||||||
ASPEED_I2C_SLAVE_READ_REQUESTED,
|
ASPEED_I2C_SLAVE_READ_REQUESTED,
|
||||||
ASPEED_I2C_SLAVE_READ_PROCESSED,
|
ASPEED_I2C_SLAVE_READ_PROCESSED,
|
||||||
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
|
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
|
||||||
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
|
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
|
||||||
ASPEED_I2C_SLAVE_STOP,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct aspeed_i2c_bus {
|
struct aspeed_i2c_bus {
|
||||||
|
@ -234,7 +234,6 @@ static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
|
||||||
bool irq_handled = true;
|
bool irq_handled = true;
|
||||||
u8 value;
|
u8 value;
|
||||||
|
|
||||||
spin_lock(&bus->lock);
|
|
||||||
if (!slave) {
|
if (!slave) {
|
||||||
irq_handled = false;
|
irq_handled = false;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -325,7 +324,6 @@ static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus)
|
||||||
writel(status_ack, bus->base + ASPEED_I2C_INTR_STS_REG);
|
writel(status_ack, bus->base + ASPEED_I2C_INTR_STS_REG);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock(&bus->lock);
|
|
||||||
return irq_handled;
|
return irq_handled;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_I2C_SLAVE */
|
#endif /* CONFIG_I2C_SLAVE */
|
||||||
|
@ -389,7 +387,6 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
u8 recv_byte;
|
u8 recv_byte;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock(&bus->lock);
|
|
||||||
irq_status = readl(bus->base + ASPEED_I2C_INTR_STS_REG);
|
irq_status = readl(bus->base + ASPEED_I2C_INTR_STS_REG);
|
||||||
/* Ack all interrupt bits. */
|
/* Ack all interrupt bits. */
|
||||||
writel(irq_status, bus->base + ASPEED_I2C_INTR_STS_REG);
|
writel(irq_status, bus->base + ASPEED_I2C_INTR_STS_REG);
|
||||||
|
@ -407,7 +404,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
*/
|
*/
|
||||||
ret = aspeed_i2c_is_irq_error(irq_status);
|
ret = aspeed_i2c_is_irq_error(irq_status);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_dbg(bus->dev, "received error interrupt: 0x%08x",
|
dev_dbg(bus->dev, "received error interrupt: 0x%08x\n",
|
||||||
irq_status);
|
irq_status);
|
||||||
bus->cmd_err = ret;
|
bus->cmd_err = ret;
|
||||||
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
|
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
|
||||||
|
@ -416,7 +413,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
|
|
||||||
/* We are in an invalid state; reset bus to a known state. */
|
/* We are in an invalid state; reset bus to a known state. */
|
||||||
if (!bus->msgs) {
|
if (!bus->msgs) {
|
||||||
dev_err(bus->dev, "bus in unknown state");
|
dev_err(bus->dev, "bus in unknown state\n");
|
||||||
bus->cmd_err = -EIO;
|
bus->cmd_err = -EIO;
|
||||||
if (bus->master_state != ASPEED_I2C_MASTER_STOP)
|
if (bus->master_state != ASPEED_I2C_MASTER_STOP)
|
||||||
aspeed_i2c_do_stop(bus);
|
aspeed_i2c_do_stop(bus);
|
||||||
|
@ -431,7 +428,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
*/
|
*/
|
||||||
if (bus->master_state == ASPEED_I2C_MASTER_START) {
|
if (bus->master_state == ASPEED_I2C_MASTER_START) {
|
||||||
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
|
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
|
||||||
pr_devel("no slave present at %02x", msg->addr);
|
pr_devel("no slave present at %02x\n", msg->addr);
|
||||||
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
|
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
|
||||||
bus->cmd_err = -ENXIO;
|
bus->cmd_err = -ENXIO;
|
||||||
aspeed_i2c_do_stop(bus);
|
aspeed_i2c_do_stop(bus);
|
||||||
|
@ -451,11 +448,11 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
switch (bus->master_state) {
|
switch (bus->master_state) {
|
||||||
case ASPEED_I2C_MASTER_TX:
|
case ASPEED_I2C_MASTER_TX:
|
||||||
if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_NAK)) {
|
if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_NAK)) {
|
||||||
dev_dbg(bus->dev, "slave NACKed TX");
|
dev_dbg(bus->dev, "slave NACKed TX\n");
|
||||||
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
|
status_ack |= ASPEED_I2CD_INTR_TX_NAK;
|
||||||
goto error_and_stop;
|
goto error_and_stop;
|
||||||
} else if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
|
} else if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
|
||||||
dev_err(bus->dev, "slave failed to ACK TX");
|
dev_err(bus->dev, "slave failed to ACK TX\n");
|
||||||
goto error_and_stop;
|
goto error_and_stop;
|
||||||
}
|
}
|
||||||
status_ack |= ASPEED_I2CD_INTR_TX_ACK;
|
status_ack |= ASPEED_I2CD_INTR_TX_ACK;
|
||||||
|
@ -478,7 +475,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
/* fallthrough intended */
|
/* fallthrough intended */
|
||||||
case ASPEED_I2C_MASTER_RX:
|
case ASPEED_I2C_MASTER_RX:
|
||||||
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) {
|
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_RX_DONE))) {
|
||||||
dev_err(bus->dev, "master failed to RX");
|
dev_err(bus->dev, "master failed to RX\n");
|
||||||
goto error_and_stop;
|
goto error_and_stop;
|
||||||
}
|
}
|
||||||
status_ack |= ASPEED_I2CD_INTR_RX_DONE;
|
status_ack |= ASPEED_I2CD_INTR_RX_DONE;
|
||||||
|
@ -509,7 +506,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
goto out_no_complete;
|
goto out_no_complete;
|
||||||
case ASPEED_I2C_MASTER_STOP:
|
case ASPEED_I2C_MASTER_STOP:
|
||||||
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))) {
|
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_NORMAL_STOP))) {
|
||||||
dev_err(bus->dev, "master failed to STOP");
|
dev_err(bus->dev, "master failed to STOP\n");
|
||||||
bus->cmd_err = -EIO;
|
bus->cmd_err = -EIO;
|
||||||
/* Do not STOP as we have already tried. */
|
/* Do not STOP as we have already tried. */
|
||||||
} else {
|
} else {
|
||||||
|
@ -520,7 +517,7 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus)
|
||||||
goto out_complete;
|
goto out_complete;
|
||||||
case ASPEED_I2C_MASTER_INACTIVE:
|
case ASPEED_I2C_MASTER_INACTIVE:
|
||||||
dev_err(bus->dev,
|
dev_err(bus->dev,
|
||||||
"master received interrupt 0x%08x, but is inactive",
|
"master received interrupt 0x%08x, but is inactive\n",
|
||||||
irq_status);
|
irq_status);
|
||||||
bus->cmd_err = -EIO;
|
bus->cmd_err = -EIO;
|
||||||
/* Do not STOP as we should be inactive. */
|
/* Do not STOP as we should be inactive. */
|
||||||
|
@ -547,22 +544,29 @@ out_no_complete:
|
||||||
dev_err(bus->dev,
|
dev_err(bus->dev,
|
||||||
"irq handled != irq. expected 0x%08x, but was 0x%08x\n",
|
"irq handled != irq. expected 0x%08x, but was 0x%08x\n",
|
||||||
irq_status, status_ack);
|
irq_status, status_ack);
|
||||||
spin_unlock(&bus->lock);
|
|
||||||
return !!irq_status;
|
return !!irq_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
|
static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct aspeed_i2c_bus *bus = dev_id;
|
struct aspeed_i2c_bus *bus = dev_id;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
spin_lock(&bus->lock);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||||
if (aspeed_i2c_slave_irq(bus)) {
|
if (aspeed_i2c_slave_irq(bus)) {
|
||||||
dev_dbg(bus->dev, "irq handled by slave.\n");
|
dev_dbg(bus->dev, "irq handled by slave.\n");
|
||||||
return IRQ_HANDLED;
|
ret = true;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_I2C_SLAVE */
|
#endif /* CONFIG_I2C_SLAVE */
|
||||||
|
|
||||||
return aspeed_i2c_master_irq(bus) ? IRQ_HANDLED : IRQ_NONE;
|
ret = aspeed_i2c_master_irq(bus);
|
||||||
|
|
||||||
|
out:
|
||||||
|
spin_unlock(&bus->lock);
|
||||||
|
return ret ? IRQ_HANDLED : IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
|
static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
|
||||||
|
@ -851,7 +855,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
|
||||||
bus->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
|
bus->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
|
||||||
if (IS_ERR(bus->rst)) {
|
if (IS_ERR(bus->rst)) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"missing or invalid reset controller device tree entry");
|
"missing or invalid reset controller device tree entry\n");
|
||||||
return PTR_ERR(bus->rst);
|
return PTR_ERR(bus->rst);
|
||||||
}
|
}
|
||||||
reset_control_deassert(bus->rst);
|
reset_control_deassert(bus->rst);
|
||||||
|
@ -868,7 +872,7 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
|
||||||
if (!match)
|
if (!match)
|
||||||
bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val;
|
bus->get_clk_reg_val = aspeed_i2c_24xx_get_clk_reg_val;
|
||||||
else
|
else
|
||||||
bus->get_clk_reg_val = match->data;
|
bus->get_clk_reg_val = (u32 (*)(u32))match->data;
|
||||||
|
|
||||||
/* Initialize the I2C adapter */
|
/* Initialize the I2C adapter */
|
||||||
spin_lock_init(&bus->lock);
|
spin_lock_init(&bus->lock);
|
||||||
|
|
|
@ -689,9 +689,9 @@ static int brcmstb_i2c_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
i2c_lock_adapter(&i2c_dev->adapter);
|
i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
i2c_dev->is_suspended = true;
|
i2c_dev->is_suspended = true;
|
||||||
i2c_unlock_adapter(&i2c_dev->adapter);
|
i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -700,10 +700,10 @@ static int brcmstb_i2c_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
i2c_lock_adapter(&i2c_dev->adapter);
|
i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
|
brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
|
||||||
i2c_dev->is_suspended = false;
|
i2c_dev->is_suspended = false;
|
||||||
i2c_unlock_adapter(&i2c_dev->adapter);
|
i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -718,14 +718,14 @@ static int i2c_davinci_cpufreq_transition(struct notifier_block *nb,
|
||||||
|
|
||||||
dev = container_of(nb, struct davinci_i2c_dev, freq_transition);
|
dev = container_of(nb, struct davinci_i2c_dev, freq_transition);
|
||||||
|
|
||||||
i2c_lock_adapter(&dev->adapter);
|
i2c_lock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
if (val == CPUFREQ_PRECHANGE) {
|
if (val == CPUFREQ_PRECHANGE) {
|
||||||
davinci_i2c_reset_ctrl(dev, 0);
|
davinci_i2c_reset_ctrl(dev, 0);
|
||||||
} else if (val == CPUFREQ_POSTCHANGE) {
|
} else if (val == CPUFREQ_POSTCHANGE) {
|
||||||
i2c_davinci_calc_clk_dividers(dev);
|
i2c_davinci_calc_clk_dividers(dev);
|
||||||
davinci_i2c_reset_ctrl(dev, 1);
|
davinci_i2c_reset_ctrl(dev, 1);
|
||||||
}
|
}
|
||||||
i2c_unlock_adapter(&dev->adapter);
|
i2c_unlock_bus(&dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,7 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Intel BayTrail PMIC I2C bus semaphore implementaion
|
* Intel BayTrail PMIC I2C bus semaphore implementaion
|
||||||
* Copyright (c) 2014, Intel Corporation.
|
* Copyright (c) 2014, Intel Corporation.
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*/
|
*/
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Synopsys DesignWare I2C adapter driver.
|
* Synopsys DesignWare I2C adapter driver.
|
||||||
*
|
*
|
||||||
|
@ -6,20 +7,6 @@
|
||||||
* Copyright (C) 2006 Texas Instruments.
|
* Copyright (C) 2006 Texas Instruments.
|
||||||
* Copyright (C) 2007 MontaVista Software Inc.
|
* Copyright (C) 2007 MontaVista Software Inc.
|
||||||
* Copyright (C) 2009 Provigent Ltd.
|
* Copyright (C) 2009 Provigent Ltd.
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -31,6 +18,7 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/swab.h>
|
||||||
|
|
||||||
#include "i2c-designware-core.h"
|
#include "i2c-designware-core.h"
|
||||||
|
|
||||||
|
@ -94,6 +82,40 @@ void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_dw_set_reg_access() - Set register access flags
|
||||||
|
* @dev: device private data
|
||||||
|
*
|
||||||
|
* Autodetects needed register access mode and sets access flags accordingly.
|
||||||
|
* This must be called before doing any other register access.
|
||||||
|
*/
|
||||||
|
int i2c_dw_set_reg_access(struct dw_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
reg = dw_readl(dev, DW_IC_COMP_TYPE);
|
||||||
|
i2c_dw_release_lock(dev);
|
||||||
|
|
||||||
|
if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
|
||||||
|
/* Configure register endianess access */
|
||||||
|
dev->flags |= ACCESS_SWAP;
|
||||||
|
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
|
||||||
|
/* Configure register access mode 16bit */
|
||||||
|
dev->flags |= ACCESS_16BIT;
|
||||||
|
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
||||||
|
dev_err(dev->dev,
|
||||||
|
"Unknown Synopsys component type: 0x%08x\n", reg);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -149,6 +171,47 @@ u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||||
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
|
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Configure SDA Hold Time if required */
|
||||||
|
reg = dw_readl(dev, DW_IC_COMP_VERSION);
|
||||||
|
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
|
||||||
|
if (!dev->sda_hold_time) {
|
||||||
|
/* Keep previous hold time setting if no one set it */
|
||||||
|
dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Workaround for avoiding TX arbitration lost in case I2C
|
||||||
|
* slave pulls SDA down "too quickly" after falling egde of
|
||||||
|
* SCL by enabling non-zero SDA RX hold. Specification says it
|
||||||
|
* extends incoming SDA low to high transition while SCL is
|
||||||
|
* high but it apprears to help also above issue.
|
||||||
|
*/
|
||||||
|
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
|
||||||
|
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
|
||||||
|
|
||||||
|
dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n",
|
||||||
|
dev->sda_hold_time & ~(u32)DW_IC_SDA_HOLD_RX_MASK,
|
||||||
|
dev->sda_hold_time >> DW_IC_SDA_HOLD_RX_SHIFT);
|
||||||
|
} else if (dev->sda_hold_time) {
|
||||||
|
dev_warn(dev->dev,
|
||||||
|
"Hardware too old to adjust SDA hold time.\n");
|
||||||
|
dev->sda_hold_time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_dw_release_lock(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
void __i2c_dw_disable(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
int timeout = 100;
|
int timeout = 100;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
/*
|
/*
|
||||||
* Synopsys DesignWare I2C adapter driver.
|
* Synopsys DesignWare I2C adapter driver.
|
||||||
*
|
*
|
||||||
|
@ -6,20 +7,6 @@
|
||||||
* Copyright (C) 2006 Texas Instruments.
|
* Copyright (C) 2006 Texas Instruments.
|
||||||
* Copyright (C) 2007 MontaVista Software Inc.
|
* Copyright (C) 2007 MontaVista Software Inc.
|
||||||
* Copyright (C) 2009 Provigent Ltd.
|
* Copyright (C) 2009 Provigent Ltd.
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
@ -212,7 +199,8 @@
|
||||||
* @tx_fifo_depth: depth of the hardware tx fifo
|
* @tx_fifo_depth: depth of the hardware tx fifo
|
||||||
* @rx_fifo_depth: depth of the hardware rx fifo
|
* @rx_fifo_depth: depth of the hardware rx fifo
|
||||||
* @rx_outstanding: current master-rx elements in tx fifo
|
* @rx_outstanding: current master-rx elements in tx fifo
|
||||||
* @clk_freq: bus clock frequency
|
* @timings: bus clock frequency, SDA hold and other timings
|
||||||
|
* @sda_hold_time: SDA hold value
|
||||||
* @ss_hcnt: standard speed HCNT value
|
* @ss_hcnt: standard speed HCNT value
|
||||||
* @ss_lcnt: standard speed LCNT value
|
* @ss_lcnt: standard speed LCNT value
|
||||||
* @fs_hcnt: fast speed HCNT value
|
* @fs_hcnt: fast speed HCNT value
|
||||||
|
@ -264,10 +252,8 @@ struct dw_i2c_dev {
|
||||||
unsigned int tx_fifo_depth;
|
unsigned int tx_fifo_depth;
|
||||||
unsigned int rx_fifo_depth;
|
unsigned int rx_fifo_depth;
|
||||||
int rx_outstanding;
|
int rx_outstanding;
|
||||||
u32 clk_freq;
|
struct i2c_timings timings;
|
||||||
u32 sda_hold_time;
|
u32 sda_hold_time;
|
||||||
u32 sda_falling_time;
|
|
||||||
u32 scl_falling_time;
|
|
||||||
u16 ss_hcnt;
|
u16 ss_hcnt;
|
||||||
u16 ss_lcnt;
|
u16 ss_lcnt;
|
||||||
u16 fs_hcnt;
|
u16 fs_hcnt;
|
||||||
|
@ -295,8 +281,10 @@ struct dw_i2c_dev {
|
||||||
|
|
||||||
u32 dw_readl(struct dw_i2c_dev *dev, int offset);
|
u32 dw_readl(struct dw_i2c_dev *dev, int offset);
|
||||||
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
|
void dw_writel(struct dw_i2c_dev *dev, u32 b, int offset);
|
||||||
|
int i2c_dw_set_reg_access(struct dw_i2c_dev *dev);
|
||||||
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
|
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
|
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
|
||||||
|
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
|
||||||
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev);
|
unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev);
|
||||||
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
|
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
|
||||||
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
|
int i2c_dw_acquire_lock(struct dw_i2c_dev *dev);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Synopsys DesignWare I2C adapter driver (master only).
|
* Synopsys DesignWare I2C adapter driver (master only).
|
||||||
*
|
*
|
||||||
|
@ -6,20 +7,6 @@
|
||||||
* Copyright (C) 2006 Texas Instruments.
|
* Copyright (C) 2006 Texas Instruments.
|
||||||
* Copyright (C) 2007 MontaVista Software Inc.
|
* Copyright (C) 2007 MontaVista Software Inc.
|
||||||
* Copyright (C) 2009 Provigent Ltd.
|
* Copyright (C) 2009 Provigent Ltd.
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -45,6 +32,114 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
||||||
dw_writel(dev, dev->master_cfg, DW_IC_CON);
|
dw_writel(dev, dev->master_cfg, DW_IC_CON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
u32 ic_clk = i2c_dw_clk_rate(dev);
|
||||||
|
const char *mode_str, *fp_str = "";
|
||||||
|
u32 comp_param1;
|
||||||
|
u32 sda_falling_time, scl_falling_time;
|
||||||
|
struct i2c_timings *t = &dev->timings;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
|
||||||
|
i2c_dw_release_lock(dev);
|
||||||
|
|
||||||
|
/* Set standard and fast speed dividers for high/low periods */
|
||||||
|
sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
|
||||||
|
scl_falling_time = t->scl_fall_ns ?: 300; /* ns */
|
||||||
|
|
||||||
|
/* Calculate SCL timing parameters for standard mode if not set */
|
||||||
|
if (!dev->ss_hcnt || !dev->ss_lcnt) {
|
||||||
|
dev->ss_hcnt =
|
||||||
|
i2c_dw_scl_hcnt(ic_clk,
|
||||||
|
4000, /* tHD;STA = tHIGH = 4.0 us */
|
||||||
|
sda_falling_time,
|
||||||
|
0, /* 0: DW default, 1: Ideal */
|
||||||
|
0); /* No offset */
|
||||||
|
dev->ss_lcnt =
|
||||||
|
i2c_dw_scl_lcnt(ic_clk,
|
||||||
|
4700, /* tLOW = 4.7 us */
|
||||||
|
scl_falling_time,
|
||||||
|
0); /* No offset */
|
||||||
|
}
|
||||||
|
dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n",
|
||||||
|
dev->ss_hcnt, dev->ss_lcnt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set SCL timing parameters for fast mode or fast mode plus. Only
|
||||||
|
* difference is the timing parameter values since the registers are
|
||||||
|
* the same.
|
||||||
|
*/
|
||||||
|
if (t->bus_freq_hz == 1000000) {
|
||||||
|
/*
|
||||||
|
* Check are fast mode plus parameters available and use
|
||||||
|
* fast mode if not.
|
||||||
|
*/
|
||||||
|
if (dev->fp_hcnt && dev->fp_lcnt) {
|
||||||
|
dev->fs_hcnt = dev->fp_hcnt;
|
||||||
|
dev->fs_lcnt = dev->fp_lcnt;
|
||||||
|
fp_str = " Plus";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Calculate SCL timing parameters for fast mode if not set. They are
|
||||||
|
* needed also in high speed mode.
|
||||||
|
*/
|
||||||
|
if (!dev->fs_hcnt || !dev->fs_lcnt) {
|
||||||
|
dev->fs_hcnt =
|
||||||
|
i2c_dw_scl_hcnt(ic_clk,
|
||||||
|
600, /* tHD;STA = tHIGH = 0.6 us */
|
||||||
|
sda_falling_time,
|
||||||
|
0, /* 0: DW default, 1: Ideal */
|
||||||
|
0); /* No offset */
|
||||||
|
dev->fs_lcnt =
|
||||||
|
i2c_dw_scl_lcnt(ic_clk,
|
||||||
|
1300, /* tLOW = 1.3 us */
|
||||||
|
scl_falling_time,
|
||||||
|
0); /* No offset */
|
||||||
|
}
|
||||||
|
dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n",
|
||||||
|
fp_str, dev->fs_hcnt, dev->fs_lcnt);
|
||||||
|
|
||||||
|
/* Check is high speed possible and fall back to fast mode if not */
|
||||||
|
if ((dev->master_cfg & DW_IC_CON_SPEED_MASK) ==
|
||||||
|
DW_IC_CON_SPEED_HIGH) {
|
||||||
|
if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
|
||||||
|
!= DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) {
|
||||||
|
dev_err(dev->dev, "High Speed not supported!\n");
|
||||||
|
dev->master_cfg &= ~DW_IC_CON_SPEED_MASK;
|
||||||
|
dev->master_cfg |= DW_IC_CON_SPEED_FAST;
|
||||||
|
dev->hs_hcnt = 0;
|
||||||
|
dev->hs_lcnt = 0;
|
||||||
|
} else if (dev->hs_hcnt && dev->hs_lcnt) {
|
||||||
|
dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n",
|
||||||
|
dev->hs_hcnt, dev->hs_lcnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i2c_dw_set_sda_hold(dev);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) {
|
||||||
|
case DW_IC_CON_SPEED_STD:
|
||||||
|
mode_str = "Standard Mode";
|
||||||
|
break;
|
||||||
|
case DW_IC_CON_SPEED_HIGH:
|
||||||
|
mode_str = "High Speed Mode";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode_str = "Fast Mode";
|
||||||
|
}
|
||||||
|
dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i2c_dw_init() - Initialize the designware I2C master hardware
|
* i2c_dw_init() - Initialize the designware I2C master hardware
|
||||||
* @dev: device private data
|
* @dev: device private data
|
||||||
|
@ -55,118 +150,32 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
||||||
*/
|
*/
|
||||||
static int i2c_dw_init_master(struct dw_i2c_dev *dev)
|
static int i2c_dw_init_master(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 hcnt, lcnt;
|
|
||||||
u32 reg, comp_param1;
|
|
||||||
u32 sda_falling_time, scl_falling_time;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = i2c_dw_acquire_lock(dev);
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
reg = dw_readl(dev, DW_IC_COMP_TYPE);
|
|
||||||
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
|
|
||||||
/* Configure register endianess access */
|
|
||||||
dev->flags |= ACCESS_SWAP;
|
|
||||||
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
|
|
||||||
/* Configure register access mode 16bit */
|
|
||||||
dev->flags |= ACCESS_16BIT;
|
|
||||||
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
|
||||||
dev_err(dev->dev,
|
|
||||||
"Unknown Synopsys component type: 0x%08x\n", reg);
|
|
||||||
i2c_dw_release_lock(dev);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
|
|
||||||
|
|
||||||
/* Disable the adapter */
|
/* Disable the adapter */
|
||||||
__i2c_dw_disable(dev);
|
__i2c_dw_disable(dev);
|
||||||
|
|
||||||
/* Set standard and fast speed deviders for high/low periods */
|
/* Write standard speed timing parameters */
|
||||||
|
dw_writel(dev, dev->ss_hcnt, DW_IC_SS_SCL_HCNT);
|
||||||
|
dw_writel(dev, dev->ss_lcnt, DW_IC_SS_SCL_LCNT);
|
||||||
|
|
||||||
sda_falling_time = dev->sda_falling_time ?: 300; /* ns */
|
/* Write fast mode/fast mode plus timing parameters */
|
||||||
scl_falling_time = dev->scl_falling_time ?: 300; /* ns */
|
dw_writel(dev, dev->fs_hcnt, DW_IC_FS_SCL_HCNT);
|
||||||
|
dw_writel(dev, dev->fs_lcnt, DW_IC_FS_SCL_LCNT);
|
||||||
|
|
||||||
/* Set SCL timing parameters for standard-mode */
|
/* Write high speed timing parameters if supported */
|
||||||
if (dev->ss_hcnt && dev->ss_lcnt) {
|
if (dev->hs_hcnt && dev->hs_lcnt) {
|
||||||
hcnt = dev->ss_hcnt;
|
dw_writel(dev, dev->hs_hcnt, DW_IC_HS_SCL_HCNT);
|
||||||
lcnt = dev->ss_lcnt;
|
dw_writel(dev, dev->hs_lcnt, DW_IC_HS_SCL_LCNT);
|
||||||
} else {
|
|
||||||
hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
|
|
||||||
4000, /* tHD;STA = tHIGH = 4.0 us */
|
|
||||||
sda_falling_time,
|
|
||||||
0, /* 0: DW default, 1: Ideal */
|
|
||||||
0); /* No offset */
|
|
||||||
lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
|
|
||||||
4700, /* tLOW = 4.7 us */
|
|
||||||
scl_falling_time,
|
|
||||||
0); /* No offset */
|
|
||||||
}
|
|
||||||
dw_writel(dev, hcnt, DW_IC_SS_SCL_HCNT);
|
|
||||||
dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT);
|
|
||||||
dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
|
|
||||||
|
|
||||||
/* Set SCL timing parameters for fast-mode or fast-mode plus */
|
|
||||||
if ((dev->clk_freq == 1000000) && dev->fp_hcnt && dev->fp_lcnt) {
|
|
||||||
hcnt = dev->fp_hcnt;
|
|
||||||
lcnt = dev->fp_lcnt;
|
|
||||||
} else if (dev->fs_hcnt && dev->fs_lcnt) {
|
|
||||||
hcnt = dev->fs_hcnt;
|
|
||||||
lcnt = dev->fs_lcnt;
|
|
||||||
} else {
|
|
||||||
hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
|
|
||||||
600, /* tHD;STA = tHIGH = 0.6 us */
|
|
||||||
sda_falling_time,
|
|
||||||
0, /* 0: DW default, 1: Ideal */
|
|
||||||
0); /* No offset */
|
|
||||||
lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
|
|
||||||
1300, /* tLOW = 1.3 us */
|
|
||||||
scl_falling_time,
|
|
||||||
0); /* No offset */
|
|
||||||
}
|
|
||||||
dw_writel(dev, hcnt, DW_IC_FS_SCL_HCNT);
|
|
||||||
dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT);
|
|
||||||
dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
|
|
||||||
|
|
||||||
if ((dev->master_cfg & DW_IC_CON_SPEED_MASK) ==
|
|
||||||
DW_IC_CON_SPEED_HIGH) {
|
|
||||||
if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
|
|
||||||
!= DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) {
|
|
||||||
dev_err(dev->dev, "High Speed not supported!\n");
|
|
||||||
dev->master_cfg &= ~DW_IC_CON_SPEED_MASK;
|
|
||||||
dev->master_cfg |= DW_IC_CON_SPEED_FAST;
|
|
||||||
} else if (dev->hs_hcnt && dev->hs_lcnt) {
|
|
||||||
hcnt = dev->hs_hcnt;
|
|
||||||
lcnt = dev->hs_lcnt;
|
|
||||||
dw_writel(dev, hcnt, DW_IC_HS_SCL_HCNT);
|
|
||||||
dw_writel(dev, lcnt, DW_IC_HS_SCL_LCNT);
|
|
||||||
dev_dbg(dev->dev, "HighSpeed-mode HCNT:LCNT = %d:%d\n",
|
|
||||||
hcnt, lcnt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure SDA Hold Time if required */
|
/* Write SDA hold time if supported */
|
||||||
reg = dw_readl(dev, DW_IC_COMP_VERSION);
|
if (dev->sda_hold_time)
|
||||||
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
|
|
||||||
if (!dev->sda_hold_time) {
|
|
||||||
/* Keep previous hold time setting if no one set it */
|
|
||||||
dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Workaround for avoiding TX arbitration lost in case I2C
|
|
||||||
* slave pulls SDA down "too quickly" after falling egde of
|
|
||||||
* SCL by enabling non-zero SDA RX hold. Specification says it
|
|
||||||
* extends incoming SDA low to high transition while SCL is
|
|
||||||
* high but it apprears to help also above issue.
|
|
||||||
*/
|
|
||||||
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
|
|
||||||
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
|
|
||||||
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
|
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
|
||||||
} else if (dev->sda_hold_time) {
|
|
||||||
dev_warn(dev->dev,
|
|
||||||
"Hardware too old to adjust SDA hold time.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_dw_configure_fifo_master(dev);
|
i2c_dw_configure_fifo_master(dev);
|
||||||
i2c_dw_release_lock(dev);
|
i2c_dw_release_lock(dev);
|
||||||
|
@ -253,13 +262,6 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msgs[dev->msg_write_idx].len == 0) {
|
|
||||||
dev_err(dev->dev,
|
|
||||||
"%s: invalid message length\n", __func__);
|
|
||||||
dev->msg_err = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
|
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
|
||||||
/* new i2c_msg */
|
/* new i2c_msg */
|
||||||
buf = msgs[dev->msg_write_idx].buf;
|
buf = msgs[dev->msg_write_idx].buf;
|
||||||
|
@ -502,6 +504,10 @@ static const struct i2c_algorithm i2c_dw_algo = {
|
||||||
.functionality = i2c_dw_func,
|
.functionality = i2c_dw_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks i2c_dw_quirks = {
|
||||||
|
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 stat;
|
u32 stat;
|
||||||
|
@ -681,6 +687,14 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||||
dev->disable = i2c_dw_disable;
|
dev->disable = i2c_dw_disable;
|
||||||
dev->disable_int = i2c_dw_disable_int;
|
dev->disable_int = i2c_dw_disable_int;
|
||||||
|
|
||||||
|
ret = i2c_dw_set_reg_access(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = i2c_dw_set_timings_master(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = dev->init(dev);
|
ret = dev->init(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -689,6 +703,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||||
"Synopsys DesignWare I2C adapter");
|
"Synopsys DesignWare I2C adapter");
|
||||||
adap->retries = 3;
|
adap->retries = 3;
|
||||||
adap->algo = &i2c_dw_algo;
|
adap->algo = &i2c_dw_algo;
|
||||||
|
adap->quirks = &i2c_dw_quirks;
|
||||||
adap->dev.parent = dev->dev;
|
adap->dev.parent = dev->dev;
|
||||||
i2c_set_adapdata(adap, dev);
|
i2c_set_adapdata(adap, dev);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Synopsys DesignWare I2C adapter driver (master only).
|
* Synopsys DesignWare I2C adapter driver (master only).
|
||||||
*
|
*
|
||||||
|
@ -7,22 +8,7 @@
|
||||||
* Copyright (C) 2007 MontaVista Software Inc.
|
* Copyright (C) 2007 MontaVista Software Inc.
|
||||||
* Copyright (C) 2009 Provigent Ltd.
|
* Copyright (C) 2009 Provigent Ltd.
|
||||||
* Copyright (C) 2011, 2015, 2016 Intel Corporation.
|
* Copyright (C) 2011, 2015, 2016 Intel Corporation.
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -105,6 +91,7 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||||
case 0x0817:
|
case 0x0817:
|
||||||
c->bus_cfg &= ~DW_IC_CON_SPEED_MASK;
|
c->bus_cfg &= ~DW_IC_CON_SPEED_MASK;
|
||||||
c->bus_cfg |= DW_IC_CON_SPEED_STD;
|
c->bus_cfg |= DW_IC_CON_SPEED_STD;
|
||||||
|
/* fall through */
|
||||||
case 0x0818:
|
case 0x0818:
|
||||||
case 0x0819:
|
case 0x0819:
|
||||||
c->bus_num = pdev->device - 0x817 + 3;
|
c->bus_num = pdev->device - 0x817 + 3;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Synopsys DesignWare I2C adapter driver.
|
* Synopsys DesignWare I2C adapter driver.
|
||||||
*
|
*
|
||||||
|
@ -6,20 +7,6 @@
|
||||||
* Copyright (C) 2006 Texas Instruments.
|
* Copyright (C) 2006 Texas Instruments.
|
||||||
* Copyright (C) 2007 MontaVista Software Inc.
|
* Copyright (C) 2007 MontaVista Software Inc.
|
||||||
* Copyright (C) 2009 Provigent Ltd.
|
* Copyright (C) 2009 Provigent Ltd.
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
|
@ -96,6 +83,7 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[],
|
||||||
static int dw_i2c_acpi_configure(struct platform_device *pdev)
|
static int dw_i2c_acpi_configure(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
|
||||||
|
struct i2c_timings *t = &dev->timings;
|
||||||
u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
|
u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0;
|
||||||
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
|
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
|
||||||
const struct acpi_device_id *id;
|
const struct acpi_device_id *id;
|
||||||
|
@ -115,7 +103,7 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev)
|
||||||
dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht);
|
dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, &hs_ht);
|
||||||
dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht);
|
dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &fs_ht);
|
||||||
|
|
||||||
switch (dev->clk_freq) {
|
switch (t->bus_freq_hz) {
|
||||||
case 100000:
|
case 100000:
|
||||||
dev->sda_hold_time = ss_ht;
|
dev->sda_hold_time = ss_ht;
|
||||||
break;
|
break;
|
||||||
|
@ -175,6 +163,8 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
|
||||||
|
|
||||||
static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
|
static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
|
struct i2c_timings *t = &dev->timings;
|
||||||
|
|
||||||
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
|
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
|
||||||
|
|
||||||
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
|
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
|
||||||
|
@ -182,7 +172,7 @@ static void i2c_dw_configure_master(struct dw_i2c_dev *dev)
|
||||||
|
|
||||||
dev->mode = DW_IC_MASTER;
|
dev->mode = DW_IC_MASTER;
|
||||||
|
|
||||||
switch (dev->clk_freq) {
|
switch (t->bus_freq_hz) {
|
||||||
case 100000:
|
case 100000:
|
||||||
dev->master_cfg |= DW_IC_CON_SPEED_STD;
|
dev->master_cfg |= DW_IC_CON_SPEED_STD;
|
||||||
break;
|
break;
|
||||||
|
@ -240,7 +230,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
struct dw_i2c_dev *dev;
|
struct dw_i2c_dev *dev;
|
||||||
u32 acpi_speed, ht = 0;
|
struct i2c_timings *t;
|
||||||
|
u32 acpi_speed;
|
||||||
struct resource *mem;
|
struct resource *mem;
|
||||||
int i, irq, ret;
|
int i, irq, ret;
|
||||||
static const int supported_speeds[] = {
|
static const int supported_speeds[] = {
|
||||||
|
@ -272,18 +263,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
reset_control_deassert(dev->rst);
|
reset_control_deassert(dev->rst);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata) {
|
t = &dev->timings;
|
||||||
dev->clk_freq = pdata->i2c_scl_freq;
|
if (pdata)
|
||||||
} else {
|
t->bus_freq_hz = pdata->i2c_scl_freq;
|
||||||
device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns",
|
else
|
||||||
&ht);
|
i2c_parse_fw_timings(&pdev->dev, t, false);
|
||||||
device_property_read_u32(&pdev->dev, "i2c-sda-falling-time-ns",
|
|
||||||
&dev->sda_falling_time);
|
|
||||||
device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns",
|
|
||||||
&dev->scl_falling_time);
|
|
||||||
device_property_read_u32(&pdev->dev, "clock-frequency",
|
|
||||||
&dev->clk_freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
|
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
|
||||||
/*
|
/*
|
||||||
|
@ -300,12 +284,12 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
* Find bus speed from the "clock-frequency" device property, ACPI
|
* Find bus speed from the "clock-frequency" device property, ACPI
|
||||||
* or by using fast mode if neither is set.
|
* or by using fast mode if neither is set.
|
||||||
*/
|
*/
|
||||||
if (acpi_speed && dev->clk_freq)
|
if (acpi_speed && t->bus_freq_hz)
|
||||||
dev->clk_freq = min(dev->clk_freq, acpi_speed);
|
t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed);
|
||||||
else if (acpi_speed || dev->clk_freq)
|
else if (acpi_speed || t->bus_freq_hz)
|
||||||
dev->clk_freq = max(dev->clk_freq, acpi_speed);
|
t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
|
||||||
else
|
else
|
||||||
dev->clk_freq = 400000;
|
t->bus_freq_hz = 400000;
|
||||||
|
|
||||||
if (has_acpi_companion(&pdev->dev))
|
if (has_acpi_companion(&pdev->dev))
|
||||||
dw_i2c_acpi_configure(pdev);
|
dw_i2c_acpi_configure(pdev);
|
||||||
|
@ -314,11 +298,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
* Only standard mode at 100kHz, fast mode at 400kHz,
|
* Only standard mode at 100kHz, fast mode at 400kHz,
|
||||||
* fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
|
* fast mode plus at 1MHz and high speed mode at 3.4MHz are supported.
|
||||||
*/
|
*/
|
||||||
if (dev->clk_freq != 100000 && dev->clk_freq != 400000
|
if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 &&
|
||||||
&& dev->clk_freq != 1000000 && dev->clk_freq != 3400000) {
|
t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
|
"%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n",
|
||||||
dev->clk_freq);
|
t->bus_freq_hz);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit_reset;
|
goto exit_reset;
|
||||||
}
|
}
|
||||||
|
@ -334,12 +318,14 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (!i2c_dw_prepare_clk(dev, true)) {
|
if (!i2c_dw_prepare_clk(dev, true)) {
|
||||||
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
u64 clk_khz;
|
||||||
|
|
||||||
if (!dev->sda_hold_time && ht)
|
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
||||||
dev->sda_hold_time = div_u64(
|
clk_khz = dev->get_clk_rate_khz(dev);
|
||||||
(u64)dev->get_clk_rate_khz(dev) * ht + 500000,
|
|
||||||
1000000);
|
if (!dev->sda_hold_time && t->sda_hold_ns)
|
||||||
|
dev->sda_hold_time =
|
||||||
|
div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
dw_i2c_set_fifo_size(dev, pdev->id);
|
dw_i2c_set_fifo_size(dev, pdev->id);
|
||||||
|
|
|
@ -1,23 +1,10 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Synopsys DesignWare I2C adapter driver (slave only).
|
* Synopsys DesignWare I2C adapter driver (slave only).
|
||||||
*
|
*
|
||||||
* Based on the Synopsys DesignWare I2C adapter driver (master).
|
* Based on the Synopsys DesignWare I2C adapter driver (master).
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016 Synopsys Inc.
|
* Copyright (C) 2016 Synopsys Inc.
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -51,53 +38,18 @@ static void i2c_dw_configure_fifo_slave(struct dw_i2c_dev *dev)
|
||||||
*/
|
*/
|
||||||
static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
|
static int i2c_dw_init_slave(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 reg, comp_param1;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = i2c_dw_acquire_lock(dev);
|
ret = i2c_dw_acquire_lock(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
reg = dw_readl(dev, DW_IC_COMP_TYPE);
|
|
||||||
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
|
|
||||||
/* Configure register endianness access. */
|
|
||||||
dev->flags |= ACCESS_SWAP;
|
|
||||||
} else if (reg == (DW_IC_COMP_TYPE_VALUE & 0x0000ffff)) {
|
|
||||||
/* Configure register access mode 16bit. */
|
|
||||||
dev->flags |= ACCESS_16BIT;
|
|
||||||
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
|
|
||||||
dev_err(dev->dev,
|
|
||||||
"Unknown Synopsys component type: 0x%08x\n", reg);
|
|
||||||
i2c_dw_release_lock(dev);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1);
|
|
||||||
|
|
||||||
/* Disable the adapter. */
|
/* Disable the adapter. */
|
||||||
__i2c_dw_disable(dev);
|
__i2c_dw_disable(dev);
|
||||||
|
|
||||||
/* Configure SDA Hold Time if required. */
|
/* Write SDA hold time if supported */
|
||||||
reg = dw_readl(dev, DW_IC_COMP_VERSION);
|
if (dev->sda_hold_time)
|
||||||
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
|
|
||||||
if (!dev->sda_hold_time) {
|
|
||||||
/* Keep previous hold time setting if no one set it. */
|
|
||||||
dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Workaround for avoiding TX arbitration lost in case I2C
|
|
||||||
* slave pulls SDA down "too quickly" after falling egde of
|
|
||||||
* SCL by enabling non-zero SDA RX hold. Specification says it
|
|
||||||
* extends incoming SDA low to high transition while SCL is
|
|
||||||
* high but it apprears to help also above issue.
|
|
||||||
*/
|
|
||||||
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
|
|
||||||
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
|
|
||||||
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
|
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
|
||||||
} else {
|
|
||||||
dev_warn(dev->dev,
|
|
||||||
"Hardware too old to adjust SDA hold time.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_dw_configure_fifo_slave(dev);
|
i2c_dw_configure_fifo_slave(dev);
|
||||||
i2c_dw_release_lock(dev);
|
i2c_dw_release_lock(dev);
|
||||||
|
@ -299,6 +251,14 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev)
|
||||||
dev->disable = i2c_dw_disable;
|
dev->disable = i2c_dw_disable;
|
||||||
dev->disable_int = i2c_dw_disable_int;
|
dev->disable_int = i2c_dw_disable_int;
|
||||||
|
|
||||||
|
ret = i2c_dw_set_reg_access(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = i2c_dw_set_sda_hold(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = dev->init(dev);
|
ret = dev->init(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -176,7 +176,10 @@
|
||||||
|
|
||||||
#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(100))
|
#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(100))
|
||||||
|
|
||||||
#define HSI2C_EXYNOS7 BIT(0)
|
enum i2c_type_exynos {
|
||||||
|
I2C_TYPE_EXYNOS5,
|
||||||
|
I2C_TYPE_EXYNOS7,
|
||||||
|
};
|
||||||
|
|
||||||
struct exynos5_i2c {
|
struct exynos5_i2c {
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
|
@ -212,27 +215,30 @@ struct exynos5_i2c {
|
||||||
/**
|
/**
|
||||||
* struct exynos_hsi2c_variant - platform specific HSI2C driver data
|
* struct exynos_hsi2c_variant - platform specific HSI2C driver data
|
||||||
* @fifo_depth: the fifo depth supported by the HSI2C module
|
* @fifo_depth: the fifo depth supported by the HSI2C module
|
||||||
|
* @hw: the hardware variant of Exynos I2C controller
|
||||||
*
|
*
|
||||||
* Specifies platform specific configuration of HSI2C module.
|
* Specifies platform specific configuration of HSI2C module.
|
||||||
* Note: A structure for driver specific platform data is used for future
|
* Note: A structure for driver specific platform data is used for future
|
||||||
* expansion of its usage.
|
* expansion of its usage.
|
||||||
*/
|
*/
|
||||||
struct exynos_hsi2c_variant {
|
struct exynos_hsi2c_variant {
|
||||||
unsigned int fifo_depth;
|
unsigned int fifo_depth;
|
||||||
unsigned int hw;
|
enum i2c_type_exynos hw;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = {
|
static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = {
|
||||||
.fifo_depth = 64,
|
.fifo_depth = 64,
|
||||||
|
.hw = I2C_TYPE_EXYNOS5,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = {
|
static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = {
|
||||||
.fifo_depth = 16,
|
.fifo_depth = 16,
|
||||||
|
.hw = I2C_TYPE_EXYNOS5,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
|
static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
|
||||||
.fifo_depth = 16,
|
.fifo_depth = 16,
|
||||||
.hw = HSI2C_EXYNOS7,
|
.hw = I2C_TYPE_EXYNOS7,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id exynos5_i2c_match[] = {
|
static const struct of_device_id exynos5_i2c_match[] = {
|
||||||
|
@ -300,7 +306,7 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings)
|
||||||
*/
|
*/
|
||||||
t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
|
t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
|
||||||
temp = clkin / op_clk - 8 - t_ftl_cycle;
|
temp = clkin / op_clk - 8 - t_ftl_cycle;
|
||||||
if (i2c->variant->hw != HSI2C_EXYNOS7)
|
if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
|
||||||
temp -= t_ftl_cycle;
|
temp -= t_ftl_cycle;
|
||||||
div = temp / 512;
|
div = temp / 512;
|
||||||
clk_cycle = temp / (div + 1) - 2;
|
clk_cycle = temp / (div + 1) - 2;
|
||||||
|
@ -424,7 +430,7 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||||
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
|
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
|
||||||
|
|
||||||
/* handle interrupt related to the transfer status */
|
/* handle interrupt related to the transfer status */
|
||||||
if (i2c->variant->hw == HSI2C_EXYNOS7) {
|
if (i2c->variant->hw == I2C_TYPE_EXYNOS7) {
|
||||||
if (int_status & HSI2C_INT_TRANS_DONE) {
|
if (int_status & HSI2C_INT_TRANS_DONE) {
|
||||||
i2c->trans_done = 1;
|
i2c->trans_done = 1;
|
||||||
i2c->state = 0;
|
i2c->state = 0;
|
||||||
|
@ -571,7 +577,7 @@ static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c)
|
||||||
{
|
{
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
if (i2c->variant->hw != HSI2C_EXYNOS7)
|
if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -612,7 +618,7 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned short trig_lvl;
|
unsigned short trig_lvl;
|
||||||
|
|
||||||
if (i2c->variant->hw == HSI2C_EXYNOS7)
|
if (i2c->variant->hw == I2C_TYPE_EXYNOS7)
|
||||||
int_en |= HSI2C_INT_I2C_TRANS;
|
int_en |= HSI2C_INT_I2C_TRANS;
|
||||||
else
|
else
|
||||||
int_en |= HSI2C_INT_I2C;
|
int_en |= HSI2C_INT_I2C;
|
||||||
|
|
|
@ -0,0 +1,752 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* FSI-attached I2C master algorithm
|
||||||
|
*
|
||||||
|
* Copyright 2018 IBM Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version
|
||||||
|
* 2 of the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/fsi.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define FSI_ENGID_I2C 0x7
|
||||||
|
|
||||||
|
#define I2C_DEFAULT_CLK_DIV 6
|
||||||
|
|
||||||
|
/* i2c registers */
|
||||||
|
#define I2C_FSI_FIFO 0x00
|
||||||
|
#define I2C_FSI_CMD 0x04
|
||||||
|
#define I2C_FSI_MODE 0x08
|
||||||
|
#define I2C_FSI_WATER_MARK 0x0C
|
||||||
|
#define I2C_FSI_INT_MASK 0x10
|
||||||
|
#define I2C_FSI_INT_COND 0x14
|
||||||
|
#define I2C_FSI_OR_INT_MASK 0x14
|
||||||
|
#define I2C_FSI_INTS 0x18
|
||||||
|
#define I2C_FSI_AND_INT_MASK 0x18
|
||||||
|
#define I2C_FSI_STAT 0x1C
|
||||||
|
#define I2C_FSI_RESET_I2C 0x1C
|
||||||
|
#define I2C_FSI_ESTAT 0x20
|
||||||
|
#define I2C_FSI_RESET_ERR 0x20
|
||||||
|
#define I2C_FSI_RESID_LEN 0x24
|
||||||
|
#define I2C_FSI_SET_SCL 0x24
|
||||||
|
#define I2C_FSI_PORT_BUSY 0x28
|
||||||
|
#define I2C_FSI_RESET_SCL 0x2C
|
||||||
|
#define I2C_FSI_SET_SDA 0x30
|
||||||
|
#define I2C_FSI_RESET_SDA 0x34
|
||||||
|
|
||||||
|
/* cmd register */
|
||||||
|
#define I2C_CMD_WITH_START BIT(31)
|
||||||
|
#define I2C_CMD_WITH_ADDR BIT(30)
|
||||||
|
#define I2C_CMD_RD_CONT BIT(29)
|
||||||
|
#define I2C_CMD_WITH_STOP BIT(28)
|
||||||
|
#define I2C_CMD_FORCELAUNCH BIT(27)
|
||||||
|
#define I2C_CMD_ADDR GENMASK(23, 17)
|
||||||
|
#define I2C_CMD_READ BIT(16)
|
||||||
|
#define I2C_CMD_LEN GENMASK(15, 0)
|
||||||
|
|
||||||
|
/* mode register */
|
||||||
|
#define I2C_MODE_CLKDIV GENMASK(31, 16)
|
||||||
|
#define I2C_MODE_PORT GENMASK(15, 10)
|
||||||
|
#define I2C_MODE_ENHANCED BIT(3)
|
||||||
|
#define I2C_MODE_DIAG BIT(2)
|
||||||
|
#define I2C_MODE_PACE_ALLOW BIT(1)
|
||||||
|
#define I2C_MODE_WRAP BIT(0)
|
||||||
|
|
||||||
|
/* watermark register */
|
||||||
|
#define I2C_WATERMARK_HI GENMASK(15, 12)
|
||||||
|
#define I2C_WATERMARK_LO GENMASK(7, 4)
|
||||||
|
|
||||||
|
#define I2C_FIFO_HI_LVL 4
|
||||||
|
#define I2C_FIFO_LO_LVL 4
|
||||||
|
|
||||||
|
/* interrupt register */
|
||||||
|
#define I2C_INT_INV_CMD BIT(15)
|
||||||
|
#define I2C_INT_PARITY BIT(14)
|
||||||
|
#define I2C_INT_BE_OVERRUN BIT(13)
|
||||||
|
#define I2C_INT_BE_ACCESS BIT(12)
|
||||||
|
#define I2C_INT_LOST_ARB BIT(11)
|
||||||
|
#define I2C_INT_NACK BIT(10)
|
||||||
|
#define I2C_INT_DAT_REQ BIT(9)
|
||||||
|
#define I2C_INT_CMD_COMP BIT(8)
|
||||||
|
#define I2C_INT_STOP_ERR BIT(7)
|
||||||
|
#define I2C_INT_BUSY BIT(6)
|
||||||
|
#define I2C_INT_IDLE BIT(5)
|
||||||
|
|
||||||
|
/* status register */
|
||||||
|
#define I2C_STAT_INV_CMD BIT(31)
|
||||||
|
#define I2C_STAT_PARITY BIT(30)
|
||||||
|
#define I2C_STAT_BE_OVERRUN BIT(29)
|
||||||
|
#define I2C_STAT_BE_ACCESS BIT(28)
|
||||||
|
#define I2C_STAT_LOST_ARB BIT(27)
|
||||||
|
#define I2C_STAT_NACK BIT(26)
|
||||||
|
#define I2C_STAT_DAT_REQ BIT(25)
|
||||||
|
#define I2C_STAT_CMD_COMP BIT(24)
|
||||||
|
#define I2C_STAT_STOP_ERR BIT(23)
|
||||||
|
#define I2C_STAT_MAX_PORT GENMASK(19, 16)
|
||||||
|
#define I2C_STAT_ANY_INT BIT(15)
|
||||||
|
#define I2C_STAT_SCL_IN BIT(11)
|
||||||
|
#define I2C_STAT_SDA_IN BIT(10)
|
||||||
|
#define I2C_STAT_PORT_BUSY BIT(9)
|
||||||
|
#define I2C_STAT_SELF_BUSY BIT(8)
|
||||||
|
#define I2C_STAT_FIFO_COUNT GENMASK(7, 0)
|
||||||
|
|
||||||
|
#define I2C_STAT_ERR (I2C_STAT_INV_CMD | \
|
||||||
|
I2C_STAT_PARITY | \
|
||||||
|
I2C_STAT_BE_OVERRUN | \
|
||||||
|
I2C_STAT_BE_ACCESS | \
|
||||||
|
I2C_STAT_LOST_ARB | \
|
||||||
|
I2C_STAT_NACK | \
|
||||||
|
I2C_STAT_STOP_ERR)
|
||||||
|
#define I2C_STAT_ANY_RESP (I2C_STAT_ERR | \
|
||||||
|
I2C_STAT_DAT_REQ | \
|
||||||
|
I2C_STAT_CMD_COMP)
|
||||||
|
|
||||||
|
/* extended status register */
|
||||||
|
#define I2C_ESTAT_FIFO_SZ GENMASK(31, 24)
|
||||||
|
#define I2C_ESTAT_SCL_IN_SY BIT(15)
|
||||||
|
#define I2C_ESTAT_SDA_IN_SY BIT(14)
|
||||||
|
#define I2C_ESTAT_S_SCL BIT(13)
|
||||||
|
#define I2C_ESTAT_S_SDA BIT(12)
|
||||||
|
#define I2C_ESTAT_M_SCL BIT(11)
|
||||||
|
#define I2C_ESTAT_M_SDA BIT(10)
|
||||||
|
#define I2C_ESTAT_HI_WATER BIT(9)
|
||||||
|
#define I2C_ESTAT_LO_WATER BIT(8)
|
||||||
|
#define I2C_ESTAT_PORT_BUSY BIT(7)
|
||||||
|
#define I2C_ESTAT_SELF_BUSY BIT(6)
|
||||||
|
#define I2C_ESTAT_VERSION GENMASK(4, 0)
|
||||||
|
|
||||||
|
/* port busy register */
|
||||||
|
#define I2C_PORT_BUSY_RESET BIT(31)
|
||||||
|
|
||||||
|
/* wait for command complete or data request */
|
||||||
|
#define I2C_CMD_SLEEP_MAX_US 500
|
||||||
|
#define I2C_CMD_SLEEP_MIN_US 50
|
||||||
|
|
||||||
|
/* wait after reset; choose time from legacy driver */
|
||||||
|
#define I2C_RESET_SLEEP_MAX_US 2000
|
||||||
|
#define I2C_RESET_SLEEP_MIN_US 1000
|
||||||
|
|
||||||
|
/* choose timeout length from legacy driver; it's well tested */
|
||||||
|
#define I2C_ABORT_TIMEOUT msecs_to_jiffies(100)
|
||||||
|
|
||||||
|
struct fsi_i2c_master {
|
||||||
|
struct fsi_device *fsi;
|
||||||
|
u8 fifo_size;
|
||||||
|
struct list_head ports;
|
||||||
|
struct mutex lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fsi_i2c_port {
|
||||||
|
struct list_head list;
|
||||||
|
struct i2c_adapter adapter;
|
||||||
|
struct fsi_i2c_master *master;
|
||||||
|
u16 port;
|
||||||
|
u16 xfrd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg,
|
||||||
|
u32 *data)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
__be32 data_be;
|
||||||
|
|
||||||
|
rc = fsi_device_read(fsi, reg, &data_be, sizeof(data_be));
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
*data = be32_to_cpu(data_be);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,
|
||||||
|
u32 *data)
|
||||||
|
{
|
||||||
|
__be32 data_be = cpu_to_be32p(data);
|
||||||
|
|
||||||
|
return fsi_device_write(fsi, reg, &data_be, sizeof(data_be));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_dev_init(struct fsi_i2c_master *i2c)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 mode = I2C_MODE_ENHANCED, extended_status, watermark;
|
||||||
|
u32 interrupt = 0;
|
||||||
|
|
||||||
|
/* since we use polling, disable interrupts */
|
||||||
|
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_INT_MASK, &interrupt);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
mode |= FIELD_PREP(I2C_MODE_CLKDIV, I2C_DEFAULT_CLK_DIV);
|
||||||
|
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_ESTAT, &extended_status);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
i2c->fifo_size = FIELD_GET(I2C_ESTAT_FIFO_SZ, extended_status);
|
||||||
|
watermark = FIELD_PREP(I2C_WATERMARK_HI,
|
||||||
|
i2c->fifo_size - I2C_FIFO_HI_LVL);
|
||||||
|
watermark |= FIELD_PREP(I2C_WATERMARK_LO, I2C_FIFO_LO_LVL);
|
||||||
|
|
||||||
|
return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_WATER_MARK, &watermark);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_set_port(struct fsi_i2c_port *port)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct fsi_device *fsi = port->master->fsi;
|
||||||
|
u32 mode, dummy = 0;
|
||||||
|
|
||||||
|
rc = fsi_i2c_read_reg(fsi, I2C_FSI_MODE, &mode);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (FIELD_GET(I2C_MODE_PORT, mode) == port->port)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mode = (mode & ~I2C_MODE_PORT) | FIELD_PREP(I2C_MODE_PORT, port->port);
|
||||||
|
rc = fsi_i2c_write_reg(fsi, I2C_FSI_MODE, &mode);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* reset engine when port is changed */
|
||||||
|
return fsi_i2c_write_reg(fsi, I2C_FSI_RESET_ERR, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_start(struct fsi_i2c_port *port, struct i2c_msg *msg,
|
||||||
|
bool stop)
|
||||||
|
{
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
u32 cmd = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR;
|
||||||
|
|
||||||
|
port->xfrd = 0;
|
||||||
|
|
||||||
|
if (msg->flags & I2C_M_RD)
|
||||||
|
cmd |= I2C_CMD_READ;
|
||||||
|
|
||||||
|
if (stop || msg->flags & I2C_M_STOP)
|
||||||
|
cmd |= I2C_CMD_WITH_STOP;
|
||||||
|
|
||||||
|
cmd |= FIELD_PREP(I2C_CMD_ADDR, msg->addr);
|
||||||
|
cmd |= FIELD_PREP(I2C_CMD_LEN, msg->len);
|
||||||
|
|
||||||
|
return fsi_i2c_write_reg(i2c->fsi, I2C_FSI_CMD, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_get_op_bytes(int op_bytes)
|
||||||
|
{
|
||||||
|
/* fsi is limited to max 4 byte aligned ops */
|
||||||
|
if (op_bytes > 4)
|
||||||
|
return 4;
|
||||||
|
else if (op_bytes == 3)
|
||||||
|
return 2;
|
||||||
|
return op_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_write_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
|
||||||
|
u8 fifo_count)
|
||||||
|
{
|
||||||
|
int write;
|
||||||
|
int rc;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
int bytes_to_write = i2c->fifo_size - fifo_count;
|
||||||
|
int bytes_remaining = msg->len - port->xfrd;
|
||||||
|
|
||||||
|
bytes_to_write = min(bytes_to_write, bytes_remaining);
|
||||||
|
|
||||||
|
while (bytes_to_write) {
|
||||||
|
write = fsi_i2c_get_op_bytes(bytes_to_write);
|
||||||
|
|
||||||
|
rc = fsi_device_write(i2c->fsi, I2C_FSI_FIFO,
|
||||||
|
&msg->buf[port->xfrd], write);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
port->xfrd += write;
|
||||||
|
bytes_to_write -= write;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_read_fifo(struct fsi_i2c_port *port, struct i2c_msg *msg,
|
||||||
|
u8 fifo_count)
|
||||||
|
{
|
||||||
|
int read;
|
||||||
|
int rc;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
int bytes_to_read;
|
||||||
|
int xfr_remaining = msg->len - port->xfrd;
|
||||||
|
u32 dummy;
|
||||||
|
|
||||||
|
bytes_to_read = min_t(int, fifo_count, xfr_remaining);
|
||||||
|
|
||||||
|
while (bytes_to_read) {
|
||||||
|
read = fsi_i2c_get_op_bytes(bytes_to_read);
|
||||||
|
|
||||||
|
if (xfr_remaining) {
|
||||||
|
rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO,
|
||||||
|
&msg->buf[port->xfrd], read);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
port->xfrd += read;
|
||||||
|
xfr_remaining -= read;
|
||||||
|
} else {
|
||||||
|
/* no more buffer but data in fifo, need to clear it */
|
||||||
|
rc = fsi_device_read(i2c->fsi, I2C_FSI_FIFO, &dummy,
|
||||||
|
read);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_to_read -= read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_get_scl(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
u32 stat = 0;
|
||||||
|
struct fsi_i2c_port *port = adap->algo_data;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
|
||||||
|
fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
|
||||||
|
|
||||||
|
return !!(stat & I2C_STAT_SCL_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsi_i2c_set_scl(struct i2c_adapter *adap, int val)
|
||||||
|
{
|
||||||
|
u32 dummy = 0;
|
||||||
|
struct fsi_i2c_port *port = adap->algo_data;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
|
||||||
|
if (val)
|
||||||
|
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SCL, &dummy);
|
||||||
|
else
|
||||||
|
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SCL, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_get_sda(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
u32 stat = 0;
|
||||||
|
struct fsi_i2c_port *port = adap->algo_data;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
|
||||||
|
fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
|
||||||
|
|
||||||
|
return !!(stat & I2C_STAT_SDA_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsi_i2c_set_sda(struct i2c_adapter *adap, int val)
|
||||||
|
{
|
||||||
|
u32 dummy = 0;
|
||||||
|
struct fsi_i2c_port *port = adap->algo_data;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
|
||||||
|
if (val)
|
||||||
|
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_SET_SDA, &dummy);
|
||||||
|
else
|
||||||
|
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_SDA, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsi_i2c_prepare_recovery(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 mode;
|
||||||
|
struct fsi_i2c_port *port = adap->algo_data;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
|
||||||
|
rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
|
||||||
|
if (rc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mode |= I2C_MODE_DIAG;
|
||||||
|
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsi_i2c_unprepare_recovery(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 mode;
|
||||||
|
struct fsi_i2c_port *port = adap->algo_data;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
|
||||||
|
rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
|
||||||
|
if (rc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mode &= ~I2C_MODE_DIAG;
|
||||||
|
fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_reset_bus(struct fsi_i2c_master *i2c,
|
||||||
|
struct fsi_i2c_port *port)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 stat, dummy = 0;
|
||||||
|
|
||||||
|
/* force bus reset, ignore errors */
|
||||||
|
i2c_recover_bus(&port->adapter);
|
||||||
|
|
||||||
|
/* reset errors */
|
||||||
|
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_ERR, &dummy);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* wait for command complete */
|
||||||
|
usleep_range(I2C_RESET_SLEEP_MIN_US, I2C_RESET_SLEEP_MAX_US);
|
||||||
|
|
||||||
|
rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_STAT, &stat);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (stat & I2C_STAT_CMD_COMP)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* failed to get command complete; reset engine again */
|
||||||
|
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* re-init engine again */
|
||||||
|
return fsi_i2c_dev_init(i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_reset_engine(struct fsi_i2c_master *i2c, u16 port)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u32 mode, dummy = 0;
|
||||||
|
|
||||||
|
/* reset engine */
|
||||||
|
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_RESET_I2C, &dummy);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* re-init engine */
|
||||||
|
rc = fsi_i2c_dev_init(i2c);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
rc = fsi_i2c_read_reg(i2c->fsi, I2C_FSI_MODE, &mode);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* set port; default after reset is 0 */
|
||||||
|
if (port) {
|
||||||
|
mode &= ~I2C_MODE_PORT;
|
||||||
|
mode |= FIELD_PREP(I2C_MODE_PORT, port);
|
||||||
|
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_MODE, &mode);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset busy register; hw workaround */
|
||||||
|
dummy = I2C_PORT_BUSY_RESET;
|
||||||
|
rc = fsi_i2c_write_reg(i2c->fsi, I2C_FSI_PORT_BUSY, &dummy);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_abort(struct fsi_i2c_port *port, u32 status)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
unsigned long start;
|
||||||
|
u32 cmd = I2C_CMD_WITH_STOP;
|
||||||
|
u32 stat;
|
||||||
|
struct fsi_i2c_master *i2c = port->master;
|
||||||
|
struct fsi_device *fsi = i2c->fsi;
|
||||||
|
|
||||||
|
rc = fsi_i2c_reset_engine(i2c, port->port);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &stat);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* if sda is low, peform full bus reset */
|
||||||
|
if (!(stat & I2C_STAT_SDA_IN)) {
|
||||||
|
rc = fsi_i2c_reset_bus(i2c, port);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip final stop command for these errors */
|
||||||
|
if (status & (I2C_STAT_PARITY | I2C_STAT_LOST_ARB | I2C_STAT_STOP_ERR))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* write stop command */
|
||||||
|
rc = fsi_i2c_write_reg(fsi, I2C_FSI_CMD, &cmd);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* wait until we see command complete in the master */
|
||||||
|
start = jiffies;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = fsi_i2c_read_reg(fsi, I2C_FSI_STAT, &status);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (status & I2C_STAT_CMD_COMP)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
|
||||||
|
} while (time_after(start + I2C_ABORT_TIMEOUT, jiffies));
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_handle_status(struct fsi_i2c_port *port,
|
||||||
|
struct i2c_msg *msg, u32 status)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u8 fifo_count;
|
||||||
|
|
||||||
|
if (status & I2C_STAT_ERR) {
|
||||||
|
rc = fsi_i2c_abort(port, status);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (status & I2C_STAT_INV_CMD)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (status & (I2C_STAT_PARITY | I2C_STAT_BE_OVERRUN |
|
||||||
|
I2C_STAT_BE_ACCESS))
|
||||||
|
return -EPROTO;
|
||||||
|
|
||||||
|
if (status & I2C_STAT_NACK)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
if (status & I2C_STAT_LOST_ARB)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
if (status & I2C_STAT_STOP_ERR)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & I2C_STAT_DAT_REQ) {
|
||||||
|
fifo_count = FIELD_GET(I2C_STAT_FIFO_COUNT, status);
|
||||||
|
|
||||||
|
if (msg->flags & I2C_M_RD)
|
||||||
|
return fsi_i2c_read_fifo(port, msg, fifo_count);
|
||||||
|
|
||||||
|
return fsi_i2c_write_fifo(port, msg, fifo_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & I2C_STAT_CMD_COMP) {
|
||||||
|
if (port->xfrd < msg->len)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
return msg->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_wait(struct fsi_i2c_port *port, struct i2c_msg *msg,
|
||||||
|
unsigned long timeout)
|
||||||
|
{
|
||||||
|
u32 status = 0;
|
||||||
|
int rc;
|
||||||
|
unsigned long start = jiffies;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = fsi_i2c_read_reg(port->master->fsi, I2C_FSI_STAT,
|
||||||
|
&status);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (status & I2C_STAT_ANY_RESP) {
|
||||||
|
rc = fsi_i2c_handle_status(port, msg, status);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* cmd complete and all data xfrd */
|
||||||
|
if (rc == msg->len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* need to xfr more data, but maybe don't need wait */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep_range(I2C_CMD_SLEEP_MIN_US, I2C_CMD_SLEEP_MAX_US);
|
||||||
|
} while (time_after(start + timeout, jiffies));
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
int i, rc;
|
||||||
|
unsigned long start_time;
|
||||||
|
struct fsi_i2c_port *port = adap->algo_data;
|
||||||
|
struct fsi_i2c_master *master = port->master;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
|
||||||
|
mutex_lock(&master->lock);
|
||||||
|
|
||||||
|
rc = fsi_i2c_set_port(port);
|
||||||
|
if (rc)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
msg = msgs + i;
|
||||||
|
start_time = jiffies;
|
||||||
|
|
||||||
|
rc = fsi_i2c_start(port, msg, i == num - 1);
|
||||||
|
if (rc)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
rc = fsi_i2c_wait(port, msg,
|
||||||
|
adap->timeout - (jiffies - start_time));
|
||||||
|
if (rc)
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&master->lock);
|
||||||
|
return rc ? : num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 fsi_i2c_functionality(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_PROTOCOL_MANGLING |
|
||||||
|
I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct i2c_bus_recovery_info fsi_i2c_bus_recovery_info = {
|
||||||
|
.recover_bus = i2c_generic_scl_recovery,
|
||||||
|
.get_scl = fsi_i2c_get_scl,
|
||||||
|
.set_scl = fsi_i2c_set_scl,
|
||||||
|
.get_sda = fsi_i2c_get_sda,
|
||||||
|
.set_sda = fsi_i2c_set_sda,
|
||||||
|
.prepare_recovery = fsi_i2c_prepare_recovery,
|
||||||
|
.unprepare_recovery = fsi_i2c_unprepare_recovery,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct i2c_algorithm fsi_i2c_algorithm = {
|
||||||
|
.master_xfer = fsi_i2c_xfer,
|
||||||
|
.functionality = fsi_i2c_functionality,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fsi_i2c_probe(struct device *dev)
|
||||||
|
{
|
||||||
|
struct fsi_i2c_master *i2c;
|
||||||
|
struct fsi_i2c_port *port;
|
||||||
|
struct device_node *np;
|
||||||
|
int rc;
|
||||||
|
u32 port_no;
|
||||||
|
|
||||||
|
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
|
||||||
|
if (!i2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&i2c->lock);
|
||||||
|
i2c->fsi = to_fsi_dev(dev);
|
||||||
|
INIT_LIST_HEAD(&i2c->ports);
|
||||||
|
|
||||||
|
rc = fsi_i2c_dev_init(i2c);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* Add adapter for each i2c port of the master. */
|
||||||
|
for_each_available_child_of_node(dev->of_node, np) {
|
||||||
|
rc = of_property_read_u32(np, "reg", &port_no);
|
||||||
|
if (rc || port_no > USHRT_MAX)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||||
|
if (!port)
|
||||||
|
break;
|
||||||
|
|
||||||
|
port->master = i2c;
|
||||||
|
port->port = port_no;
|
||||||
|
|
||||||
|
port->adapter.owner = THIS_MODULE;
|
||||||
|
port->adapter.dev.of_node = np;
|
||||||
|
port->adapter.dev.parent = dev;
|
||||||
|
port->adapter.algo = &fsi_i2c_algorithm;
|
||||||
|
port->adapter.bus_recovery_info = &fsi_i2c_bus_recovery_info;
|
||||||
|
port->adapter.algo_data = port;
|
||||||
|
|
||||||
|
snprintf(port->adapter.name, sizeof(port->adapter.name),
|
||||||
|
"i2c_bus-%u", port_no);
|
||||||
|
|
||||||
|
rc = i2c_add_adapter(&port->adapter);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Failed to register adapter: %d\n", rc);
|
||||||
|
kfree(port);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add(&port->list, &i2c->ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_set_drvdata(dev, i2c);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsi_i2c_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
struct fsi_i2c_master *i2c = dev_get_drvdata(dev);
|
||||||
|
struct fsi_i2c_port *port, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(port, tmp, &i2c->ports, list) {
|
||||||
|
list_del(&port->list);
|
||||||
|
i2c_del_adapter(&port->adapter);
|
||||||
|
kfree(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fsi_device_id fsi_i2c_ids[] = {
|
||||||
|
{ FSI_ENGID_I2C, FSI_VERSION_ANY },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fsi_driver fsi_i2c_driver = {
|
||||||
|
.id_table = fsi_i2c_ids,
|
||||||
|
.drv = {
|
||||||
|
.name = "i2c-fsi",
|
||||||
|
.bus = &fsi_bus_type,
|
||||||
|
.probe = fsi_i2c_probe,
|
||||||
|
.remove = fsi_i2c_remove,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_fsi_driver(fsi_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
|
||||||
|
MODULE_DESCRIPTION("FSI attached I2C master");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -78,49 +78,43 @@ static struct dentry *i2c_gpio_debug_dir;
|
||||||
#define getscl(bd) ((bd)->getscl((bd)->data))
|
#define getscl(bd) ((bd)->getscl((bd)->data))
|
||||||
|
|
||||||
#define WIRE_ATTRIBUTE(wire) \
|
#define WIRE_ATTRIBUTE(wire) \
|
||||||
static int fops_##wire##_get(void *data, u64 *val) \
|
static int fops_##wire##_get(void *data, u64 *val) \
|
||||||
{ \
|
{ \
|
||||||
struct i2c_gpio_private_data *priv = data; \
|
struct i2c_gpio_private_data *priv = data; \
|
||||||
\
|
\
|
||||||
i2c_lock_adapter(&priv->adap); \
|
i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
|
||||||
*val = get##wire(&priv->bit_data); \
|
*val = get##wire(&priv->bit_data); \
|
||||||
i2c_unlock_adapter(&priv->adap); \
|
i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
|
||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
static int fops_##wire##_set(void *data, u64 val) \
|
static int fops_##wire##_set(void *data, u64 val) \
|
||||||
{ \
|
{ \
|
||||||
struct i2c_gpio_private_data *priv = data; \
|
struct i2c_gpio_private_data *priv = data; \
|
||||||
\
|
\
|
||||||
i2c_lock_adapter(&priv->adap); \
|
i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
|
||||||
set##wire(&priv->bit_data, val); \
|
set##wire(&priv->bit_data, val); \
|
||||||
i2c_unlock_adapter(&priv->adap); \
|
i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); \
|
||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_##wire, fops_##wire##_get, fops_##wire##_set, "%llu\n")
|
DEFINE_DEBUGFS_ATTRIBUTE(fops_##wire, fops_##wire##_get, fops_##wire##_set, "%llu\n")
|
||||||
|
|
||||||
WIRE_ATTRIBUTE(scl);
|
WIRE_ATTRIBUTE(scl);
|
||||||
WIRE_ATTRIBUTE(sda);
|
WIRE_ATTRIBUTE(sda);
|
||||||
|
|
||||||
static int fops_incomplete_transfer_set(void *data, u64 addr)
|
static void i2c_gpio_incomplete_transfer(struct i2c_gpio_private_data *priv,
|
||||||
|
u32 pattern, u8 pattern_size)
|
||||||
{
|
{
|
||||||
struct i2c_gpio_private_data *priv = data;
|
|
||||||
struct i2c_algo_bit_data *bit_data = &priv->bit_data;
|
struct i2c_algo_bit_data *bit_data = &priv->bit_data;
|
||||||
int i, pattern;
|
int i;
|
||||||
|
|
||||||
if (addr > 0x7f)
|
i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* ADDR (7 bit) + RD (1 bit) + SDA hi (1 bit) */
|
|
||||||
pattern = (addr << 2) | 3;
|
|
||||||
|
|
||||||
i2c_lock_adapter(&priv->adap);
|
|
||||||
|
|
||||||
/* START condition */
|
/* START condition */
|
||||||
setsda(bit_data, 0);
|
setsda(bit_data, 0);
|
||||||
udelay(bit_data->udelay);
|
udelay(bit_data->udelay);
|
||||||
|
|
||||||
/* Send ADDR+RD, request ACK, don't send STOP */
|
/* Send pattern, request ACK, don't send STOP */
|
||||||
for (i = 8; i >= 0; i--) {
|
for (i = pattern_size - 1; i >= 0; i--) {
|
||||||
setscl(bit_data, 0);
|
setscl(bit_data, 0);
|
||||||
udelay(bit_data->udelay / 2);
|
udelay(bit_data->udelay / 2);
|
||||||
setsda(bit_data, (pattern >> i) & 1);
|
setsda(bit_data, (pattern >> i) & 1);
|
||||||
|
@ -129,11 +123,44 @@ static int fops_incomplete_transfer_set(void *data, u64 addr)
|
||||||
udelay(bit_data->udelay);
|
udelay(bit_data->udelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_unlock_adapter(&priv->adap);
|
i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fops_incomplete_addr_phase_set(void *data, u64 addr)
|
||||||
|
{
|
||||||
|
struct i2c_gpio_private_data *priv = data;
|
||||||
|
u32 pattern;
|
||||||
|
|
||||||
|
if (addr > 0x7f)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* ADDR (7 bit) + RD (1 bit) + Client ACK, keep SDA hi (1 bit) */
|
||||||
|
pattern = (addr << 2) | 3;
|
||||||
|
|
||||||
|
i2c_gpio_incomplete_transfer(priv, pattern, 9);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_transfer, NULL, fops_incomplete_transfer_set, "%llu\n");
|
DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_addr_phase, NULL, fops_incomplete_addr_phase_set, "%llu\n");
|
||||||
|
|
||||||
|
static int fops_incomplete_write_byte_set(void *data, u64 addr)
|
||||||
|
{
|
||||||
|
struct i2c_gpio_private_data *priv = data;
|
||||||
|
u32 pattern;
|
||||||
|
|
||||||
|
if (addr > 0x7f)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* ADDR (7 bit) + WR (1 bit) + Client ACK (1 bit) */
|
||||||
|
pattern = (addr << 2) | 1;
|
||||||
|
/* 0x00 (8 bit) + Client ACK, keep SDA hi (1 bit) */
|
||||||
|
pattern = (pattern << 9) | 1;
|
||||||
|
|
||||||
|
i2c_gpio_incomplete_transfer(priv, pattern, 18);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n");
|
||||||
|
|
||||||
static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
|
static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
@ -156,8 +183,10 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
|
||||||
|
|
||||||
debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
|
debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
|
||||||
debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
|
debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
|
||||||
debugfs_create_file_unsafe("incomplete_transfer", 0200, priv->debug_dir,
|
debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir,
|
||||||
priv, &fops_incomplete_transfer);
|
priv, &fops_incomplete_addr_phase);
|
||||||
|
debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir,
|
||||||
|
priv, &fops_incomplete_write_byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2c_gpio_fault_injector_exit(struct platform_device *pdev)
|
static void i2c_gpio_fault_injector_exit(struct platform_device *pdev)
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
* Cannon Lake-H (PCH) 0xa323 32 hard yes yes yes
|
* Cannon Lake-H (PCH) 0xa323 32 hard yes yes yes
|
||||||
* Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
|
* Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
|
||||||
* Cedar Fork (PCH) 0x18df 32 hard yes yes yes
|
* Cedar Fork (PCH) 0x18df 32 hard yes yes yes
|
||||||
|
* Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes
|
||||||
*
|
*
|
||||||
* Features supported by this driver:
|
* Features supported by this driver:
|
||||||
* Software PEC no
|
* Software PEC no
|
||||||
|
@ -220,6 +221,7 @@
|
||||||
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
|
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
|
||||||
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
|
#define PCI_DEVICE_ID_INTEL_COLETOCREEK_SMBUS 0x23b0
|
||||||
#define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4
|
#define PCI_DEVICE_ID_INTEL_GEMINILAKE_SMBUS 0x31d4
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS 0x34a3
|
||||||
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
|
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
|
||||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
||||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
||||||
|
@ -1034,6 +1036,7 @@ static const struct pci_device_id i801_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_H_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) },
|
||||||
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) },
|
||||||
{ 0, }
|
{ 0, }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1518,6 +1521,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
case PCI_DEVICE_ID_INTEL_CDF_SMBUS:
|
case PCI_DEVICE_ID_INTEL_CDF_SMBUS:
|
||||||
case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
|
case PCI_DEVICE_ID_INTEL_DNV_SMBUS:
|
||||||
case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS:
|
case PCI_DEVICE_ID_INTEL_KABYLAKE_PCH_H_SMBUS:
|
||||||
|
case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS:
|
||||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||||
priv->features |= FEATURE_IRQ;
|
priv->features |= FEATURE_IRQ;
|
||||||
priv->features |= FEATURE_SMBUS_PEC;
|
priv->features |= FEATURE_SMBUS_PEC;
|
||||||
|
|
|
@ -421,10 +421,14 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (for_busy && (temp & I2SR_IBB))
|
if (for_busy && (temp & I2SR_IBB)) {
|
||||||
|
i2c_imx->stopped = 0;
|
||||||
break;
|
break;
|
||||||
if (!for_busy && !(temp & I2SR_IBB))
|
}
|
||||||
|
if (!for_busy && !(temp & I2SR_IBB)) {
|
||||||
|
i2c_imx->stopped = 1;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
|
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
|
||||||
dev_dbg(&i2c_imx->adapter.dev,
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
"<%s> I2C bus is busy\n", __func__);
|
"<%s> I2C bus is busy\n", __func__);
|
||||||
|
@ -538,7 +542,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
||||||
result = i2c_imx_bus_busy(i2c_imx, 1);
|
result = i2c_imx_bus_busy(i2c_imx, 1);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
i2c_imx->stopped = 0;
|
|
||||||
|
|
||||||
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
|
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
|
||||||
temp &= ~I2CR_DMAEN;
|
temp &= ~I2CR_DMAEN;
|
||||||
|
@ -567,10 +570,8 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
|
||||||
udelay(i2c_imx->disable_delay);
|
udelay(i2c_imx->disable_delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!i2c_imx->stopped) {
|
if (!i2c_imx->stopped)
|
||||||
i2c_imx_bus_busy(i2c_imx, 0);
|
i2c_imx_bus_busy(i2c_imx, 0);
|
||||||
i2c_imx->stopped = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable I2C controller */
|
/* Disable I2C controller */
|
||||||
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
||||||
|
@ -668,9 +669,6 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||||
struct imx_i2c_dma *dma = i2c_imx->dma;
|
struct imx_i2c_dma *dma = i2c_imx->dma;
|
||||||
struct device *dev = &i2c_imx->adapter.dev;
|
struct device *dev = &i2c_imx->adapter.dev;
|
||||||
|
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
|
||||||
temp |= I2CR_DMAEN;
|
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
|
||||||
|
|
||||||
dma->chan_using = dma->chan_rx;
|
dma->chan_using = dma->chan_rx;
|
||||||
dma->dma_transfer_dir = DMA_DEV_TO_MEM;
|
dma->dma_transfer_dir = DMA_DEV_TO_MEM;
|
||||||
|
@ -727,7 +725,6 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
i2c_imx_bus_busy(i2c_imx, 0);
|
i2c_imx_bus_busy(i2c_imx, 0);
|
||||||
i2c_imx->stopped = 1;
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* For i2c master receiver repeat restart operation like:
|
* For i2c master receiver repeat restart operation like:
|
||||||
|
@ -783,6 +780,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
|
||||||
int i, result;
|
int i, result;
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
int block_data = msgs->flags & I2C_M_RECV_LEN;
|
||||||
|
int use_dma = i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data;
|
||||||
|
|
||||||
dev_dbg(&i2c_imx->adapter.dev,
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
"<%s> write slave address: addr=0x%x\n",
|
"<%s> write slave address: addr=0x%x\n",
|
||||||
|
@ -809,12 +807,14 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
|
||||||
*/
|
*/
|
||||||
if ((msgs->len - 1) || block_data)
|
if ((msgs->len - 1) || block_data)
|
||||||
temp &= ~I2CR_TXAK;
|
temp &= ~I2CR_TXAK;
|
||||||
|
if (use_dma)
|
||||||
|
temp |= I2CR_DMAEN;
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */
|
||||||
|
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
|
||||||
|
|
||||||
if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data)
|
if (use_dma)
|
||||||
return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
|
return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg);
|
||||||
|
|
||||||
/* read data */
|
/* read data */
|
||||||
|
@ -850,7 +850,6 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
|
||||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
i2c_imx_bus_busy(i2c_imx, 0);
|
i2c_imx_bus_busy(i2c_imx, 0);
|
||||||
i2c_imx->stopped = 1;
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* For i2c master receiver repeat restart operation like:
|
* For i2c master receiver repeat restart operation like:
|
||||||
|
|
|
@ -567,9 +567,6 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
|
||||||
dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
|
dev_dbg(i2c->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
|
||||||
msg->addr, msg->len, msg->flags, stop);
|
msg->addr, msg->len, msg->flags, stop);
|
||||||
|
|
||||||
if (msg->len == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The MX28 I2C IP block can only do PIO READ for transfer of to up
|
* The MX28 I2C IP block can only do PIO READ for transfer of to up
|
||||||
* 4 bytes of length. The write transfer is not limited as it can use
|
* 4 bytes of length. The write transfer is not limited as it can use
|
||||||
|
@ -683,6 +680,10 @@ static const struct i2c_algorithm mxs_i2c_algo = {
|
||||||
.functionality = mxs_i2c_func,
|
.functionality = mxs_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks mxs_i2c_quirks = {
|
||||||
|
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, uint32_t speed)
|
static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, uint32_t speed)
|
||||||
{
|
{
|
||||||
/* The I2C block clock runs at 24MHz */
|
/* The I2C block clock runs at 24MHz */
|
||||||
|
@ -854,6 +855,7 @@ static int mxs_i2c_probe(struct platform_device *pdev)
|
||||||
strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name));
|
strlcpy(adap->name, "MXS I2C adapter", sizeof(adap->name));
|
||||||
adap->owner = THIS_MODULE;
|
adap->owner = THIS_MODULE;
|
||||||
adap->algo = &mxs_i2c_algo;
|
adap->algo = &mxs_i2c_algo;
|
||||||
|
adap->quirks = &mxs_i2c_quirks;
|
||||||
adap->dev.parent = dev;
|
adap->dev.parent = dev;
|
||||||
adap->nr = pdev->id;
|
adap->nr = pdev->id;
|
||||||
adap->dev.of_node = pdev->dev.of_node;
|
adap->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
|
@ -0,0 +1,495 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Actions Semiconductor Owl SoC's I2C driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Actions Semi Inc.
|
||||||
|
* Author: David Liu <liuwei@actions-semi.com>
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Linaro Ltd.
|
||||||
|
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
|
||||||
|
/* I2C registers */
|
||||||
|
#define OWL_I2C_REG_CTL 0x0000
|
||||||
|
#define OWL_I2C_REG_CLKDIV 0x0004
|
||||||
|
#define OWL_I2C_REG_STAT 0x0008
|
||||||
|
#define OWL_I2C_REG_ADDR 0x000C
|
||||||
|
#define OWL_I2C_REG_TXDAT 0x0010
|
||||||
|
#define OWL_I2C_REG_RXDAT 0x0014
|
||||||
|
#define OWL_I2C_REG_CMD 0x0018
|
||||||
|
#define OWL_I2C_REG_FIFOCTL 0x001C
|
||||||
|
#define OWL_I2C_REG_FIFOSTAT 0x0020
|
||||||
|
#define OWL_I2C_REG_DATCNT 0x0024
|
||||||
|
#define OWL_I2C_REG_RCNT 0x0028
|
||||||
|
|
||||||
|
/* I2Cx_CTL Bit Mask */
|
||||||
|
#define OWL_I2C_CTL_RB BIT(1)
|
||||||
|
#define OWL_I2C_CTL_GBCC(x) (((x) & 0x3) << 2)
|
||||||
|
#define OWL_I2C_CTL_GBCC_NONE OWL_I2C_CTL_GBCC(0)
|
||||||
|
#define OWL_I2C_CTL_GBCC_START OWL_I2C_CTL_GBCC(1)
|
||||||
|
#define OWL_I2C_CTL_GBCC_STOP OWL_I2C_CTL_GBCC(2)
|
||||||
|
#define OWL_I2C_CTL_GBCC_RSTART OWL_I2C_CTL_GBCC(3)
|
||||||
|
#define OWL_I2C_CTL_IRQE BIT(5)
|
||||||
|
#define OWL_I2C_CTL_EN BIT(7)
|
||||||
|
#define OWL_I2C_CTL_AE BIT(8)
|
||||||
|
#define OWL_I2C_CTL_SHSM BIT(10)
|
||||||
|
|
||||||
|
#define OWL_I2C_DIV_FACTOR(x) ((x) & 0xff)
|
||||||
|
|
||||||
|
/* I2Cx_STAT Bit Mask */
|
||||||
|
#define OWL_I2C_STAT_RACK BIT(0)
|
||||||
|
#define OWL_I2C_STAT_BEB BIT(1)
|
||||||
|
#define OWL_I2C_STAT_IRQP BIT(2)
|
||||||
|
#define OWL_I2C_STAT_LAB BIT(3)
|
||||||
|
#define OWL_I2C_STAT_STPD BIT(4)
|
||||||
|
#define OWL_I2C_STAT_STAD BIT(5)
|
||||||
|
#define OWL_I2C_STAT_BBB BIT(6)
|
||||||
|
#define OWL_I2C_STAT_TCB BIT(7)
|
||||||
|
#define OWL_I2C_STAT_LBST BIT(8)
|
||||||
|
#define OWL_I2C_STAT_SAMB BIT(9)
|
||||||
|
#define OWL_I2C_STAT_SRGC BIT(10)
|
||||||
|
|
||||||
|
/* I2Cx_CMD Bit Mask */
|
||||||
|
#define OWL_I2C_CMD_SBE BIT(0)
|
||||||
|
#define OWL_I2C_CMD_RBE BIT(4)
|
||||||
|
#define OWL_I2C_CMD_DE BIT(8)
|
||||||
|
#define OWL_I2C_CMD_NS BIT(9)
|
||||||
|
#define OWL_I2C_CMD_SE BIT(10)
|
||||||
|
#define OWL_I2C_CMD_MSS BIT(11)
|
||||||
|
#define OWL_I2C_CMD_WRS BIT(12)
|
||||||
|
#define OWL_I2C_CMD_SECL BIT(15)
|
||||||
|
|
||||||
|
#define OWL_I2C_CMD_AS(x) (((x) & 0x7) << 1)
|
||||||
|
#define OWL_I2C_CMD_SAS(x) (((x) & 0x7) << 5)
|
||||||
|
|
||||||
|
/* I2Cx_FIFOCTL Bit Mask */
|
||||||
|
#define OWL_I2C_FIFOCTL_NIB BIT(0)
|
||||||
|
#define OWL_I2C_FIFOCTL_RFR BIT(1)
|
||||||
|
#define OWL_I2C_FIFOCTL_TFR BIT(2)
|
||||||
|
|
||||||
|
/* I2Cc_FIFOSTAT Bit Mask */
|
||||||
|
#define OWL_I2C_FIFOSTAT_RNB BIT(1)
|
||||||
|
#define OWL_I2C_FIFOSTAT_RFE BIT(2)
|
||||||
|
#define OWL_I2C_FIFOSTAT_TFF BIT(5)
|
||||||
|
#define OWL_I2C_FIFOSTAT_TFD GENMASK(23, 16)
|
||||||
|
#define OWL_I2C_FIFOSTAT_RFD GENMASK(15, 8)
|
||||||
|
|
||||||
|
/* I2C bus timeout */
|
||||||
|
#define OWL_I2C_TIMEOUT msecs_to_jiffies(4 * 1000)
|
||||||
|
|
||||||
|
#define OWL_I2C_MAX_RETRIES 50
|
||||||
|
|
||||||
|
#define OWL_I2C_DEF_SPEED_HZ 100000
|
||||||
|
#define OWL_I2C_MAX_SPEED_HZ 400000
|
||||||
|
|
||||||
|
struct owl_i2c_dev {
|
||||||
|
struct i2c_adapter adap;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
struct completion msg_complete;
|
||||||
|
struct clk *clk;
|
||||||
|
spinlock_t lock;
|
||||||
|
void __iomem *base;
|
||||||
|
unsigned long clk_rate;
|
||||||
|
u32 bus_freq;
|
||||||
|
u32 msg_ptr;
|
||||||
|
int err;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void owl_i2c_update_reg(void __iomem *reg, unsigned int val, bool state)
|
||||||
|
{
|
||||||
|
unsigned int regval;
|
||||||
|
|
||||||
|
regval = readl(reg);
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
regval |= val;
|
||||||
|
else
|
||||||
|
regval &= ~val;
|
||||||
|
|
||||||
|
writel(regval, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void owl_i2c_reset(struct owl_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
|
||||||
|
OWL_I2C_CTL_EN, false);
|
||||||
|
mdelay(1);
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
|
||||||
|
OWL_I2C_CTL_EN, true);
|
||||||
|
|
||||||
|
/* Clear status registers */
|
||||||
|
writel(0, i2c_dev->base + OWL_I2C_REG_STAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int owl_i2c_reset_fifo(struct owl_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
unsigned int val, timeout = 0;
|
||||||
|
|
||||||
|
/* Reset FIFO */
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
|
||||||
|
OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR,
|
||||||
|
true);
|
||||||
|
|
||||||
|
/* Wait 50ms for FIFO reset complete */
|
||||||
|
do {
|
||||||
|
val = readl(i2c_dev->base + OWL_I2C_REG_FIFOCTL);
|
||||||
|
if (!(val & (OWL_I2C_FIFOCTL_RFR | OWL_I2C_FIFOCTL_TFR)))
|
||||||
|
break;
|
||||||
|
usleep_range(500, 1000);
|
||||||
|
} while (timeout++ < OWL_I2C_MAX_RETRIES);
|
||||||
|
|
||||||
|
if (timeout > OWL_I2C_MAX_RETRIES) {
|
||||||
|
dev_err(&i2c_dev->adap.dev, "FIFO reset timeout\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void owl_i2c_set_freq(struct owl_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
val = DIV_ROUND_UP(i2c_dev->clk_rate, i2c_dev->bus_freq * 16);
|
||||||
|
|
||||||
|
/* Set clock divider factor */
|
||||||
|
writel(OWL_I2C_DIV_FACTOR(val), i2c_dev->base + OWL_I2C_REG_CLKDIV);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
|
||||||
|
{
|
||||||
|
struct owl_i2c_dev *i2c_dev = _dev;
|
||||||
|
struct i2c_msg *msg = i2c_dev->msg;
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned int stat, fifostat;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&i2c_dev->lock, flags);
|
||||||
|
|
||||||
|
i2c_dev->err = 0;
|
||||||
|
|
||||||
|
/* Handle NACK from slave */
|
||||||
|
fifostat = readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT);
|
||||||
|
if (fifostat & OWL_I2C_FIFOSTAT_RNB) {
|
||||||
|
i2c_dev->err = -ENXIO;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle bus error */
|
||||||
|
stat = readl(i2c_dev->base + OWL_I2C_REG_STAT);
|
||||||
|
if (stat & OWL_I2C_STAT_BEB) {
|
||||||
|
i2c_dev->err = -EIO;
|
||||||
|
goto stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle FIFO read */
|
||||||
|
if (msg->flags & I2C_M_RD) {
|
||||||
|
while ((readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
|
||||||
|
OWL_I2C_FIFOSTAT_RFE) && i2c_dev->msg_ptr < msg->len) {
|
||||||
|
msg->buf[i2c_dev->msg_ptr++] = readl(i2c_dev->base +
|
||||||
|
OWL_I2C_REG_RXDAT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Handle the remaining bytes which were not sent */
|
||||||
|
while (!(readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
|
||||||
|
OWL_I2C_FIFOSTAT_TFF) && i2c_dev->msg_ptr < msg->len) {
|
||||||
|
writel(msg->buf[i2c_dev->msg_ptr++],
|
||||||
|
i2c_dev->base + OWL_I2C_REG_TXDAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop:
|
||||||
|
/* Clear pending interrupts */
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_STAT,
|
||||||
|
OWL_I2C_STAT_IRQP, true);
|
||||||
|
|
||||||
|
complete_all(&i2c_dev->msg_complete);
|
||||||
|
spin_unlock_irqrestore(&i2c_dev->lock, flags);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 owl_i2c_func(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int owl_i2c_check_bus_busy(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||||
|
unsigned long timeout;
|
||||||
|
|
||||||
|
/* Check for Bus busy */
|
||||||
|
timeout = jiffies + OWL_I2C_TIMEOUT;
|
||||||
|
while (readl(i2c_dev->base + OWL_I2C_REG_STAT) & OWL_I2C_STAT_BBB) {
|
||||||
|
if (time_after(jiffies, timeout)) {
|
||||||
|
dev_err(&adap->dev, "Bus busy timeout\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int owl_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct owl_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
unsigned long time_left, flags;
|
||||||
|
unsigned int i2c_cmd, val;
|
||||||
|
unsigned int addr;
|
||||||
|
int ret, idx;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&i2c_dev->lock, flags);
|
||||||
|
|
||||||
|
/* Reset I2C controller */
|
||||||
|
owl_i2c_reset(i2c_dev);
|
||||||
|
|
||||||
|
/* Set bus frequency */
|
||||||
|
owl_i2c_set_freq(i2c_dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Spinlock should be released before calling reset FIFO and
|
||||||
|
* bus busy check since those functions may sleep
|
||||||
|
*/
|
||||||
|
spin_unlock_irqrestore(&i2c_dev->lock, flags);
|
||||||
|
|
||||||
|
/* Reset FIFO */
|
||||||
|
ret = owl_i2c_reset_fifo(i2c_dev);
|
||||||
|
if (ret)
|
||||||
|
goto unlocked_err_exit;
|
||||||
|
|
||||||
|
/* Check for bus busy */
|
||||||
|
ret = owl_i2c_check_bus_busy(adap);
|
||||||
|
if (ret)
|
||||||
|
goto unlocked_err_exit;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&i2c_dev->lock, flags);
|
||||||
|
|
||||||
|
/* Check for Arbitration lost */
|
||||||
|
val = readl(i2c_dev->base + OWL_I2C_REG_STAT);
|
||||||
|
if (val & OWL_I2C_STAT_LAB) {
|
||||||
|
val &= ~OWL_I2C_STAT_LAB;
|
||||||
|
writel(val, i2c_dev->base + OWL_I2C_REG_STAT);
|
||||||
|
ret = -EAGAIN;
|
||||||
|
goto err_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
reinit_completion(&i2c_dev->msg_complete);
|
||||||
|
|
||||||
|
/* Enable I2C controller interrupt */
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
|
||||||
|
OWL_I2C_CTL_IRQE, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select: FIFO enable, Master mode, Stop enable, Data count enable,
|
||||||
|
* Send start bit
|
||||||
|
*/
|
||||||
|
i2c_cmd = OWL_I2C_CMD_SECL | OWL_I2C_CMD_MSS | OWL_I2C_CMD_SE |
|
||||||
|
OWL_I2C_CMD_NS | OWL_I2C_CMD_DE | OWL_I2C_CMD_SBE;
|
||||||
|
|
||||||
|
/* Handle repeated start condition */
|
||||||
|
if (num > 1) {
|
||||||
|
/* Set internal address length and enable repeated start */
|
||||||
|
i2c_cmd |= OWL_I2C_CMD_AS(msgs[0].len + 1) |
|
||||||
|
OWL_I2C_CMD_SAS(1) | OWL_I2C_CMD_RBE;
|
||||||
|
|
||||||
|
/* Write slave address */
|
||||||
|
addr = i2c_8bit_addr_from_msg(&msgs[0]);
|
||||||
|
writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT);
|
||||||
|
|
||||||
|
/* Write internal register address */
|
||||||
|
for (idx = 0; idx < msgs[0].len; idx++)
|
||||||
|
writel(msgs[0].buf[idx],
|
||||||
|
i2c_dev->base + OWL_I2C_REG_TXDAT);
|
||||||
|
|
||||||
|
msg = &msgs[1];
|
||||||
|
} else {
|
||||||
|
/* Set address length */
|
||||||
|
i2c_cmd |= OWL_I2C_CMD_AS(1);
|
||||||
|
msg = &msgs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_dev->msg = msg;
|
||||||
|
i2c_dev->msg_ptr = 0;
|
||||||
|
|
||||||
|
/* Set data count for the message */
|
||||||
|
writel(msg->len, i2c_dev->base + OWL_I2C_REG_DATCNT);
|
||||||
|
|
||||||
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
|
writel(addr, i2c_dev->base + OWL_I2C_REG_TXDAT);
|
||||||
|
|
||||||
|
if (!(msg->flags & I2C_M_RD)) {
|
||||||
|
/* Write data to FIFO */
|
||||||
|
for (idx = 0; idx < msg->len; idx++) {
|
||||||
|
/* Check for FIFO full */
|
||||||
|
if (readl(i2c_dev->base + OWL_I2C_REG_FIFOSTAT) &
|
||||||
|
OWL_I2C_FIFOSTAT_TFF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
writel(msg->buf[idx],
|
||||||
|
i2c_dev->base + OWL_I2C_REG_TXDAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_dev->msg_ptr = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore the NACK if needed */
|
||||||
|
if (msg->flags & I2C_M_IGNORE_NAK)
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
|
||||||
|
OWL_I2C_FIFOCTL_NIB, true);
|
||||||
|
else
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_FIFOCTL,
|
||||||
|
OWL_I2C_FIFOCTL_NIB, false);
|
||||||
|
|
||||||
|
/* Start the transfer */
|
||||||
|
writel(i2c_cmd, i2c_dev->base + OWL_I2C_REG_CMD);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&i2c_dev->lock, flags);
|
||||||
|
|
||||||
|
time_left = wait_for_completion_timeout(&i2c_dev->msg_complete,
|
||||||
|
adap->timeout);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&i2c_dev->lock, flags);
|
||||||
|
if (time_left == 0) {
|
||||||
|
dev_err(&adap->dev, "Transaction timed out\n");
|
||||||
|
/* Send stop condition and release the bus */
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
|
||||||
|
OWL_I2C_CTL_GBCC_STOP | OWL_I2C_CTL_RB,
|
||||||
|
true);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto err_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i2c_dev->err < 0 ? i2c_dev->err : num;
|
||||||
|
|
||||||
|
err_exit:
|
||||||
|
spin_unlock_irqrestore(&i2c_dev->lock, flags);
|
||||||
|
|
||||||
|
unlocked_err_exit:
|
||||||
|
/* Disable I2C controller */
|
||||||
|
owl_i2c_update_reg(i2c_dev->base + OWL_I2C_REG_CTL,
|
||||||
|
OWL_I2C_CTL_EN, false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm owl_i2c_algorithm = {
|
||||||
|
.master_xfer = owl_i2c_master_xfer,
|
||||||
|
.functionality = owl_i2c_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks owl_i2c_quirks = {
|
||||||
|
.flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST,
|
||||||
|
.max_read_len = 240,
|
||||||
|
.max_write_len = 240,
|
||||||
|
.max_comb_1st_msg_len = 6,
|
||||||
|
.max_comb_2nd_msg_len = 240,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int owl_i2c_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct owl_i2c_dev *i2c_dev;
|
||||||
|
struct resource *res;
|
||||||
|
int ret, irq;
|
||||||
|
|
||||||
|
i2c_dev = devm_kzalloc(dev, sizeof(*i2c_dev), GFP_KERNEL);
|
||||||
|
if (!i2c_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
i2c_dev->base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(i2c_dev->base))
|
||||||
|
return PTR_ERR(i2c_dev->base);
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(dev, "failed to get IRQ number\n");
|
||||||
|
return irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_read_u32(dev->of_node, "clock-frequency",
|
||||||
|
&i2c_dev->bus_freq))
|
||||||
|
i2c_dev->bus_freq = OWL_I2C_DEF_SPEED_HZ;
|
||||||
|
|
||||||
|
/* We support only frequencies of 100k and 400k for now */
|
||||||
|
if (i2c_dev->bus_freq != OWL_I2C_DEF_SPEED_HZ &&
|
||||||
|
i2c_dev->bus_freq != OWL_I2C_MAX_SPEED_HZ) {
|
||||||
|
dev_err(dev, "invalid clock-frequency %d\n", i2c_dev->bus_freq);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_dev->clk = devm_clk_get(dev, NULL);
|
||||||
|
if (IS_ERR(i2c_dev->clk)) {
|
||||||
|
dev_err(dev, "failed to get clock\n");
|
||||||
|
return PTR_ERR(i2c_dev->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(i2c_dev->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
i2c_dev->clk_rate = clk_get_rate(i2c_dev->clk);
|
||||||
|
if (!i2c_dev->clk_rate) {
|
||||||
|
dev_err(dev, "input clock rate should not be zero\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto disable_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_completion(&i2c_dev->msg_complete);
|
||||||
|
spin_lock_init(&i2c_dev->lock);
|
||||||
|
i2c_dev->adap.owner = THIS_MODULE;
|
||||||
|
i2c_dev->adap.algo = &owl_i2c_algorithm;
|
||||||
|
i2c_dev->adap.timeout = OWL_I2C_TIMEOUT;
|
||||||
|
i2c_dev->adap.quirks = &owl_i2c_quirks;
|
||||||
|
i2c_dev->adap.dev.parent = dev;
|
||||||
|
i2c_dev->adap.dev.of_node = dev->of_node;
|
||||||
|
snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
|
||||||
|
"%s", "OWL I2C adapter");
|
||||||
|
i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, i2c_dev);
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, irq, owl_i2c_interrupt, 0, pdev->name,
|
||||||
|
i2c_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to request irq %d\n", irq);
|
||||||
|
goto disable_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i2c_add_adapter(&i2c_dev->adap);
|
||||||
|
|
||||||
|
disable_clk:
|
||||||
|
clk_disable_unprepare(i2c_dev->clk);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id owl_i2c_of_match[] = {
|
||||||
|
{ .compatible = "actions,s900-i2c" },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, owl_i2c_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver owl_i2c_driver = {
|
||||||
|
.probe = owl_i2c_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "owl-i2c",
|
||||||
|
.of_match_table = of_match_ptr(owl_i2c_of_match),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(owl_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("David Liu <liuwei@actions-semi.com>");
|
||||||
|
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
|
||||||
|
MODULE_DESCRIPTION("Actions Semiconductor Owl SoC's I2C driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -365,7 +365,6 @@ static int pasemi_smb_probe(struct pci_dev *dev,
|
||||||
smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||||
smbus->adapter.algo = &smbus_algorithm;
|
smbus->adapter.algo = &smbus_algorithm;
|
||||||
smbus->adapter.algo_data = smbus;
|
smbus->adapter.algo_data = smbus;
|
||||||
smbus->adapter.nr = PCI_FUNC(dev->devfn);
|
|
||||||
|
|
||||||
/* set up the sysfs linkage to our parent device */
|
/* set up the sysfs linkage to our parent device */
|
||||||
smbus->adapter.dev.parent = &dev->dev;
|
smbus->adapter.dev.parent = &dev->dev;
|
||||||
|
@ -373,7 +372,7 @@ static int pasemi_smb_probe(struct pci_dev *dev,
|
||||||
reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
|
reg_write(smbus, REG_CTL, (CTL_MTR | CTL_MRR |
|
||||||
(CLK_100K_DIV & CTL_CLK_M)));
|
(CLK_100K_DIV & CTL_CLK_M)));
|
||||||
|
|
||||||
error = i2c_add_numbered_adapter(&smbus->adapter);
|
error = i2c_add_adapter(&smbus->adapter);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_release_region;
|
goto out_release_region;
|
||||||
|
|
||||||
|
|
|
@ -444,16 +444,6 @@ static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd(
|
||||||
{
|
{
|
||||||
enum pmcmsptwi_xfer_result retval;
|
enum pmcmsptwi_xfer_result retval;
|
||||||
|
|
||||||
if ((cmd->type == MSP_TWI_CMD_WRITE && cmd->write_len == 0) ||
|
|
||||||
(cmd->type == MSP_TWI_CMD_READ && cmd->read_len == 0) ||
|
|
||||||
(cmd->type == MSP_TWI_CMD_WRITE_READ &&
|
|
||||||
(cmd->read_len == 0 || cmd->write_len == 0))) {
|
|
||||||
dev_err(&pmcmsptwi_adapter.dev,
|
|
||||||
"%s: Cannot transfer less than 1 byte\n",
|
|
||||||
__func__);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&data->lock);
|
mutex_lock(&data->lock);
|
||||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||||
"Setting address to 0x%04x\n", cmd->addr);
|
"Setting address to 0x%04x\n", cmd->addr);
|
||||||
|
@ -532,11 +522,6 @@ static int pmcmsptwi_master_xfer(struct i2c_adapter *adap,
|
||||||
cmd.write_data = msg->buf;
|
cmd.write_data = msg->buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg->len == 0) {
|
|
||||||
dev_err(&adap->dev, "Zero-byte messages unsupported\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.addr = msg->addr;
|
cmd.addr = msg->addr;
|
||||||
|
|
||||||
if (msg->flags & I2C_M_TEN) {
|
if (msg->flags & I2C_M_TEN) {
|
||||||
|
@ -578,7 +563,7 @@ static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = {
|
static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = {
|
||||||
.flags = I2C_AQ_COMB_WRITE_THEN_READ,
|
.flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN,
|
||||||
.max_write_len = MSP_MAX_BYTES_PER_RW,
|
.max_write_len = MSP_MAX_BYTES_PER_RW,
|
||||||
.max_read_len = MSP_MAX_BYTES_PER_RW,
|
.max_read_len = MSP_MAX_BYTES_PER_RW,
|
||||||
.max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW,
|
.max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW,
|
||||||
|
|
|
@ -0,0 +1,673 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/qcom-geni-se.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#define SE_I2C_TX_TRANS_LEN 0x26c
|
||||||
|
#define SE_I2C_RX_TRANS_LEN 0x270
|
||||||
|
#define SE_I2C_SCL_COUNTERS 0x278
|
||||||
|
|
||||||
|
#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
|
||||||
|
M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
|
||||||
|
#define SE_I2C_ABORT BIT(1)
|
||||||
|
|
||||||
|
/* M_CMD OP codes for I2C */
|
||||||
|
#define I2C_WRITE 0x1
|
||||||
|
#define I2C_READ 0x2
|
||||||
|
#define I2C_WRITE_READ 0x3
|
||||||
|
#define I2C_ADDR_ONLY 0x4
|
||||||
|
#define I2C_BUS_CLEAR 0x6
|
||||||
|
#define I2C_STOP_ON_BUS 0x7
|
||||||
|
/* M_CMD params for I2C */
|
||||||
|
#define PRE_CMD_DELAY BIT(0)
|
||||||
|
#define TIMESTAMP_BEFORE BIT(1)
|
||||||
|
#define STOP_STRETCH BIT(2)
|
||||||
|
#define TIMESTAMP_AFTER BIT(3)
|
||||||
|
#define POST_COMMAND_DELAY BIT(4)
|
||||||
|
#define IGNORE_ADD_NACK BIT(6)
|
||||||
|
#define READ_FINISHED_WITH_ACK BIT(7)
|
||||||
|
#define BYPASS_ADDR_PHASE BIT(8)
|
||||||
|
#define SLV_ADDR_MSK GENMASK(15, 9)
|
||||||
|
#define SLV_ADDR_SHFT 9
|
||||||
|
/* I2C SCL COUNTER fields */
|
||||||
|
#define HIGH_COUNTER_MSK GENMASK(29, 20)
|
||||||
|
#define HIGH_COUNTER_SHFT 20
|
||||||
|
#define LOW_COUNTER_MSK GENMASK(19, 10)
|
||||||
|
#define LOW_COUNTER_SHFT 10
|
||||||
|
#define CYCLE_COUNTER_MSK GENMASK(9, 0)
|
||||||
|
|
||||||
|
enum geni_i2c_err_code {
|
||||||
|
GP_IRQ0,
|
||||||
|
NACK,
|
||||||
|
GP_IRQ2,
|
||||||
|
BUS_PROTO,
|
||||||
|
ARB_LOST,
|
||||||
|
GP_IRQ5,
|
||||||
|
GENI_OVERRUN,
|
||||||
|
GENI_ILLEGAL_CMD,
|
||||||
|
GENI_ABORT_DONE,
|
||||||
|
GENI_TIMEOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DM_I2C_CB_ERR ((BIT(NACK) | BIT(BUS_PROTO) | BIT(ARB_LOST)) \
|
||||||
|
<< 5)
|
||||||
|
|
||||||
|
#define I2C_AUTO_SUSPEND_DELAY 250
|
||||||
|
#define KHZ(freq) (1000 * freq)
|
||||||
|
#define PACKING_BYTES_PW 4
|
||||||
|
|
||||||
|
#define ABORT_TIMEOUT HZ
|
||||||
|
#define XFER_TIMEOUT HZ
|
||||||
|
#define RST_TIMEOUT HZ
|
||||||
|
|
||||||
|
struct geni_i2c_dev {
|
||||||
|
struct geni_se se;
|
||||||
|
u32 tx_wm;
|
||||||
|
int irq;
|
||||||
|
int err;
|
||||||
|
struct i2c_adapter adap;
|
||||||
|
struct completion done;
|
||||||
|
struct i2c_msg *cur;
|
||||||
|
int cur_wr;
|
||||||
|
int cur_rd;
|
||||||
|
spinlock_t lock;
|
||||||
|
u32 clk_freq_out;
|
||||||
|
const struct geni_i2c_clk_fld *clk_fld;
|
||||||
|
int suspended;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct geni_i2c_err_log {
|
||||||
|
int err;
|
||||||
|
const char *msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct geni_i2c_err_log gi2c_log[] = {
|
||||||
|
[GP_IRQ0] = {-EIO, "Unknown I2C err GP_IRQ0"},
|
||||||
|
[NACK] = {-ENXIO, "NACK: slv unresponsive, check its power/reset-ln"},
|
||||||
|
[GP_IRQ2] = {-EIO, "Unknown I2C err GP IRQ2"},
|
||||||
|
[BUS_PROTO] = {-EPROTO, "Bus proto err, noisy/unepxected start/stop"},
|
||||||
|
[ARB_LOST] = {-EAGAIN, "Bus arbitration lost, clock line undriveable"},
|
||||||
|
[GP_IRQ5] = {-EIO, "Unknown I2C err GP IRQ5"},
|
||||||
|
[GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
|
||||||
|
[GENI_ILLEGAL_CMD] = {-EIO, "Illegal cmd, check GENI cmd-state machine"},
|
||||||
|
[GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
|
||||||
|
[GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct geni_i2c_clk_fld {
|
||||||
|
u32 clk_freq_out;
|
||||||
|
u8 clk_div;
|
||||||
|
u8 t_high_cnt;
|
||||||
|
u8 t_low_cnt;
|
||||||
|
u8 t_cycle_cnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hardware uses the underlying formula to calculate time periods of
|
||||||
|
* SCL clock cycle. Firmware uses some additional cycles excluded from the
|
||||||
|
* below formula and it is confirmed that the time periods are within
|
||||||
|
* specification limits.
|
||||||
|
*
|
||||||
|
* time of high period of SCL: t_high = (t_high_cnt * clk_div) / source_clock
|
||||||
|
* time of low period of SCL: t_low = (t_low_cnt * clk_div) / source_clock
|
||||||
|
* time of full period of SCL: t_cycle = (t_cycle_cnt * clk_div) / source_clock
|
||||||
|
* clk_freq_out = t / t_cycle
|
||||||
|
* source_clock = 19.2 MHz
|
||||||
|
*/
|
||||||
|
static const struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
|
||||||
|
{KHZ(100), 7, 10, 11, 26},
|
||||||
|
{KHZ(400), 2, 5, 12, 24},
|
||||||
|
{KHZ(1000), 1, 3, 9, 18},
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
|
||||||
|
if (itr->clk_freq_out == gi2c->clk_freq_out) {
|
||||||
|
gi2c->clk_fld = itr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c)
|
||||||
|
{
|
||||||
|
const struct geni_i2c_clk_fld *itr = gi2c->clk_fld;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
writel_relaxed(0, gi2c->se.base + SE_GENI_CLK_SEL);
|
||||||
|
|
||||||
|
val = (itr->clk_div << CLK_DIV_SHFT) | SER_CLK_EN;
|
||||||
|
writel_relaxed(val, gi2c->se.base + GENI_SER_M_CLK_CFG);
|
||||||
|
|
||||||
|
val = itr->t_high_cnt << HIGH_COUNTER_SHFT;
|
||||||
|
val |= itr->t_low_cnt << LOW_COUNTER_SHFT;
|
||||||
|
val |= itr->t_cycle_cnt;
|
||||||
|
writel_relaxed(val, gi2c->se.base + SE_I2C_SCL_COUNTERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geni_i2c_err_misc(struct geni_i2c_dev *gi2c)
|
||||||
|
{
|
||||||
|
u32 m_cmd = readl_relaxed(gi2c->se.base + SE_GENI_M_CMD0);
|
||||||
|
u32 m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
|
||||||
|
u32 geni_s = readl_relaxed(gi2c->se.base + SE_GENI_STATUS);
|
||||||
|
u32 geni_ios = readl_relaxed(gi2c->se.base + SE_GENI_IOS);
|
||||||
|
u32 dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
|
||||||
|
u32 rx_st, tx_st;
|
||||||
|
|
||||||
|
if (dma) {
|
||||||
|
rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
|
||||||
|
tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
|
||||||
|
} else {
|
||||||
|
rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
|
||||||
|
tx_st = readl_relaxed(gi2c->se.base + SE_GENI_TX_FIFO_STATUS);
|
||||||
|
}
|
||||||
|
dev_dbg(gi2c->se.dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
|
||||||
|
dma, tx_st, rx_st, m_stat);
|
||||||
|
dev_dbg(gi2c->se.dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
|
||||||
|
m_cmd, geni_s, geni_ios);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
|
||||||
|
{
|
||||||
|
if (!gi2c->err)
|
||||||
|
gi2c->err = gi2c_log[err].err;
|
||||||
|
if (gi2c->cur)
|
||||||
|
dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
|
||||||
|
gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
|
||||||
|
|
||||||
|
if (err != NACK && err != GENI_ABORT_DONE) {
|
||||||
|
dev_err(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
|
||||||
|
geni_i2c_err_misc(gi2c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
||||||
|
{
|
||||||
|
struct geni_i2c_dev *gi2c = dev;
|
||||||
|
int j;
|
||||||
|
u32 m_stat;
|
||||||
|
u32 rx_st;
|
||||||
|
u32 dm_tx_st;
|
||||||
|
u32 dm_rx_st;
|
||||||
|
u32 dma;
|
||||||
|
struct i2c_msg *cur;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&gi2c->lock, flags);
|
||||||
|
m_stat = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
|
||||||
|
rx_st = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFO_STATUS);
|
||||||
|
dm_tx_st = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
|
||||||
|
dm_rx_st = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
|
||||||
|
dma = readl_relaxed(gi2c->se.base + SE_GENI_DMA_MODE_EN);
|
||||||
|
cur = gi2c->cur;
|
||||||
|
|
||||||
|
if (!cur ||
|
||||||
|
m_stat & (M_CMD_FAILURE_EN | M_CMD_ABORT_EN) ||
|
||||||
|
dm_rx_st & (DM_I2C_CB_ERR)) {
|
||||||
|
if (m_stat & M_GP_IRQ_1_EN)
|
||||||
|
geni_i2c_err(gi2c, NACK);
|
||||||
|
if (m_stat & M_GP_IRQ_3_EN)
|
||||||
|
geni_i2c_err(gi2c, BUS_PROTO);
|
||||||
|
if (m_stat & M_GP_IRQ_4_EN)
|
||||||
|
geni_i2c_err(gi2c, ARB_LOST);
|
||||||
|
if (m_stat & M_CMD_OVERRUN_EN)
|
||||||
|
geni_i2c_err(gi2c, GENI_OVERRUN);
|
||||||
|
if (m_stat & M_ILLEGAL_CMD_EN)
|
||||||
|
geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
|
||||||
|
if (m_stat & M_CMD_ABORT_EN)
|
||||||
|
geni_i2c_err(gi2c, GENI_ABORT_DONE);
|
||||||
|
if (m_stat & M_GP_IRQ_0_EN)
|
||||||
|
geni_i2c_err(gi2c, GP_IRQ0);
|
||||||
|
|
||||||
|
/* Disable the TX Watermark interrupt to stop TX */
|
||||||
|
if (!dma)
|
||||||
|
writel_relaxed(0, gi2c->se.base +
|
||||||
|
SE_GENI_TX_WATERMARK_REG);
|
||||||
|
goto irqret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dma) {
|
||||||
|
dev_dbg(gi2c->se.dev, "i2c dma tx:0x%x, dma rx:0x%x\n",
|
||||||
|
dm_tx_st, dm_rx_st);
|
||||||
|
goto irqret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur->flags & I2C_M_RD &&
|
||||||
|
m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) {
|
||||||
|
u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
|
||||||
|
|
||||||
|
for (j = 0; j < rxcnt; j++) {
|
||||||
|
u32 val;
|
||||||
|
int p = 0;
|
||||||
|
|
||||||
|
val = readl_relaxed(gi2c->se.base + SE_GENI_RX_FIFOn);
|
||||||
|
while (gi2c->cur_rd < cur->len && p < sizeof(val)) {
|
||||||
|
cur->buf[gi2c->cur_rd++] = val & 0xff;
|
||||||
|
val >>= 8;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (gi2c->cur_rd == cur->len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (!(cur->flags & I2C_M_RD) &&
|
||||||
|
m_stat & M_TX_FIFO_WATERMARK_EN) {
|
||||||
|
for (j = 0; j < gi2c->tx_wm; j++) {
|
||||||
|
u32 temp;
|
||||||
|
u32 val = 0;
|
||||||
|
int p = 0;
|
||||||
|
|
||||||
|
while (gi2c->cur_wr < cur->len && p < sizeof(val)) {
|
||||||
|
temp = cur->buf[gi2c->cur_wr++];
|
||||||
|
val |= temp << (p * 8);
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
writel_relaxed(val, gi2c->se.base + SE_GENI_TX_FIFOn);
|
||||||
|
/* TX Complete, Disable the TX Watermark interrupt */
|
||||||
|
if (gi2c->cur_wr == cur->len) {
|
||||||
|
writel_relaxed(0, gi2c->se.base +
|
||||||
|
SE_GENI_TX_WATERMARK_REG);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
irqret:
|
||||||
|
if (m_stat)
|
||||||
|
writel_relaxed(m_stat, gi2c->se.base + SE_GENI_M_IRQ_CLEAR);
|
||||||
|
|
||||||
|
if (dma) {
|
||||||
|
if (dm_tx_st)
|
||||||
|
writel_relaxed(dm_tx_st, gi2c->se.base +
|
||||||
|
SE_DMA_TX_IRQ_CLR);
|
||||||
|
if (dm_rx_st)
|
||||||
|
writel_relaxed(dm_rx_st, gi2c->se.base +
|
||||||
|
SE_DMA_RX_IRQ_CLR);
|
||||||
|
}
|
||||||
|
/* if this is err with done-bit not set, handle that through timeout. */
|
||||||
|
if (m_stat & M_CMD_DONE_EN || m_stat & M_CMD_ABORT_EN)
|
||||||
|
complete(&gi2c->done);
|
||||||
|
else if (dm_tx_st & TX_DMA_DONE || dm_tx_st & TX_RESET_DONE)
|
||||||
|
complete(&gi2c->done);
|
||||||
|
else if (dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
|
||||||
|
complete(&gi2c->done);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&gi2c->lock, flags);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
unsigned long time_left = ABORT_TIMEOUT;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&gi2c->lock, flags);
|
||||||
|
geni_i2c_err(gi2c, GENI_TIMEOUT);
|
||||||
|
gi2c->cur = NULL;
|
||||||
|
geni_se_abort_m_cmd(&gi2c->se);
|
||||||
|
spin_unlock_irqrestore(&gi2c->lock, flags);
|
||||||
|
do {
|
||||||
|
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||||
|
val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS);
|
||||||
|
} while (!(val & M_CMD_ABORT_EN) && time_left);
|
||||||
|
|
||||||
|
if (!(val & M_CMD_ABORT_EN))
|
||||||
|
dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
unsigned long time_left = RST_TIMEOUT;
|
||||||
|
|
||||||
|
writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
|
||||||
|
do {
|
||||||
|
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||||
|
val = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
|
||||||
|
} while (!(val & RX_RESET_DONE) && time_left);
|
||||||
|
|
||||||
|
if (!(val & RX_RESET_DONE))
|
||||||
|
dev_err(gi2c->se.dev, "Timeout resetting RX_FSM\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
unsigned long time_left = RST_TIMEOUT;
|
||||||
|
|
||||||
|
writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
|
||||||
|
do {
|
||||||
|
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||||
|
val = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
|
||||||
|
} while (!(val & TX_RESET_DONE) && time_left);
|
||||||
|
|
||||||
|
if (!(val & TX_RESET_DONE))
|
||||||
|
dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
||||||
|
u32 m_param)
|
||||||
|
{
|
||||||
|
dma_addr_t rx_dma;
|
||||||
|
enum geni_se_xfer_mode mode;
|
||||||
|
unsigned long time_left = XFER_TIMEOUT;
|
||||||
|
|
||||||
|
gi2c->cur = msg;
|
||||||
|
mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
|
||||||
|
geni_se_select_mode(&gi2c->se, mode);
|
||||||
|
writel_relaxed(msg->len, gi2c->se.base + SE_I2C_RX_TRANS_LEN);
|
||||||
|
geni_se_setup_m_cmd(&gi2c->se, I2C_READ, m_param);
|
||||||
|
if (mode == GENI_SE_DMA) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = geni_se_rx_dma_prep(&gi2c->se, msg->buf, msg->len,
|
||||||
|
&rx_dma);
|
||||||
|
if (ret) {
|
||||||
|
mode = GENI_SE_FIFO;
|
||||||
|
geni_se_select_mode(&gi2c->se, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||||
|
if (!time_left)
|
||||||
|
geni_i2c_abort_xfer(gi2c);
|
||||||
|
|
||||||
|
gi2c->cur_rd = 0;
|
||||||
|
if (mode == GENI_SE_DMA) {
|
||||||
|
if (gi2c->err)
|
||||||
|
geni_i2c_rx_fsm_rst(gi2c);
|
||||||
|
geni_se_rx_dma_unprep(&gi2c->se, rx_dma, msg->len);
|
||||||
|
}
|
||||||
|
return gi2c->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
|
||||||
|
u32 m_param)
|
||||||
|
{
|
||||||
|
dma_addr_t tx_dma;
|
||||||
|
enum geni_se_xfer_mode mode;
|
||||||
|
unsigned long time_left;
|
||||||
|
|
||||||
|
gi2c->cur = msg;
|
||||||
|
mode = msg->len > 32 ? GENI_SE_DMA : GENI_SE_FIFO;
|
||||||
|
geni_se_select_mode(&gi2c->se, mode);
|
||||||
|
writel_relaxed(msg->len, gi2c->se.base + SE_I2C_TX_TRANS_LEN);
|
||||||
|
geni_se_setup_m_cmd(&gi2c->se, I2C_WRITE, m_param);
|
||||||
|
if (mode == GENI_SE_DMA) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = geni_se_tx_dma_prep(&gi2c->se, msg->buf, msg->len,
|
||||||
|
&tx_dma);
|
||||||
|
if (ret) {
|
||||||
|
mode = GENI_SE_FIFO;
|
||||||
|
geni_se_select_mode(&gi2c->se, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == GENI_SE_FIFO) /* Get FIFO IRQ */
|
||||||
|
writel_relaxed(1, gi2c->se.base + SE_GENI_TX_WATERMARK_REG);
|
||||||
|
|
||||||
|
time_left = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT);
|
||||||
|
if (!time_left)
|
||||||
|
geni_i2c_abort_xfer(gi2c);
|
||||||
|
|
||||||
|
gi2c->cur_wr = 0;
|
||||||
|
if (mode == GENI_SE_DMA) {
|
||||||
|
if (gi2c->err)
|
||||||
|
geni_i2c_tx_fsm_rst(gi2c);
|
||||||
|
geni_se_tx_dma_unprep(&gi2c->se, tx_dma, msg->len);
|
||||||
|
}
|
||||||
|
return gi2c->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int geni_i2c_xfer(struct i2c_adapter *adap,
|
||||||
|
struct i2c_msg msgs[],
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
gi2c->err = 0;
|
||||||
|
reinit_completion(&gi2c->done);
|
||||||
|
ret = pm_runtime_get_sync(gi2c->se.dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
|
||||||
|
pm_runtime_put_noidle(gi2c->se.dev);
|
||||||
|
/* Set device in suspended since resume failed */
|
||||||
|
pm_runtime_set_suspended(gi2c->se.dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
qcom_geni_i2c_conf(gi2c);
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
u32 m_param = i < (num - 1) ? STOP_STRETCH : 0;
|
||||||
|
|
||||||
|
m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
|
||||||
|
|
||||||
|
if (msgs[i].flags & I2C_M_RD)
|
||||||
|
ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
|
||||||
|
else
|
||||||
|
ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret == 0)
|
||||||
|
ret = num;
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(gi2c->se.dev);
|
||||||
|
pm_runtime_put_autosuspend(gi2c->se.dev);
|
||||||
|
gi2c->cur = NULL;
|
||||||
|
gi2c->err = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 geni_i2c_func(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm geni_i2c_algo = {
|
||||||
|
.master_xfer = geni_i2c_xfer,
|
||||||
|
.functionality = geni_i2c_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int geni_i2c_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct geni_i2c_dev *gi2c;
|
||||||
|
struct resource *res;
|
||||||
|
u32 proto, tx_depth;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
|
||||||
|
if (!gi2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
gi2c->se.dev = &pdev->dev;
|
||||||
|
gi2c->se.wrapper = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
gi2c->se.base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(gi2c->se.base))
|
||||||
|
return PTR_ERR(gi2c->se.base);
|
||||||
|
|
||||||
|
gi2c->se.clk = devm_clk_get(&pdev->dev, "se");
|
||||||
|
if (IS_ERR(gi2c->se.clk)) {
|
||||||
|
ret = PTR_ERR(gi2c->se.clk);
|
||||||
|
dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||||
|
&gi2c->clk_freq_out);
|
||||||
|
if (ret) {
|
||||||
|
dev_info(&pdev->dev,
|
||||||
|
"Bus frequency not specified, default to 100kHz.\n");
|
||||||
|
gi2c->clk_freq_out = KHZ(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
gi2c->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (gi2c->irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "IRQ error for i2c-geni\n");
|
||||||
|
return gi2c->irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = geni_i2c_clk_map_idx(gi2c);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Invalid clk frequency %d Hz: %d\n",
|
||||||
|
gi2c->clk_freq_out, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi2c->adap.algo = &geni_i2c_algo;
|
||||||
|
init_completion(&gi2c->done);
|
||||||
|
spin_lock_init(&gi2c->lock);
|
||||||
|
platform_set_drvdata(pdev, gi2c);
|
||||||
|
ret = devm_request_irq(&pdev->dev, gi2c->irq, geni_i2c_irq,
|
||||||
|
IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Request_irq failed:%d: err:%d\n",
|
||||||
|
gi2c->irq, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Disable the interrupt so that the system can enter low-power mode */
|
||||||
|
disable_irq(gi2c->irq);
|
||||||
|
i2c_set_adapdata(&gi2c->adap, gi2c);
|
||||||
|
gi2c->adap.dev.parent = &pdev->dev;
|
||||||
|
gi2c->adap.dev.of_node = pdev->dev.of_node;
|
||||||
|
strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
|
||||||
|
|
||||||
|
ret = geni_se_resources_on(&gi2c->se);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Error turning on resources %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
proto = geni_se_read_proto(&gi2c->se);
|
||||||
|
tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se);
|
||||||
|
if (proto != GENI_SE_I2C) {
|
||||||
|
dev_err(&pdev->dev, "Invalid proto %d\n", proto);
|
||||||
|
geni_se_resources_off(&gi2c->se);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
gi2c->tx_wm = tx_depth - 1;
|
||||||
|
geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth);
|
||||||
|
geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW,
|
||||||
|
true, true, true);
|
||||||
|
ret = geni_se_resources_off(&gi2c->se);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Error turning off resources %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
|
||||||
|
|
||||||
|
ret = i2c_add_adapter(&gi2c->adap);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Error adding i2c adapter %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi2c->suspended = 1;
|
||||||
|
pm_runtime_set_suspended(gi2c->se.dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(gi2c->se.dev, I2C_AUTO_SUSPEND_DELAY);
|
||||||
|
pm_runtime_use_autosuspend(gi2c->se.dev);
|
||||||
|
pm_runtime_enable(gi2c->se.dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int geni_i2c_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
pm_runtime_disable(gi2c->se.dev);
|
||||||
|
i2c_del_adapter(&gi2c->adap);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
disable_irq(gi2c->irq);
|
||||||
|
ret = geni_se_resources_off(&gi2c->se);
|
||||||
|
if (ret) {
|
||||||
|
enable_irq(gi2c->irq);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
gi2c->suspended = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
ret = geni_se_resources_on(&gi2c->se);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
enable_irq(gi2c->irq);
|
||||||
|
gi2c->suspended = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (!gi2c->suspended) {
|
||||||
|
geni_i2c_runtime_suspend(dev);
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
pm_runtime_set_suspended(dev);
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops geni_i2c_pm_ops = {
|
||||||
|
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(geni_i2c_suspend_noirq, NULL)
|
||||||
|
SET_RUNTIME_PM_OPS(geni_i2c_runtime_suspend, geni_i2c_runtime_resume,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id geni_i2c_dt_match[] = {
|
||||||
|
{ .compatible = "qcom,geni-i2c" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);
|
||||||
|
|
||||||
|
static struct platform_driver geni_i2c_driver = {
|
||||||
|
.probe = geni_i2c_probe,
|
||||||
|
.remove = geni_i2c_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "geni_i2c",
|
||||||
|
.pm = &geni_i2c_pm_ops,
|
||||||
|
.of_match_table = geni_i2c_dt_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(geni_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("I2C Controller Driver for GENI based QUP cores");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -19,6 +19,7 @@
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
#include <linux/bitops.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
|
@ -112,9 +113,10 @@
|
||||||
#define ID_ARBLOST (1 << 3)
|
#define ID_ARBLOST (1 << 3)
|
||||||
#define ID_NACK (1 << 4)
|
#define ID_NACK (1 << 4)
|
||||||
/* persistent flags */
|
/* persistent flags */
|
||||||
#define ID_P_NO_RXDMA (1 << 30) /* HW forbids RXDMA sometimes */
|
#define ID_P_REP_AFTER_RD BIT(29)
|
||||||
#define ID_P_PM_BLOCKED (1 << 31)
|
#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */
|
||||||
#define ID_P_MASK (ID_P_PM_BLOCKED | ID_P_NO_RXDMA)
|
#define ID_P_PM_BLOCKED BIT(31)
|
||||||
|
#define ID_P_MASK GENMASK(31, 29)
|
||||||
|
|
||||||
enum rcar_i2c_type {
|
enum rcar_i2c_type {
|
||||||
I2C_RCAR_GEN1,
|
I2C_RCAR_GEN1,
|
||||||
|
@ -183,8 +185,6 @@ static void rcar_i2c_set_scl(struct i2c_adapter *adap, int val)
|
||||||
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
|
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* No get_sda, because the HW only reports its bus free logic, not SDA itself */
|
|
||||||
|
|
||||||
static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val)
|
static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val)
|
||||||
{
|
{
|
||||||
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||||
|
@ -197,10 +197,19 @@ static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val)
|
||||||
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
|
rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int rcar_i2c_get_bus_free(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||||
|
|
||||||
|
return !(rcar_i2c_read(priv, ICMCR) & FSDA);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
static struct i2c_bus_recovery_info rcar_i2c_bri = {
|
static struct i2c_bus_recovery_info rcar_i2c_bri = {
|
||||||
.get_scl = rcar_i2c_get_scl,
|
.get_scl = rcar_i2c_get_scl,
|
||||||
.set_scl = rcar_i2c_set_scl,
|
.set_scl = rcar_i2c_set_scl,
|
||||||
.set_sda = rcar_i2c_set_sda,
|
.set_sda = rcar_i2c_set_sda,
|
||||||
|
.get_bus_free = rcar_i2c_get_bus_free,
|
||||||
.recover_bus = i2c_generic_scl_recovery,
|
.recover_bus = i2c_generic_scl_recovery,
|
||||||
};
|
};
|
||||||
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
||||||
|
@ -215,7 +224,7 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
||||||
|
|
||||||
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < LOOP_TIMEOUT; i++) {
|
for (i = 0; i < LOOP_TIMEOUT; i++) {
|
||||||
/* make sure that bus is not busy */
|
/* make sure that bus is not busy */
|
||||||
|
@ -226,13 +235,7 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||||
|
|
||||||
/* Waiting did not help, try to recover */
|
/* Waiting did not help, try to recover */
|
||||||
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
|
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
|
||||||
ret = i2c_recover_bus(&priv->adap);
|
return i2c_recover_bus(&priv->adap);
|
||||||
|
|
||||||
/* No failure when recovering, so check bus busy bit again */
|
|
||||||
if (ret == 0)
|
|
||||||
ret = (rcar_i2c_read(priv, ICMCR) & FSDA) ? -EBUSY : 0;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t)
|
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t)
|
||||||
|
@ -343,7 +346,10 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
|
||||||
rcar_i2c_write(priv, ICMSR, 0);
|
rcar_i2c_write(priv, ICMSR, 0);
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
||||||
} else {
|
} else {
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
if (priv->flags & ID_P_REP_AFTER_RD)
|
||||||
|
priv->flags &= ~ID_P_REP_AFTER_RD;
|
||||||
|
else
|
||||||
|
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
||||||
rcar_i2c_write(priv, ICMSR, 0);
|
rcar_i2c_write(priv, ICMSR, 0);
|
||||||
}
|
}
|
||||||
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
|
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
|
||||||
|
@ -548,15 +554,15 @@ static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
priv->pos++;
|
priv->pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* If next received data is the _LAST_, go to new phase. */
|
||||||
* If next received data is the _LAST_, go to STOP phase. Might be
|
if (priv->pos + 1 == msg->len) {
|
||||||
* overwritten by REP START when setting up a new msg. Not elegant
|
if (priv->flags & ID_LAST_MSG) {
|
||||||
* but the only stable sequence for REP START I have found so far.
|
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
|
||||||
* If you want to change this code, make sure sending one transfer with
|
} else {
|
||||||
* four messages (WR-RD-WR-RD) works!
|
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
||||||
*/
|
priv->flags |= ID_P_REP_AFTER_RD;
|
||||||
if (priv->pos + 1 >= msg->len)
|
}
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
|
}
|
||||||
|
|
||||||
if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
|
if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
|
||||||
rcar_i2c_next_msg(priv);
|
rcar_i2c_next_msg(priv);
|
||||||
|
@ -624,9 +630,11 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
||||||
struct rcar_i2c_priv *priv = ptr;
|
struct rcar_i2c_priv *priv = ptr;
|
||||||
u32 msr, val;
|
u32 msr, val;
|
||||||
|
|
||||||
/* Clear START or STOP as soon as we can */
|
/* Clear START or STOP immediately, except for REPSTART after read */
|
||||||
val = rcar_i2c_read(priv, ICMCR);
|
if (likely(!(priv->flags & ID_P_REP_AFTER_RD))) {
|
||||||
rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
|
val = rcar_i2c_read(priv, ICMCR);
|
||||||
|
rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
msr = rcar_i2c_read(priv, ICMSR);
|
msr = rcar_i2c_read(priv, ICMSR);
|
||||||
|
|
||||||
|
@ -795,14 +803,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++)
|
||||||
/* This HW can't send STOP after address phase */
|
|
||||||
if (msgs[i].len == 0) {
|
|
||||||
ret = -EOPNOTSUPP;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
rcar_i2c_request_dma(priv, msgs + i);
|
rcar_i2c_request_dma(priv, msgs + i);
|
||||||
}
|
|
||||||
|
|
||||||
/* init first message */
|
/* init first message */
|
||||||
priv->msg = msgs;
|
priv->msg = msgs;
|
||||||
|
@ -889,6 +891,10 @@ static const struct i2c_algorithm rcar_i2c_algo = {
|
||||||
.unreg_slave = rcar_unreg_slave,
|
.unreg_slave = rcar_unreg_slave,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks rcar_i2c_quirks = {
|
||||||
|
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id rcar_i2c_dt_ids[] = {
|
static const struct of_device_id rcar_i2c_dt_ids[] = {
|
||||||
{ .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 },
|
{ .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 },
|
||||||
{ .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 },
|
{ .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 },
|
||||||
|
@ -942,6 +948,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||||
adap->dev.parent = dev;
|
adap->dev.parent = dev;
|
||||||
adap->dev.of_node = dev->of_node;
|
adap->dev.of_node = dev->of_node;
|
||||||
adap->bus_recovery_info = &rcar_i2c_bri;
|
adap->bus_recovery_info = &rcar_i2c_bri;
|
||||||
|
adap->quirks = &rcar_i2c_quirks;
|
||||||
i2c_set_adapdata(adap, priv);
|
i2c_set_adapdata(adap, priv);
|
||||||
strlcpy(adap->name, pdev->name, sizeof(adap->name));
|
strlcpy(adap->name, pdev->name, sizeof(adap->name));
|
||||||
|
|
||||||
|
|
|
@ -919,9 +919,9 @@ static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb,
|
||||||
|
|
||||||
if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) ||
|
if ((val == CPUFREQ_POSTCHANGE && delta_f < 0) ||
|
||||||
(val == CPUFREQ_PRECHANGE && delta_f > 0)) {
|
(val == CPUFREQ_PRECHANGE && delta_f > 0)) {
|
||||||
i2c_lock_adapter(&i2c->adap);
|
i2c_lock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
ret = s3c24xx_i2c_clockrate(i2c, &got);
|
ret = s3c24xx_i2c_clockrate(i2c, &got);
|
||||||
i2c_unlock_adapter(&i2c->adap);
|
i2c_unlock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(i2c->dev, "cannot find frequency (%d)\n", ret);
|
dev_err(i2c->dev, "cannot find frequency (%d)\n", ret);
|
||||||
|
|
|
@ -613,11 +613,6 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
|
||||||
static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
|
static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
|
||||||
bool do_init)
|
bool do_init)
|
||||||
{
|
{
|
||||||
if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) {
|
|
||||||
dev_err(pd->dev, "Unsupported zero length i2c read\n");
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (do_init) {
|
if (do_init) {
|
||||||
/* Initialize channel registers */
|
/* Initialize channel registers */
|
||||||
iic_wr(pd, ICCR, ICCR_SCP);
|
iic_wr(pd, ICCR, ICCR_SCP);
|
||||||
|
@ -758,6 +753,10 @@ static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
|
||||||
.master_xfer = sh_mobile_i2c_xfer,
|
.master_xfer = sh_mobile_i2c_xfer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
|
||||||
|
.flags = I2C_AQ_NO_ZERO_LEN_READ,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* r8a7740 chip has lasting errata on I2C I/O pad reset.
|
* r8a7740 chip has lasting errata on I2C I/O pad reset.
|
||||||
* this is work-around for it.
|
* this is work-around for it.
|
||||||
|
@ -925,6 +924,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||||
|
|
||||||
adap->owner = THIS_MODULE;
|
adap->owner = THIS_MODULE;
|
||||||
adap->algo = &sh_mobile_i2c_algorithm;
|
adap->algo = &sh_mobile_i2c_algorithm;
|
||||||
|
adap->quirks = &sh_mobile_i2c_quirks;
|
||||||
adap->dev.parent = &dev->dev;
|
adap->dev.parent = &dev->dev;
|
||||||
adap->retries = 5;
|
adap->retries = 5;
|
||||||
adap->nr = dev->id;
|
adap->nr = dev->id;
|
||||||
|
|
|
@ -590,9 +590,9 @@ static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
|
||||||
{
|
{
|
||||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
||||||
|
|
||||||
i2c_lock_adapter(&i2c_dev->adap);
|
i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
i2c_dev->is_suspended = true;
|
i2c_dev->is_suspended = true;
|
||||||
i2c_unlock_adapter(&i2c_dev->adap);
|
i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
return pm_runtime_force_suspend(pdev);
|
return pm_runtime_force_suspend(pdev);
|
||||||
}
|
}
|
||||||
|
@ -601,9 +601,9 @@ static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
|
||||||
{
|
{
|
||||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
||||||
|
|
||||||
i2c_lock_adapter(&i2c_dev->adap);
|
i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
i2c_dev->is_suspended = false;
|
i2c_dev->is_suspended = false;
|
||||||
i2c_unlock_adapter(&i2c_dev->adap);
|
i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
return pm_runtime_force_resume(pdev);
|
return pm_runtime_force_resume(pdev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -673,12 +673,6 @@ static int stu300_xfer_msg(struct i2c_adapter *adap,
|
||||||
msg->addr, msg->len, msg->flags, stop);
|
msg->addr, msg->len, msg->flags, stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zero-length messages are not supported by this hardware */
|
|
||||||
if (msg->len == 0) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto exit_disable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For some reason, sending the address sometimes fails when running
|
* For some reason, sending the address sometimes fails when running
|
||||||
* on the 13 MHz clock. No interrupt arrives. This is a work around,
|
* on the 13 MHz clock. No interrupt arrives. This is a work around,
|
||||||
|
@ -863,6 +857,10 @@ static const struct i2c_algorithm stu300_algo = {
|
||||||
.functionality = stu300_func,
|
.functionality = stu300_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks stu300_quirks = {
|
||||||
|
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
static int stu300_probe(struct platform_device *pdev)
|
static int stu300_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct stu300_dev *dev;
|
struct stu300_dev *dev;
|
||||||
|
@ -920,6 +918,8 @@ static int stu300_probe(struct platform_device *pdev)
|
||||||
adap->algo = &stu300_algo;
|
adap->algo = &stu300_algo;
|
||||||
adap->dev.parent = &pdev->dev;
|
adap->dev.parent = &pdev->dev;
|
||||||
adap->dev.of_node = pdev->dev.of_node;
|
adap->dev.of_node = pdev->dev.of_node;
|
||||||
|
adap->quirks = &stu300_quirks;
|
||||||
|
|
||||||
i2c_set_adapdata(adap, dev);
|
i2c_set_adapdata(adap, dev);
|
||||||
|
|
||||||
/* i2c device drivers may be active on return from add_adapter() */
|
/* i2c device drivers may be active on return from add_adapter() */
|
||||||
|
|
|
@ -115,6 +115,18 @@
|
||||||
|
|
||||||
#define I2C_CONFIG_LOAD_TIMEOUT 1000000
|
#define I2C_CONFIG_LOAD_TIMEOUT 1000000
|
||||||
|
|
||||||
|
#define I2C_MST_FIFO_CONTROL 0x0b4
|
||||||
|
#define I2C_MST_FIFO_CONTROL_RX_FLUSH BIT(0)
|
||||||
|
#define I2C_MST_FIFO_CONTROL_TX_FLUSH BIT(1)
|
||||||
|
#define I2C_MST_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 4)
|
||||||
|
#define I2C_MST_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 16)
|
||||||
|
|
||||||
|
#define I2C_MST_FIFO_STATUS 0x0b8
|
||||||
|
#define I2C_MST_FIFO_STATUS_RX_MASK 0xff
|
||||||
|
#define I2C_MST_FIFO_STATUS_RX_SHIFT 0
|
||||||
|
#define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000
|
||||||
|
#define I2C_MST_FIFO_STATUS_TX_SHIFT 16
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* msg_end_type: The bus control which need to be send at end of transfer.
|
* msg_end_type: The bus control which need to be send at end of transfer.
|
||||||
* @MSG_END_STOP: Send stop pulse at end of transfer.
|
* @MSG_END_STOP: Send stop pulse at end of transfer.
|
||||||
|
@ -154,6 +166,7 @@ struct tegra_i2c_hw_feature {
|
||||||
u16 clk_divisor_fast_plus_mode;
|
u16 clk_divisor_fast_plus_mode;
|
||||||
bool has_multi_master_mode;
|
bool has_multi_master_mode;
|
||||||
bool has_slcg_override_reg;
|
bool has_slcg_override_reg;
|
||||||
|
bool has_mst_fifo;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,13 +279,24 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
|
||||||
static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
|
static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev)
|
||||||
{
|
{
|
||||||
unsigned long timeout = jiffies + HZ;
|
unsigned long timeout = jiffies + HZ;
|
||||||
u32 val = i2c_readl(i2c_dev, I2C_FIFO_CONTROL);
|
unsigned int offset;
|
||||||
|
u32 mask, val;
|
||||||
|
|
||||||
val |= I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH;
|
if (i2c_dev->hw->has_mst_fifo) {
|
||||||
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
|
mask = I2C_MST_FIFO_CONTROL_TX_FLUSH |
|
||||||
|
I2C_MST_FIFO_CONTROL_RX_FLUSH;
|
||||||
|
offset = I2C_MST_FIFO_CONTROL;
|
||||||
|
} else {
|
||||||
|
mask = I2C_FIFO_CONTROL_TX_FLUSH |
|
||||||
|
I2C_FIFO_CONTROL_RX_FLUSH;
|
||||||
|
offset = I2C_FIFO_CONTROL;
|
||||||
|
}
|
||||||
|
|
||||||
while (i2c_readl(i2c_dev, I2C_FIFO_CONTROL) &
|
val = i2c_readl(i2c_dev, offset);
|
||||||
(I2C_FIFO_CONTROL_TX_FLUSH | I2C_FIFO_CONTROL_RX_FLUSH)) {
|
val |= mask;
|
||||||
|
i2c_writel(i2c_dev, val, offset);
|
||||||
|
|
||||||
|
while (i2c_readl(i2c_dev, offset) & mask) {
|
||||||
if (time_after(jiffies, timeout)) {
|
if (time_after(jiffies, timeout)) {
|
||||||
dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n");
|
dev_warn(i2c_dev->dev, "timeout waiting for fifo flush\n");
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
|
@ -290,9 +314,15 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
|
||||||
size_t buf_remaining = i2c_dev->msg_buf_remaining;
|
size_t buf_remaining = i2c_dev->msg_buf_remaining;
|
||||||
int words_to_transfer;
|
int words_to_transfer;
|
||||||
|
|
||||||
val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
|
if (i2c_dev->hw->has_mst_fifo) {
|
||||||
rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
|
val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
|
||||||
I2C_FIFO_STATUS_RX_SHIFT;
|
rx_fifo_avail = (val & I2C_MST_FIFO_STATUS_RX_MASK) >>
|
||||||
|
I2C_MST_FIFO_STATUS_RX_SHIFT;
|
||||||
|
} else {
|
||||||
|
val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
|
||||||
|
rx_fifo_avail = (val & I2C_FIFO_STATUS_RX_MASK) >>
|
||||||
|
I2C_FIFO_STATUS_RX_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
/* Rounds down to not include partial word at the end of buf */
|
/* Rounds down to not include partial word at the end of buf */
|
||||||
words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
|
words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
|
||||||
|
@ -321,6 +351,7 @@ static int tegra_i2c_empty_rx_fifo(struct tegra_i2c_dev *i2c_dev)
|
||||||
BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0);
|
BUG_ON(rx_fifo_avail > 0 && buf_remaining > 0);
|
||||||
i2c_dev->msg_buf_remaining = buf_remaining;
|
i2c_dev->msg_buf_remaining = buf_remaining;
|
||||||
i2c_dev->msg_buf = buf;
|
i2c_dev->msg_buf = buf;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,9 +363,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
|
||||||
size_t buf_remaining = i2c_dev->msg_buf_remaining;
|
size_t buf_remaining = i2c_dev->msg_buf_remaining;
|
||||||
int words_to_transfer;
|
int words_to_transfer;
|
||||||
|
|
||||||
val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
|
if (i2c_dev->hw->has_mst_fifo) {
|
||||||
tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
|
val = i2c_readl(i2c_dev, I2C_MST_FIFO_STATUS);
|
||||||
I2C_FIFO_STATUS_TX_SHIFT;
|
tx_fifo_avail = (val & I2C_MST_FIFO_STATUS_TX_MASK) >>
|
||||||
|
I2C_MST_FIFO_STATUS_TX_SHIFT;
|
||||||
|
} else {
|
||||||
|
val = i2c_readl(i2c_dev, I2C_FIFO_STATUS);
|
||||||
|
tx_fifo_avail = (val & I2C_FIFO_STATUS_TX_MASK) >>
|
||||||
|
I2C_FIFO_STATUS_TX_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
/* Rounds down to not include partial word at the end of buf */
|
/* Rounds down to not include partial word at the end of buf */
|
||||||
words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
|
words_to_transfer = buf_remaining / BYTES_PER_FIFO_WORD;
|
||||||
|
@ -516,9 +553,15 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
||||||
i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
|
i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2);
|
||||||
}
|
}
|
||||||
|
|
||||||
val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
|
if (i2c_dev->hw->has_mst_fifo) {
|
||||||
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
|
val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) |
|
||||||
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
|
I2C_MST_FIFO_CONTROL_RX_TRIG(1);
|
||||||
|
i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL);
|
||||||
|
} else {
|
||||||
|
val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
|
||||||
|
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
|
||||||
|
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
err = tegra_i2c_flush_fifos(i2c_dev);
|
err = tegra_i2c_flush_fifos(i2c_dev);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -802,6 +845,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
|
||||||
.has_config_load_reg = false,
|
.has_config_load_reg = false,
|
||||||
.has_multi_master_mode = false,
|
.has_multi_master_mode = false,
|
||||||
.has_slcg_override_reg = false,
|
.has_slcg_override_reg = false,
|
||||||
|
.has_mst_fifo = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
||||||
|
@ -814,6 +858,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
||||||
.has_config_load_reg = false,
|
.has_config_load_reg = false,
|
||||||
.has_multi_master_mode = false,
|
.has_multi_master_mode = false,
|
||||||
.has_slcg_override_reg = false,
|
.has_slcg_override_reg = false,
|
||||||
|
.has_mst_fifo = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
||||||
|
@ -826,6 +871,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
||||||
.has_config_load_reg = false,
|
.has_config_load_reg = false,
|
||||||
.has_multi_master_mode = false,
|
.has_multi_master_mode = false,
|
||||||
.has_slcg_override_reg = false,
|
.has_slcg_override_reg = false,
|
||||||
|
.has_mst_fifo = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
|
||||||
|
@ -838,6 +884,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
|
||||||
.has_config_load_reg = true,
|
.has_config_load_reg = true,
|
||||||
.has_multi_master_mode = false,
|
.has_multi_master_mode = false,
|
||||||
.has_slcg_override_reg = true,
|
.has_slcg_override_reg = true,
|
||||||
|
.has_mst_fifo = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
|
||||||
|
@ -850,10 +897,25 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
|
||||||
.has_config_load_reg = true,
|
.has_config_load_reg = true,
|
||||||
.has_multi_master_mode = true,
|
.has_multi_master_mode = true,
|
||||||
.has_slcg_override_reg = true,
|
.has_slcg_override_reg = true,
|
||||||
|
.has_mst_fifo = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
|
||||||
|
.has_continue_xfer_support = true,
|
||||||
|
.has_per_pkt_xfer_complete_irq = true,
|
||||||
|
.has_single_clk_source = true,
|
||||||
|
.clk_divisor_hs_mode = 1,
|
||||||
|
.clk_divisor_std_fast_mode = 0x19,
|
||||||
|
.clk_divisor_fast_plus_mode = 0x10,
|
||||||
|
.has_config_load_reg = true,
|
||||||
|
.has_multi_master_mode = true,
|
||||||
|
.has_slcg_override_reg = true,
|
||||||
|
.has_mst_fifo = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Match table for of_platform binding */
|
/* Match table for of_platform binding */
|
||||||
static const struct of_device_id tegra_i2c_of_match[] = {
|
static const struct of_device_id tegra_i2c_of_match[] = {
|
||||||
|
{ .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
|
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
|
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
|
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
|
||||||
|
|
|
@ -173,9 +173,6 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
|
||||||
u8 offset;
|
u8 offset;
|
||||||
u32 xfer;
|
u32 xfer;
|
||||||
|
|
||||||
if (!len)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
offset = buf[0];
|
offset = buf[0];
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
||||||
|
@ -241,9 +238,6 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
|
||||||
unsigned long timeout, stoptime, checktime;
|
unsigned long timeout, stoptime, checktime;
|
||||||
int nbytes, timedout;
|
int nbytes, timedout;
|
||||||
|
|
||||||
if (!len)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
|
||||||
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
|
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
|
||||||
|
@ -340,6 +334,10 @@ static const struct i2c_algorithm xlr_i2c_algo = {
|
||||||
.functionality = xlr_func,
|
.functionality = xlr_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks xlr_i2c_quirks = {
|
||||||
|
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct xlr_i2c_config xlr_i2c_config_default = {
|
static const struct xlr_i2c_config xlr_i2c_config_default = {
|
||||||
.status_busy = XLR_I2C_BUS_BUSY,
|
.status_busy = XLR_I2C_BUS_BUSY,
|
||||||
.cfg_extra = 0,
|
.cfg_extra = 0,
|
||||||
|
@ -427,6 +425,7 @@ static int xlr_i2c_probe(struct platform_device *pdev)
|
||||||
priv->adap.owner = THIS_MODULE;
|
priv->adap.owner = THIS_MODULE;
|
||||||
priv->adap.algo_data = priv;
|
priv->adap.algo_data = priv;
|
||||||
priv->adap.algo = &xlr_i2c_algo;
|
priv->adap.algo = &xlr_i2c_algo;
|
||||||
|
priv->adap.quirks = &xlr_i2c_quirks;
|
||||||
priv->adap.nr = pdev->id;
|
priv->adap.nr = pdev->id;
|
||||||
priv->adap.class = I2C_CLASS_HWMON;
|
priv->adap.class = I2C_CLASS_HWMON;
|
||||||
snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
|
snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
|
||||||
|
|
|
@ -453,8 +453,12 @@ static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
|
||||||
else
|
else
|
||||||
dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n",
|
dev_err(&client->adapter->dev, "i2c read %d bytes from client@%#x starting at reg %#x failed, error: %d\n",
|
||||||
data_len, client->addr, cmd, ret);
|
data_len, client->addr, cmd, ret);
|
||||||
} else {
|
/* 2 transfers must have completed successfully */
|
||||||
|
} else if (ret == 2) {
|
||||||
memcpy(data, buffer, data_len);
|
memcpy(data, buffer, data_len);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
|
@ -482,11 +486,16 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
|
||||||
msgs[0].buf = buffer;
|
msgs[0].buf = buffer;
|
||||||
|
|
||||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||||
if (ret < 0)
|
|
||||||
dev_err(&client->adapter->dev, "i2c write failed\n");
|
|
||||||
|
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
return ret;
|
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1 transfer must have completed successfully */
|
||||||
|
return (ret == 1) ? 0 : -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static acpi_status
|
static acpi_status
|
||||||
|
@ -590,8 +599,6 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command,
|
||||||
if (action == ACPI_READ) {
|
if (action == ACPI_READ) {
|
||||||
status = acpi_gsb_i2c_read_bytes(client, command,
|
status = acpi_gsb_i2c_read_bytes(client, command,
|
||||||
gsb->data, info->access_length);
|
gsb->data, info->access_length);
|
||||||
if (status > 0)
|
|
||||||
status = 0;
|
|
||||||
} else {
|
} else {
|
||||||
status = acpi_gsb_i2c_write_bytes(client, command,
|
status = acpi_gsb_i2c_write_bytes(client, command,
|
||||||
gsb->data, info->access_length);
|
gsb->data, info->access_length);
|
||||||
|
|
|
@ -158,6 +158,22 @@ static void set_sda_gpio_value(struct i2c_adapter *adap, int val)
|
||||||
gpiod_set_value_cansleep(adap->bus_recovery_info->sda_gpiod, val);
|
gpiod_set_value_cansleep(adap->bus_recovery_info->sda_gpiod, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int i2c_generic_bus_free(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
|
||||||
|
int ret = -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (bri->get_bus_free)
|
||||||
|
ret = bri->get_bus_free(adap);
|
||||||
|
else if (bri->get_sda)
|
||||||
|
ret = bri->get_sda(adap);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ret ? 0 : -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We are generating clock pulses. ndelay() determines durating of clk pulses.
|
* We are generating clock pulses. ndelay() determines durating of clk pulses.
|
||||||
* We will generate clock with rate 100 KHz and so duration of both clock levels
|
* We will generate clock with rate 100 KHz and so duration of both clock levels
|
||||||
|
@ -169,21 +185,28 @@ static void set_sda_gpio_value(struct i2c_adapter *adap, int val)
|
||||||
int i2c_generic_scl_recovery(struct i2c_adapter *adap)
|
int i2c_generic_scl_recovery(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
|
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
|
||||||
int i = 0, val = 1, ret = 0;
|
int i = 0, scl = 1, ret;
|
||||||
|
|
||||||
if (bri->prepare_recovery)
|
if (bri->prepare_recovery)
|
||||||
bri->prepare_recovery(adap);
|
bri->prepare_recovery(adap);
|
||||||
|
|
||||||
bri->set_scl(adap, val);
|
/*
|
||||||
|
* If we can set SDA, we will always create a STOP to ensure additional
|
||||||
|
* pulses will do no harm. This is achieved by letting SDA follow SCL
|
||||||
|
* half a cycle later. Check the 'incomplete_write_byte' fault injector
|
||||||
|
* for details.
|
||||||
|
*/
|
||||||
|
bri->set_scl(adap, scl);
|
||||||
|
ndelay(RECOVERY_NDELAY / 2);
|
||||||
if (bri->set_sda)
|
if (bri->set_sda)
|
||||||
bri->set_sda(adap, 1);
|
bri->set_sda(adap, scl);
|
||||||
ndelay(RECOVERY_NDELAY);
|
ndelay(RECOVERY_NDELAY / 2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* By this time SCL is high, as we need to give 9 falling-rising edges
|
* By this time SCL is high, as we need to give 9 falling-rising edges
|
||||||
*/
|
*/
|
||||||
while (i++ < RECOVERY_CLK_CNT * 2) {
|
while (i++ < RECOVERY_CLK_CNT * 2) {
|
||||||
if (val) {
|
if (scl) {
|
||||||
/* SCL shouldn't be low here */
|
/* SCL shouldn't be low here */
|
||||||
if (!bri->get_scl(adap)) {
|
if (!bri->get_scl(adap)) {
|
||||||
dev_err(&adap->dev,
|
dev_err(&adap->dev,
|
||||||
|
@ -191,40 +214,26 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap)
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Break if SDA is high */
|
|
||||||
if (bri->get_sda && bri->get_sda(adap))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val = !val;
|
scl = !scl;
|
||||||
bri->set_scl(adap, val);
|
bri->set_scl(adap, scl);
|
||||||
|
/* Creating STOP again, see above */
|
||||||
/*
|
|
||||||
* If we can set SDA, we will always create STOP here to ensure
|
|
||||||
* the additional pulses will do no harm. This is achieved by
|
|
||||||
* letting SDA follow SCL half a cycle later.
|
|
||||||
*/
|
|
||||||
ndelay(RECOVERY_NDELAY / 2);
|
ndelay(RECOVERY_NDELAY / 2);
|
||||||
if (bri->set_sda)
|
if (bri->set_sda)
|
||||||
bri->set_sda(adap, val);
|
bri->set_sda(adap, scl);
|
||||||
ndelay(RECOVERY_NDELAY / 2);
|
ndelay(RECOVERY_NDELAY / 2);
|
||||||
|
|
||||||
|
if (scl) {
|
||||||
|
ret = i2c_generic_bus_free(adap);
|
||||||
|
if (ret == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if recovery actually succeeded */
|
/* If we can't check bus status, assume recovery worked */
|
||||||
if (bri->get_sda && !bri->get_sda(adap))
|
if (ret == -EOPNOTSUPP)
|
||||||
ret = -EBUSY;
|
ret = 0;
|
||||||
|
|
||||||
/* If all went well, send STOP for a sane bus state. */
|
|
||||||
if (ret == 0 && bri->set_sda) {
|
|
||||||
bri->set_scl(adap, 0);
|
|
||||||
ndelay(RECOVERY_NDELAY / 2);
|
|
||||||
bri->set_sda(adap, 0);
|
|
||||||
ndelay(RECOVERY_NDELAY / 2);
|
|
||||||
bri->set_scl(adap, 1);
|
|
||||||
ndelay(RECOVERY_NDELAY / 2);
|
|
||||||
bri->set_sda(adap, 1);
|
|
||||||
ndelay(RECOVERY_NDELAY / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bri->unprepare_recovery)
|
if (bri->unprepare_recovery)
|
||||||
bri->unprepare_recovery(adap);
|
bri->unprepare_recovery(adap);
|
||||||
|
@ -274,6 +283,10 @@ static void i2c_init_recovery(struct i2c_adapter *adap)
|
||||||
err_str = "no {get|set}_scl() found";
|
err_str = "no {get|set}_scl() found";
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
if (!bri->set_sda && !bri->get_sda) {
|
||||||
|
err_str = "either get_sda() or set_sda() needed";
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -1563,6 +1576,8 @@ void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_de
|
||||||
ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
|
ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
|
||||||
if (ret && use_defaults)
|
if (ret && use_defaults)
|
||||||
t->sda_fall_ns = t->scl_fall_ns;
|
t->sda_fall_ns = t->scl_fall_ns;
|
||||||
|
|
||||||
|
device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
|
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
|
||||||
|
|
||||||
|
@ -1826,9 +1841,15 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
if (msgs[i].flags & I2C_M_RD) {
|
if (msgs[i].flags & I2C_M_RD) {
|
||||||
if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len))
|
if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len))
|
||||||
return i2c_quirk_error(adap, &msgs[i], "msg too long");
|
return i2c_quirk_error(adap, &msgs[i], "msg too long");
|
||||||
|
|
||||||
|
if (q->flags & I2C_AQ_NO_ZERO_LEN_READ && len == 0)
|
||||||
|
return i2c_quirk_error(adap, &msgs[i], "no zero length");
|
||||||
} else {
|
} else {
|
||||||
if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len))
|
if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len))
|
||||||
return i2c_quirk_error(adap, &msgs[i], "msg too long");
|
return i2c_quirk_error(adap, &msgs[i], "msg too long");
|
||||||
|
|
||||||
|
if (q->flags & I2C_AQ_NO_ZERO_LEN_WRITE && len == 0)
|
||||||
|
return i2c_quirk_error(adap, &msgs[i], "no zero length");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,9 +47,9 @@ int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
|
||||||
|
|
||||||
client->slave_cb = slave_cb;
|
client->slave_cb = slave_cb;
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
ret = client->adapter->algo->reg_slave(client);
|
ret = client->adapter->algo->reg_slave(client);
|
||||||
i2c_unlock_adapter(client->adapter);
|
i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
client->slave_cb = NULL;
|
client->slave_cb = NULL;
|
||||||
|
@ -69,9 +69,9 @@ int i2c_slave_unregister(struct i2c_client *client)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
i2c_lock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
ret = client->adapter->algo->unreg_slave(client);
|
ret = client->adapter->algo->unreg_slave(client);
|
||||||
i2c_unlock_adapter(client->adapter);
|
i2c_unlock_bus(client->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
client->slave_cb = NULL;
|
client->slave_cb = NULL;
|
||||||
|
|
|
@ -87,8 +87,8 @@ static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
|
||||||
|
|
||||||
ret = muxc->select(muxc, priv->chan_id);
|
ret = muxc->select(muxc, priv->chan_id);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
ret = parent->algo->smbus_xfer(parent, addr, flags,
|
ret = __i2c_smbus_xfer(parent, addr, flags,
|
||||||
read_write, command, size, data);
|
read_write, command, size, data);
|
||||||
if (muxc->deselect)
|
if (muxc->deselect)
|
||||||
muxc->deselect(muxc, priv->chan_id);
|
muxc->deselect(muxc, priv->chan_id);
|
||||||
|
|
||||||
|
|
|
@ -94,31 +94,11 @@ static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
|
||||||
struct i2c_client *client, u8 val)
|
struct i2c_client *client, u8 val)
|
||||||
{
|
{
|
||||||
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
|
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
|
||||||
int ret = -ENODEV;
|
union i2c_smbus_data data = { .byte = val };
|
||||||
|
|
||||||
if (adap->algo->master_xfer) {
|
return __i2c_smbus_xfer(adap, client->addr, client->flags,
|
||||||
struct i2c_msg msg;
|
I2C_SMBUS_WRITE, pdata->sel_reg_addr,
|
||||||
u8 msgbuf[] = {pdata->sel_reg_addr, val};
|
I2C_SMBUS_BYTE_DATA, &data);
|
||||||
|
|
||||||
msg.addr = client->addr;
|
|
||||||
msg.flags = 0;
|
|
||||||
msg.len = 2;
|
|
||||||
msg.buf = msgbuf;
|
|
||||||
ret = __i2c_transfer(adap, &msg, 1);
|
|
||||||
|
|
||||||
if (ret >= 0 && ret != 1)
|
|
||||||
ret = -EREMOTEIO;
|
|
||||||
} else if (adap->algo->smbus_xfer) {
|
|
||||||
union i2c_smbus_data data;
|
|
||||||
|
|
||||||
data.byte = val;
|
|
||||||
ret = adap->algo->smbus_xfer(adap, client->addr,
|
|
||||||
client->flags, I2C_SMBUS_WRITE,
|
|
||||||
pdata->sel_reg_addr,
|
|
||||||
I2C_SMBUS_BYTE_DATA, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||||
|
|
|
@ -99,31 +99,11 @@ MODULE_DEVICE_TABLE(of, pca9541_of_match);
|
||||||
static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
|
static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap = client->adapter;
|
struct i2c_adapter *adap = client->adapter;
|
||||||
int ret;
|
union i2c_smbus_data data = { .byte = val };
|
||||||
|
|
||||||
if (adap->algo->master_xfer) {
|
return __i2c_smbus_xfer(adap, client->addr, client->flags,
|
||||||
struct i2c_msg msg;
|
I2C_SMBUS_WRITE, command,
|
||||||
char buf[2];
|
I2C_SMBUS_BYTE_DATA, &data);
|
||||||
|
|
||||||
msg.addr = client->addr;
|
|
||||||
msg.flags = 0;
|
|
||||||
msg.len = 2;
|
|
||||||
buf[0] = command;
|
|
||||||
buf[1] = val;
|
|
||||||
msg.buf = buf;
|
|
||||||
ret = __i2c_transfer(adap, &msg, 1);
|
|
||||||
} else {
|
|
||||||
union i2c_smbus_data data;
|
|
||||||
|
|
||||||
data.byte = val;
|
|
||||||
ret = adap->algo->smbus_xfer(adap, client->addr,
|
|
||||||
client->flags,
|
|
||||||
I2C_SMBUS_WRITE,
|
|
||||||
command,
|
|
||||||
I2C_SMBUS_BYTE_DATA, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -133,41 +113,14 @@ static int pca9541_reg_write(struct i2c_client *client, u8 command, u8 val)
|
||||||
static int pca9541_reg_read(struct i2c_client *client, u8 command)
|
static int pca9541_reg_read(struct i2c_client *client, u8 command)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap = client->adapter;
|
struct i2c_adapter *adap = client->adapter;
|
||||||
|
union i2c_smbus_data data;
|
||||||
int ret;
|
int ret;
|
||||||
u8 val;
|
|
||||||
|
|
||||||
if (adap->algo->master_xfer) {
|
ret = __i2c_smbus_xfer(adap, client->addr, client->flags,
|
||||||
struct i2c_msg msg[2] = {
|
I2C_SMBUS_READ, command,
|
||||||
{
|
I2C_SMBUS_BYTE_DATA, &data);
|
||||||
.addr = client->addr,
|
|
||||||
.flags = 0,
|
|
||||||
.len = 1,
|
|
||||||
.buf = &command
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = I2C_M_RD,
|
|
||||||
.len = 1,
|
|
||||||
.buf = &val
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ret = __i2c_transfer(adap, msg, 2);
|
|
||||||
if (ret == 2)
|
|
||||||
ret = val;
|
|
||||||
else if (ret >= 0)
|
|
||||||
ret = -EIO;
|
|
||||||
} else {
|
|
||||||
union i2c_smbus_data data;
|
|
||||||
|
|
||||||
ret = adap->algo->smbus_xfer(adap, client->addr,
|
return ret ?: data.byte;
|
||||||
client->flags,
|
|
||||||
I2C_SMBUS_READ,
|
|
||||||
command,
|
|
||||||
I2C_SMBUS_BYTE_DATA, &data);
|
|
||||||
if (!ret)
|
|
||||||
ret = data.byte;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -345,11 +298,11 @@ static int pca9541_probe(struct i2c_client *client,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I2C accesses are unprotected here.
|
* I2C accesses are unprotected here.
|
||||||
* We have to lock the adapter before releasing the bus.
|
* We have to lock the I2C segment before releasing the bus.
|
||||||
*/
|
*/
|
||||||
i2c_lock_adapter(adap);
|
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
pca9541_release_bus(client);
|
pca9541_release_bus(client);
|
||||||
i2c_unlock_adapter(adap);
|
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
/* Create mux adapter */
|
/* Create mux adapter */
|
||||||
|
|
||||||
|
|
|
@ -220,30 +220,11 @@ MODULE_DEVICE_TABLE(of, pca954x_of_match);
|
||||||
static int pca954x_reg_write(struct i2c_adapter *adap,
|
static int pca954x_reg_write(struct i2c_adapter *adap,
|
||||||
struct i2c_client *client, u8 val)
|
struct i2c_client *client, u8 val)
|
||||||
{
|
{
|
||||||
int ret = -ENODEV;
|
union i2c_smbus_data dummy;
|
||||||
|
|
||||||
if (adap->algo->master_xfer) {
|
return __i2c_smbus_xfer(adap, client->addr, client->flags,
|
||||||
struct i2c_msg msg;
|
I2C_SMBUS_WRITE, val,
|
||||||
char buf[1];
|
I2C_SMBUS_BYTE, &dummy);
|
||||||
|
|
||||||
msg.addr = client->addr;
|
|
||||||
msg.flags = 0;
|
|
||||||
msg.len = 1;
|
|
||||||
buf[0] = val;
|
|
||||||
msg.buf = buf;
|
|
||||||
ret = __i2c_transfer(adap, &msg, 1);
|
|
||||||
|
|
||||||
if (ret >= 0 && ret != 1)
|
|
||||||
ret = -EREMOTEIO;
|
|
||||||
} else {
|
|
||||||
union i2c_smbus_data data;
|
|
||||||
ret = adap->algo->smbus_xfer(adap, client->addr,
|
|
||||||
client->flags,
|
|
||||||
I2C_SMBUS_WRITE,
|
|
||||||
val, I2C_SMBUS_BYTE, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||||
|
@ -368,7 +349,8 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
||||||
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||||
struct device_node *of_node = client->dev.of_node;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
bool idle_disconnect_dt;
|
bool idle_disconnect_dt;
|
||||||
struct gpio_desc *gpio;
|
struct gpio_desc *gpio;
|
||||||
int num, force, class;
|
int num, force, class;
|
||||||
|
@ -379,8 +361,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
muxc = i2c_mux_alloc(adap, &client->dev,
|
muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0,
|
||||||
PCA954X_MAX_NCHANS, sizeof(*data), 0,
|
|
||||||
pca954x_select_chan, pca954x_deselect_mux);
|
pca954x_select_chan, pca954x_deselect_mux);
|
||||||
if (!muxc)
|
if (!muxc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -390,7 +371,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
data->client = client;
|
data->client = client;
|
||||||
|
|
||||||
/* Reset the mux if a reset GPIO is specified. */
|
/* Reset the mux if a reset GPIO is specified. */
|
||||||
gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
|
gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||||
if (IS_ERR(gpio))
|
if (IS_ERR(gpio))
|
||||||
return PTR_ERR(gpio);
|
return PTR_ERR(gpio);
|
||||||
if (gpio) {
|
if (gpio) {
|
||||||
|
@ -400,7 +381,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
udelay(1);
|
udelay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
data->chip = of_device_get_match_data(&client->dev);
|
data->chip = of_device_get_match_data(dev);
|
||||||
if (!data->chip)
|
if (!data->chip)
|
||||||
data->chip = &chips[id->driver_data];
|
data->chip = &chips[id->driver_data];
|
||||||
|
|
||||||
|
@ -414,8 +395,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
if (!ret &&
|
if (!ret &&
|
||||||
(id.manufacturer_id != data->chip->id.manufacturer_id ||
|
(id.manufacturer_id != data->chip->id.manufacturer_id ||
|
||||||
id.part_id != data->chip->id.part_id)) {
|
id.part_id != data->chip->id.part_id)) {
|
||||||
dev_warn(&client->dev,
|
dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
|
||||||
"unexpected device id %03x-%03x-%x\n",
|
|
||||||
id.manufacturer_id, id.part_id,
|
id.manufacturer_id, id.part_id,
|
||||||
id.die_revision);
|
id.die_revision);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -427,14 +407,14 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
* initializes the mux to disconnected state.
|
* initializes the mux to disconnected state.
|
||||||
*/
|
*/
|
||||||
if (i2c_smbus_write_byte(client, 0) < 0) {
|
if (i2c_smbus_write_byte(client, 0) < 0) {
|
||||||
dev_warn(&client->dev, "probe failed\n");
|
dev_warn(dev, "probe failed\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->last_chan = 0; /* force the first selection */
|
data->last_chan = 0; /* force the first selection */
|
||||||
|
|
||||||
idle_disconnect_dt = of_node &&
|
idle_disconnect_dt = np &&
|
||||||
of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
|
of_property_read_bool(np, "i2c-mux-idle-disconnect");
|
||||||
|
|
||||||
ret = pca954x_irq_setup(muxc);
|
ret = pca954x_irq_setup(muxc);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -465,7 +445,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->irq) {
|
if (data->irq) {
|
||||||
ret = devm_request_threaded_irq(&client->dev, data->client->irq,
|
ret = devm_request_threaded_irq(dev, data->client->irq,
|
||||||
NULL, pca954x_irq_handler,
|
NULL, pca954x_irq_handler,
|
||||||
IRQF_ONESHOT | IRQF_SHARED,
|
IRQF_ONESHOT | IRQF_SHARED,
|
||||||
"pca954x", data);
|
"pca954x", data);
|
||||||
|
@ -473,8 +453,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
goto fail_cleanup;
|
goto fail_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&client->dev,
|
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
|
||||||
"registered %d multiplexed busses for I2C %s %s\n",
|
|
||||||
num, data->chip->muxtype == pca954x_ismux
|
num, data->chip->muxtype == pca954x_ismux
|
||||||
? "mux" : "switch", client->name);
|
? "mux" : "switch", client->name);
|
||||||
|
|
||||||
|
|
|
@ -433,11 +433,11 @@ static int mlx90614_wakeup(struct mlx90614_data *data)
|
||||||
|
|
||||||
dev_dbg(&data->client->dev, "Requesting wake-up");
|
dev_dbg(&data->client->dev, "Requesting wake-up");
|
||||||
|
|
||||||
i2c_lock_adapter(data->client->adapter);
|
i2c_lock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
gpiod_direction_output(data->wakeup_gpio, 0);
|
gpiod_direction_output(data->wakeup_gpio, 0);
|
||||||
msleep(MLX90614_TIMING_WAKEUP);
|
msleep(MLX90614_TIMING_WAKEUP);
|
||||||
gpiod_direction_input(data->wakeup_gpio);
|
gpiod_direction_input(data->wakeup_gpio);
|
||||||
i2c_unlock_adapter(data->client->adapter);
|
i2c_unlock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
data->ready_timestamp = jiffies +
|
data->ready_timestamp = jiffies +
|
||||||
msecs_to_jiffies(MLX90614_TIMING_STARTUP);
|
msecs_to_jiffies(MLX90614_TIMING_STARTUP);
|
||||||
|
|
|
@ -304,7 +304,7 @@ static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf,
|
||||||
msg[1].len = len;
|
msg[1].len = len;
|
||||||
msg[1].buf = buf;
|
msg[1].buf = buf;
|
||||||
|
|
||||||
i2c_lock_adapter(adap);
|
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
if (__i2c_transfer(adap, &msg[i], 1) < 0) {
|
if (__i2c_transfer(adap, &msg[i], 1) < 0) {
|
||||||
|
@ -313,7 +313,7 @@ static int rohm_i2c_burst_read(struct i2c_client *client, u8 start, void *buf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_unlock_adapter(adap);
|
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1311,10 +1311,10 @@ static int af9013_wregs(struct i2c_client *client, u8 cmd, u16 reg,
|
||||||
memcpy(&buf[3], val, len);
|
memcpy(&buf[3], val, len);
|
||||||
|
|
||||||
if (lock)
|
if (lock)
|
||||||
i2c_lock_adapter(client->adapter);
|
i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
ret = __i2c_transfer(client->adapter, msg, 1);
|
ret = __i2c_transfer(client->adapter, msg, 1);
|
||||||
if (lock)
|
if (lock)
|
||||||
i2c_unlock_adapter(client->adapter);
|
i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
} else if (ret != 1) {
|
} else if (ret != 1) {
|
||||||
|
@ -1352,10 +1352,10 @@ static int af9013_rregs(struct i2c_client *client, u8 cmd, u16 reg,
|
||||||
buf[2] = cmd;
|
buf[2] = cmd;
|
||||||
|
|
||||||
if (lock)
|
if (lock)
|
||||||
i2c_lock_adapter(client->adapter);
|
i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
ret = __i2c_transfer(client->adapter, msg, 2);
|
ret = __i2c_transfer(client->adapter, msg, 2);
|
||||||
if (lock)
|
if (lock)
|
||||||
i2c_unlock_adapter(client->adapter);
|
i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
} else if (ret != 2) {
|
} else if (ret != 2) {
|
||||||
|
|
|
@ -213,7 +213,7 @@ static inline u32 log10times100(u32 value)
|
||||||
|
|
||||||
static int drxk_i2c_lock(struct drxk_state *state)
|
static int drxk_i2c_lock(struct drxk_state *state)
|
||||||
{
|
{
|
||||||
i2c_lock_adapter(state->i2c);
|
i2c_lock_bus(state->i2c, I2C_LOCK_SEGMENT);
|
||||||
state->drxk_i2c_exclusive_lock = true;
|
state->drxk_i2c_exclusive_lock = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -224,7 +224,7 @@ static void drxk_i2c_unlock(struct drxk_state *state)
|
||||||
if (!state->drxk_i2c_exclusive_lock)
|
if (!state->drxk_i2c_exclusive_lock)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
i2c_unlock_adapter(state->i2c);
|
i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT);
|
||||||
state->drxk_i2c_exclusive_lock = false;
|
state->drxk_i2c_exclusive_lock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,9 @@ static int rtl2830_bulk_write(struct i2c_client *client, unsigned int reg,
|
||||||
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
|
ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
|
||||||
i2c_unlock_adapter(client->adapter);
|
i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,9 +36,9 @@ static int rtl2830_update_bits(struct i2c_client *client, unsigned int reg,
|
||||||
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
ret = regmap_update_bits(dev->regmap, reg, mask, val);
|
ret = regmap_update_bits(dev->regmap, reg, mask, val);
|
||||||
i2c_unlock_adapter(client->adapter);
|
i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ static int rtl2830_bulk_read(struct i2c_client *client, unsigned int reg,
|
||||||
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
|
ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
|
||||||
i2c_unlock_adapter(client->adapter);
|
i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -329,7 +329,7 @@ static int tda1004x_do_upload(struct tda1004x_state *state,
|
||||||
tda1004x_write_byteI(state, dspCodeCounterReg, 0);
|
tda1004x_write_byteI(state, dspCodeCounterReg, 0);
|
||||||
fw_msg.addr = state->config->demod_address;
|
fw_msg.addr = state->config->demod_address;
|
||||||
|
|
||||||
i2c_lock_adapter(state->i2c);
|
i2c_lock_bus(state->i2c, I2C_LOCK_SEGMENT);
|
||||||
buf[0] = dspCodeInReg;
|
buf[0] = dspCodeInReg;
|
||||||
while (pos != len) {
|
while (pos != len) {
|
||||||
// work out how much to send this time
|
// work out how much to send this time
|
||||||
|
@ -342,14 +342,14 @@ static int tda1004x_do_upload(struct tda1004x_state *state,
|
||||||
fw_msg.len = tx_size + 1;
|
fw_msg.len = tx_size + 1;
|
||||||
if (__i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
|
if (__i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
|
||||||
printk(KERN_ERR "tda1004x: Error during firmware upload\n");
|
printk(KERN_ERR "tda1004x: Error during firmware upload\n");
|
||||||
i2c_unlock_adapter(state->i2c);
|
i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
pos += tx_size;
|
pos += tx_size;
|
||||||
|
|
||||||
dprintk("%s: fw_pos=0x%x\n", __func__, pos);
|
dprintk("%s: fw_pos=0x%x\n", __func__, pos);
|
||||||
}
|
}
|
||||||
i2c_unlock_adapter(state->i2c);
|
i2c_unlock_bus(state->i2c, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
/* give the DSP a chance to settle 03/10/05 Hac */
|
/* give the DSP a chance to settle 03/10/05 Hac */
|
||||||
msleep(100);
|
msleep(100);
|
||||||
|
|
|
@ -225,7 +225,7 @@ static int __tda18271_write_regs(struct dvb_frontend *fe, int idx, int len,
|
||||||
*/
|
*/
|
||||||
if (lock_i2c) {
|
if (lock_i2c) {
|
||||||
tda18271_i2c_gate_ctrl(fe, 1);
|
tda18271_i2c_gate_ctrl(fe, 1);
|
||||||
i2c_lock_adapter(priv->i2c_props.adap);
|
i2c_lock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
|
||||||
}
|
}
|
||||||
while (len) {
|
while (len) {
|
||||||
if (max > len)
|
if (max > len)
|
||||||
|
@ -246,7 +246,7 @@ static int __tda18271_write_regs(struct dvb_frontend *fe, int idx, int len,
|
||||||
len -= max;
|
len -= max;
|
||||||
}
|
}
|
||||||
if (lock_i2c) {
|
if (lock_i2c) {
|
||||||
i2c_unlock_adapter(priv->i2c_props.adap);
|
i2c_unlock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
|
||||||
tda18271_i2c_gate_ctrl(fe, 0);
|
tda18271_i2c_gate_ctrl(fe, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ int tda18271_init_regs(struct dvb_frontend *fe)
|
||||||
* as those could cause bad things
|
* as those could cause bad things
|
||||||
*/
|
*/
|
||||||
tda18271_i2c_gate_ctrl(fe, 1);
|
tda18271_i2c_gate_ctrl(fe, 1);
|
||||||
i2c_lock_adapter(priv->i2c_props.adap);
|
i2c_lock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
/* initialize registers */
|
/* initialize registers */
|
||||||
switch (priv->id) {
|
switch (priv->id) {
|
||||||
|
@ -516,7 +516,7 @@ int tda18271_init_regs(struct dvb_frontend *fe)
|
||||||
/* synchronize */
|
/* synchronize */
|
||||||
__tda18271_write_regs(fe, R_EP1, 1, false);
|
__tda18271_write_regs(fe, R_EP1, 1, false);
|
||||||
|
|
||||||
i2c_unlock_adapter(priv->i2c_props.adap);
|
i2c_unlock_bus(priv->i2c_props.adap, I2C_LOCK_SEGMENT);
|
||||||
tda18271_i2c_gate_ctrl(fe, 0);
|
tda18271_i2c_gate_ctrl(fe, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -146,14 +146,14 @@ int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
|
||||||
unsigned char zero;
|
unsigned char zero;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
i2c_lock_adapter(i2c->adapter);
|
i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
|
||||||
read_device(i2c, 0xFA, 0, &zero);
|
read_device(i2c, 0xFA, 0, &zero);
|
||||||
read_device(i2c, 0xFB, 0, &zero);
|
read_device(i2c, 0xFB, 0, &zero);
|
||||||
read_device(i2c, 0xFF, 0, &zero);
|
read_device(i2c, 0xFF, 0, &zero);
|
||||||
ret = write_device(i2c, reg, 1, &data);
|
ret = write_device(i2c, reg, 1, &data);
|
||||||
read_device(i2c, 0xFE, 0, &zero);
|
read_device(i2c, 0xFE, 0, &zero);
|
||||||
read_device(i2c, 0xFC, 0, &zero);
|
read_device(i2c, 0xFC, 0, &zero);
|
||||||
i2c_unlock_adapter(i2c->adapter);
|
i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pm860x_page_reg_write);
|
EXPORT_SYMBOL(pm860x_page_reg_write);
|
||||||
|
@ -164,14 +164,14 @@ int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
|
||||||
unsigned char zero = 0;
|
unsigned char zero = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
i2c_lock_adapter(i2c->adapter);
|
i2c_lock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
|
||||||
read_device(i2c, 0xfa, 0, &zero);
|
read_device(i2c, 0xfa, 0, &zero);
|
||||||
read_device(i2c, 0xfb, 0, &zero);
|
read_device(i2c, 0xfb, 0, &zero);
|
||||||
read_device(i2c, 0xff, 0, &zero);
|
read_device(i2c, 0xff, 0, &zero);
|
||||||
ret = read_device(i2c, reg, count, buf);
|
ret = read_device(i2c, reg, count, buf);
|
||||||
read_device(i2c, 0xFE, 0, &zero);
|
read_device(i2c, 0xFE, 0, &zero);
|
||||||
read_device(i2c, 0xFC, 0, &zero);
|
read_device(i2c, 0xFC, 0, &zero);
|
||||||
i2c_unlock_adapter(i2c->adapter);
|
i2c_unlock_bus(i2c->adapter, I2C_LOCK_SEGMENT);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pm860x_page_bulk_read);
|
EXPORT_SYMBOL(pm860x_page_bulk_read);
|
||||||
|
|
|
@ -478,6 +478,23 @@ static void at24_properties_to_pdata(struct device *dev,
|
||||||
if (device_property_present(dev, "no-read-rollover"))
|
if (device_property_present(dev, "no-read-rollover"))
|
||||||
chip->flags |= AT24_FLAG_NO_RDROL;
|
chip->flags |= AT24_FLAG_NO_RDROL;
|
||||||
|
|
||||||
|
err = device_property_read_u32(dev, "address-width", &val);
|
||||||
|
if (!err) {
|
||||||
|
switch (val) {
|
||||||
|
case 8:
|
||||||
|
if (chip->flags & AT24_FLAG_ADDR16)
|
||||||
|
dev_warn(dev, "Override address width to be 8, while default is 16\n");
|
||||||
|
chip->flags &= ~AT24_FLAG_ADDR16;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
chip->flags |= AT24_FLAG_ADDR16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_warn(dev, "Bad \"address-width\" property: %u\n",
|
||||||
|
val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = device_property_read_u32(dev, "size", &val);
|
err = device_property_read_u32(dev, "size", &val);
|
||||||
if (!err)
|
if (!err)
|
||||||
chip->byte_len = val;
|
chip->byte_len = val;
|
||||||
|
|
|
@ -564,6 +564,7 @@ struct i2c_lock_operations {
|
||||||
* @scl_fall_ns: time SCL signal takes to fall in ns; t(f) in the I2C specification
|
* @scl_fall_ns: time SCL signal takes to fall in ns; t(f) in the I2C specification
|
||||||
* @scl_int_delay_ns: time IP core additionally needs to setup SCL in ns
|
* @scl_int_delay_ns: time IP core additionally needs to setup SCL in ns
|
||||||
* @sda_fall_ns: time SDA signal takes to fall in ns; t(f) in the I2C specification
|
* @sda_fall_ns: time SDA signal takes to fall in ns; t(f) in the I2C specification
|
||||||
|
* @sda_hold_ns: time IP core additionally needs to hold SDA in ns
|
||||||
*/
|
*/
|
||||||
struct i2c_timings {
|
struct i2c_timings {
|
||||||
u32 bus_freq_hz;
|
u32 bus_freq_hz;
|
||||||
|
@ -571,6 +572,7 @@ struct i2c_timings {
|
||||||
u32 scl_fall_ns;
|
u32 scl_fall_ns;
|
||||||
u32 scl_int_delay_ns;
|
u32 scl_int_delay_ns;
|
||||||
u32 sda_fall_ns;
|
u32 sda_fall_ns;
|
||||||
|
u32 sda_hold_ns;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -581,12 +583,14 @@ struct i2c_timings {
|
||||||
* recovery. Populated internally for generic GPIO recovery.
|
* recovery. Populated internally for generic GPIO recovery.
|
||||||
* @set_scl: This sets/clears the SCL line. Mandatory for generic SCL recovery.
|
* @set_scl: This sets/clears the SCL line. Mandatory for generic SCL recovery.
|
||||||
* Populated internally for generic GPIO recovery.
|
* Populated internally for generic GPIO recovery.
|
||||||
* @get_sda: This gets current value of SDA line. Optional for generic SCL
|
* @get_sda: This gets current value of SDA line. This or set_sda() is mandatory
|
||||||
* recovery. Populated internally, if sda_gpio is a valid GPIO, for generic
|
* for generic SCL recovery. Populated internally, if sda_gpio is a valid
|
||||||
* GPIO recovery.
|
* GPIO, for generic GPIO recovery.
|
||||||
* @set_sda: This sets/clears the SDA line. Optional for generic SCL recovery.
|
* @set_sda: This sets/clears the SDA line. This or get_sda() is mandatory for
|
||||||
* Populated internally, if sda_gpio is a valid GPIO, for generic GPIO
|
* generic SCL recovery. Populated internally, if sda_gpio is a valid GPIO,
|
||||||
* recovery.
|
* for generic GPIO recovery.
|
||||||
|
* @get_bus_free: Returns the bus free state as seen from the IP core in case it
|
||||||
|
* has a more complex internal logic than just reading SDA. Optional.
|
||||||
* @prepare_recovery: This will be called before starting recovery. Platform may
|
* @prepare_recovery: This will be called before starting recovery. Platform may
|
||||||
* configure padmux here for SDA/SCL line or something else they want.
|
* configure padmux here for SDA/SCL line or something else they want.
|
||||||
* @unprepare_recovery: This will be called after completing recovery. Platform
|
* @unprepare_recovery: This will be called after completing recovery. Platform
|
||||||
|
@ -601,6 +605,7 @@ struct i2c_bus_recovery_info {
|
||||||
void (*set_scl)(struct i2c_adapter *adap, int val);
|
void (*set_scl)(struct i2c_adapter *adap, int val);
|
||||||
int (*get_sda)(struct i2c_adapter *adap);
|
int (*get_sda)(struct i2c_adapter *adap);
|
||||||
void (*set_sda)(struct i2c_adapter *adap, int val);
|
void (*set_sda)(struct i2c_adapter *adap, int val);
|
||||||
|
int (*get_bus_free)(struct i2c_adapter *adap);
|
||||||
|
|
||||||
void (*prepare_recovery)(struct i2c_adapter *adap);
|
void (*prepare_recovery)(struct i2c_adapter *adap);
|
||||||
void (*unprepare_recovery)(struct i2c_adapter *adap);
|
void (*unprepare_recovery)(struct i2c_adapter *adap);
|
||||||
|
@ -658,6 +663,10 @@ struct i2c_adapter_quirks {
|
||||||
I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
|
I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
|
||||||
/* clock stretching is not supported */
|
/* clock stretching is not supported */
|
||||||
#define I2C_AQ_NO_CLK_STRETCH BIT(4)
|
#define I2C_AQ_NO_CLK_STRETCH BIT(4)
|
||||||
|
/* message cannot have length of 0 */
|
||||||
|
#define I2C_AQ_NO_ZERO_LEN_READ BIT(5)
|
||||||
|
#define I2C_AQ_NO_ZERO_LEN_WRITE BIT(6)
|
||||||
|
#define I2C_AQ_NO_ZERO_LEN (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i2c_adapter is the structure used to identify a physical i2c bus along
|
* i2c_adapter is the structure used to identify a physical i2c bus along
|
||||||
|
@ -759,18 +768,6 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
|
||||||
adapter->lock_ops->unlock_bus(adapter, flags);
|
adapter->lock_ops->unlock_bus(adapter, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
i2c_lock_adapter(struct i2c_adapter *adapter)
|
|
||||||
{
|
|
||||||
i2c_lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
i2c_unlock_adapter(struct i2c_adapter *adapter)
|
|
||||||
{
|
|
||||||
i2c_unlock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*flags for the client struct: */
|
/*flags for the client struct: */
|
||||||
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
||||||
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
|
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
|
||||||
|
|
Loading…
Reference in New Issue