Merge branch 'i2c/for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - API for late atomic transfers (e.g. to shut down via PMIC). We have a seperate callback now which is called under clearly defined conditions. In-kernel users are converted, too. - new driver for the AMD PCIe MP2 I2C controller - large refactoring for at91 and bcm-iproc (both gain slave support due to this) - and a good share of various driver improvements anf fixes * 'i2c/for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (57 commits) dt-bindings: i2c: riic: document r7s9210 support i2c: imx-lpi2c: Use __maybe_unused instead of #if CONFIG_PM_SLEEP i2c-piix4: Add Hygon Dhyana SMBus support i2c: core: apply 'is_suspended' check for SMBus, too i2c: core: ratelimit 'transfer when suspended' errors i2c: iproc: Change driver to use 'BIT' macro i2c: riic: Add Runtime PM support i2c: mux: demux-pinctrl: use struct_size() in devm_kzalloc() i2c: mux: pca954x: allow management of device idle state via sysfs i2c: mux: pca9541: remove support for unused platform data i2c: mux: pca954x: remove support for unused platform data dt-bindings: i2c: i2c-mtk: add support for MT8516 i2c: axxia: use auto cmd for last message i2c: gpio: flag atomic capability if possible i2c: algo: bit: add flag to whitelist atomic transfers i2c: stu300: use xfer_atomic callback to bail out early i2c: ocores: enable atomic xfers i2c: ocores: refactor setup for polling i2c: tegra-bpmp: convert to use new atomic callbacks i2c: omap: Add the master_xfer_atomic hook ...
This commit is contained in:
commit
45182e4e1f
|
@ -0,0 +1,20 @@
|
||||||
|
What: /sys/bus/i2c/.../idle_state
|
||||||
|
Date: January 2019
|
||||||
|
KernelVersion: 5.2
|
||||||
|
Contact: Robert Shearman <robert.shearman@att.com>
|
||||||
|
Description:
|
||||||
|
Value that exists only for mux devices that can be
|
||||||
|
written to control the behaviour of the multiplexer on
|
||||||
|
idle. Possible values:
|
||||||
|
-2 - disconnect on idle, i.e. deselect the last used
|
||||||
|
channel, which is useful when there is a device
|
||||||
|
with an address that conflicts with another
|
||||||
|
device on another mux on the same parent bus.
|
||||||
|
-1 - leave the mux as-is, which is the most optimal
|
||||||
|
setting in terms of I2C operations and is the
|
||||||
|
default mode.
|
||||||
|
0..<nchans> - set the mux to a predetermined channel,
|
||||||
|
which is useful if there is one channel that is
|
||||||
|
used almost always, and you want to reduce the
|
||||||
|
latency for normal operations after rare
|
||||||
|
transactions on other channels
|
|
@ -50,6 +50,7 @@ Required properties:
|
||||||
|
|
||||||
"nxp,se97b" - the fallback is "atmel,24c02",
|
"nxp,se97b" - the fallback is "atmel,24c02",
|
||||||
"renesas,r1ex24002" - the fallback is "atmel,24c02"
|
"renesas,r1ex24002" - the fallback is "atmel,24c02"
|
||||||
|
"renesas,r1ex24016" - the fallback is "atmel,24c16"
|
||||||
"renesas,r1ex24128" - the fallback is "atmel,24c128"
|
"renesas,r1ex24128" - the fallback is "atmel,24c128"
|
||||||
"rohm,br24t01" - the fallback is "atmel,24c01"
|
"rohm,br24t01" - the fallback is "atmel,24c01"
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,12 @@ Broadcom iProc I2C controller
|
||||||
Required properties:
|
Required properties:
|
||||||
|
|
||||||
- compatible:
|
- compatible:
|
||||||
Must be "brcm,iproc-i2c"
|
Must be "brcm,iproc-i2c" or "brcm,iproc-nic-i2c"
|
||||||
|
|
||||||
- reg:
|
- reg:
|
||||||
Define the base and range of the I/O address space that contain the iProc
|
Define the base and range of the I/O address space that contain the iProc
|
||||||
I2C controller registers
|
I2C controller registers
|
||||||
|
|
||||||
- interrupts:
|
|
||||||
Should contain the I2C interrupt
|
|
||||||
|
|
||||||
- clock-frequency:
|
- clock-frequency:
|
||||||
This is the I2C bus clock. Need to be either 100000 or 400000
|
This is the I2C bus clock. Need to be either 100000 or 400000
|
||||||
|
|
||||||
|
@ -21,6 +18,18 @@ Required properties:
|
||||||
- #size-cells:
|
- #size-cells:
|
||||||
Always 0
|
Always 0
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
- interrupts:
|
||||||
|
Should contain the I2C interrupt. For certain revisions of the I2C
|
||||||
|
controller, I2C interrupt is unwired to the interrupt controller. In such
|
||||||
|
case, this property should be left unspecified, and driver will fall back
|
||||||
|
to polling mode
|
||||||
|
|
||||||
|
- brcm,ape-hsls-addr-mask:
|
||||||
|
Required for "brcm,iproc-nic-i2c". Host view of address mask into the
|
||||||
|
'APE' co-processor. Value must be unsigned, 32-bit
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
i2c0: i2c@18008000 {
|
i2c0: i2c@18008000 {
|
||||||
compatible = "brcm,iproc-i2c";
|
compatible = "brcm,iproc-i2c";
|
||||||
|
|
|
@ -6,12 +6,21 @@ Required properties :
|
||||||
or "mscc,ocelot-i2c" with "snps,designware-i2c" for fallback
|
or "mscc,ocelot-i2c" with "snps,designware-i2c" for fallback
|
||||||
- reg : Offset and length of the register set for the device
|
- reg : Offset and length of the register set for the device
|
||||||
- interrupts : <IRQ> where IRQ is the interrupt number.
|
- interrupts : <IRQ> where IRQ is the interrupt number.
|
||||||
|
- clocks : phandles for the clocks, see the description of clock-names below.
|
||||||
|
The phandle for the "ic_clk" clock is required. The phandle for the "pclk"
|
||||||
|
clock is optional. If a single clock is specified but no clock-name, it is
|
||||||
|
the "ic_clk" clock. If both clocks are listed, the "ic_clk" must be first.
|
||||||
|
|
||||||
Recommended properties :
|
Recommended properties :
|
||||||
|
|
||||||
- clock-frequency : desired I2C bus clock frequency in Hz.
|
- clock-frequency : desired I2C bus clock frequency in Hz.
|
||||||
|
|
||||||
Optional properties :
|
Optional properties :
|
||||||
|
|
||||||
|
- clock-names : Contains the names of the clocks:
|
||||||
|
"ic_clk", for the core clock used to generate the external I2C clock.
|
||||||
|
"pclk", the interface clock, required for register access.
|
||||||
|
|
||||||
- reg : for "mscc,ocelot-i2c", a second register set to configure the SDA hold
|
- reg : for "mscc,ocelot-i2c", a second register set to configure the SDA hold
|
||||||
time, named ICPU_CFG:TWI_DELAY in the datasheet.
|
time, named ICPU_CFG:TWI_DELAY in the datasheet.
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,16 @@ Required properties:
|
||||||
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
|
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
|
||||||
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
|
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
|
||||||
"mediatek,mt8173-i2c": for MediaTek MT8173
|
"mediatek,mt8173-i2c": for MediaTek MT8173
|
||||||
|
"mediatek,mt8183-i2c": for MediaTek MT8183
|
||||||
|
"mediatek,mt8516-i2c", "mediatek,mt2712-i2c": for MediaTek MT8516
|
||||||
- reg: physical base address of the controller and dma base, length of memory
|
- reg: physical base address of the controller and dma base, length of memory
|
||||||
mapped region.
|
mapped region.
|
||||||
- interrupts: interrupt number to the cpu.
|
- interrupts: interrupt number to the cpu.
|
||||||
- clock-div: the fixed value for frequency divider of clock source in i2c
|
- clock-div: the fixed value for frequency divider of clock source in i2c
|
||||||
module. Each IC may be different.
|
module. Each IC may be different.
|
||||||
- clocks: clock name from clock manager
|
- clocks: clock name from clock manager
|
||||||
- clock-names: Must include "main" and "dma", if enable have-pmic need include
|
- clock-names: Must include "main" and "dma", "arb" is for multi-master that
|
||||||
|
one bus has more than two i2c controllers, if enable have-pmic need include
|
||||||
"pmic" extra.
|
"pmic" extra.
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
Device tree configuration for Renesas RIIC driver
|
Device tree configuration for Renesas RIIC driver
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "renesas,riic-<soctype>". "renesas,riic-rz" as fallback
|
- compatible :
|
||||||
|
"renesas,riic-r7s72100" if the device is a part of a R7S72100 SoC.
|
||||||
|
"renesas,riic-r7s9210" if the device is a part of a R7S9210 SoC.
|
||||||
|
"renesas,riic-rz" for a generic RZ/A compatible device.
|
||||||
- reg : address start and address range size of device
|
- reg : address start and address range size of device
|
||||||
- interrupts : 8 interrupts (TEI, RI, TI, SPI, STI, NAKI, ALI, TMOI)
|
- interrupts : 8 interrupts (TEI, RI, TI, SPI, STI, NAKI, ALI, TMOI)
|
||||||
- clock-frequency : frequency of bus clock in Hz
|
- clock-frequency : frequency of bus clock in Hz
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
* I2C controller embedded in STMicroelectronics STM32 I2C platform
|
* I2C controller embedded in STMicroelectronics STM32 I2C platform
|
||||||
|
|
||||||
Required properties :
|
Required properties:
|
||||||
- compatible : Must be one of the following
|
- compatible: Must be one of the following
|
||||||
- "st,stm32f4-i2c"
|
- "st,stm32f4-i2c"
|
||||||
- "st,stm32f7-i2c"
|
- "st,stm32f7-i2c"
|
||||||
- reg : Offset and length of the register set for the device
|
- reg: Offset and length of the register set for the device
|
||||||
- interrupts : Must contain the interrupt id for I2C event and then the
|
- interrupts: Must contain the interrupt id for I2C event and then the
|
||||||
interrupt id for I2C error.
|
interrupt id for I2C error.
|
||||||
- resets: Must contain the phandle to the reset controller.
|
- resets: Must contain the phandle to the reset controller.
|
||||||
- clocks: Must contain the input clock of the I2C instance.
|
- clocks: Must contain the input clock of the I2C instance.
|
||||||
|
@ -14,25 +14,26 @@ Required properties :
|
||||||
- #address-cells = <1>;
|
- #address-cells = <1>;
|
||||||
- #size-cells = <0>;
|
- #size-cells = <0>;
|
||||||
|
|
||||||
Optional properties :
|
Optional properties:
|
||||||
- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified,
|
- clock-frequency: Desired I2C bus clock frequency in Hz. If not specified,
|
||||||
the default 100 kHz frequency will be used.
|
the default 100 kHz frequency will be used.
|
||||||
For STM32F4 SoC Standard-mode and Fast-mode are supported, possible values are
|
For STM32F4 SoC Standard-mode and Fast-mode are supported, possible values are
|
||||||
100000 and 400000.
|
100000 and 400000.
|
||||||
For STM32F7 SoC, Standard-mode, Fast-mode and Fast-mode Plus are supported,
|
For STM32F7, STM32H7 and STM32MP1 SoCs, Standard-mode, Fast-mode and Fast-mode
|
||||||
possible values are 100000, 400000 and 1000000.
|
Plus are supported, possible values are 100000, 400000 and 1000000.
|
||||||
- i2c-scl-rising-time-ns : Only for STM32F7, I2C SCL Rising time for the board
|
- i2c-scl-rising-time-ns: I2C SCL Rising time for the board (default: 25)
|
||||||
(default: 25)
|
For STM32F7, STM32H7 and STM32MP1 only.
|
||||||
- i2c-scl-falling-time-ns : Only for STM32F7, I2C SCL Falling time for the board
|
- i2c-scl-falling-time-ns: I2C SCL Falling time for the board (default: 10)
|
||||||
(default: 10)
|
For STM32F7, STM32H7 and STM32MP1 only.
|
||||||
I2C Timings are derived from these 2 values
|
I2C Timings are derived from these 2 values
|
||||||
- st,syscfg-fmp: Only for STM32F7, use to set Fast Mode Plus bit within SYSCFG
|
- st,syscfg-fmp: Use to set Fast Mode Plus bit within SYSCFG when Fast Mode
|
||||||
whether Fast Mode Plus speed is selected by slave.
|
Plus speed is selected by slave.
|
||||||
1st cell : phandle to syscfg
|
1st cell: phandle to syscfg
|
||||||
2nd cell : register offset within SYSCFG
|
2nd cell: register offset within SYSCFG
|
||||||
3rd cell : register bitmask for FMP bit
|
3rd cell: register bitmask for FMP bit
|
||||||
|
For STM32F7, STM32H7 and STM32MP1 only.
|
||||||
|
|
||||||
Example :
|
Example:
|
||||||
|
|
||||||
i2c@40005400 {
|
i2c@40005400 {
|
||||||
compatible = "st,stm32f4-i2c";
|
compatible = "st,stm32f4-i2c";
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
Kernel driver i2c-amd-mp2
|
||||||
|
|
||||||
|
Supported adapters:
|
||||||
|
* AMD MP2 PCIe interface
|
||||||
|
|
||||||
|
Datasheet: not publicly available.
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||||
|
Nehal Shah <nehal-bakulchandra.shah@amd.com>
|
||||||
|
Elie Morisse <syniurge@gmail.com>
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The MP2 is an ARM processor programmed as an I2C controller and communicating
|
||||||
|
with the x86 host through PCI.
|
||||||
|
|
||||||
|
If you see something like this:
|
||||||
|
|
||||||
|
03:00.7 MP2 I2C controller: Advanced Micro Devices, Inc. [AMD] Device 15e6
|
||||||
|
|
||||||
|
in your 'lspci -v', then this driver is for your device.
|
|
@ -15,6 +15,8 @@ Supported adapters:
|
||||||
http://support.amd.com/us/Embedded_TechDocs/44413.pdf
|
http://support.amd.com/us/Embedded_TechDocs/44413.pdf
|
||||||
* AMD Hudson-2, ML, CZ
|
* AMD Hudson-2, ML, CZ
|
||||||
Datasheet: Not publicly available
|
Datasheet: Not publicly available
|
||||||
|
* Hygon CZ
|
||||||
|
Datasheet: Not publicly available
|
||||||
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
|
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
|
||||||
Datasheet: Publicly available at the SMSC website http://www.smsc.com
|
Datasheet: Publicly available at the SMSC website http://www.smsc.com
|
||||||
|
|
||||||
|
|
13
MAINTAINERS
13
MAINTAINERS
|
@ -826,6 +826,14 @@ F: drivers/gpu/drm/amd/include/vi_structs.h
|
||||||
F: drivers/gpu/drm/amd/include/v9_structs.h
|
F: drivers/gpu/drm/amd/include/v9_structs.h
|
||||||
F: include/uapi/linux/kfd_ioctl.h
|
F: include/uapi/linux/kfd_ioctl.h
|
||||||
|
|
||||||
|
AMD MP2 I2C DRIVER
|
||||||
|
M: Elie Morisse <syniurge@gmail.com>
|
||||||
|
M: Nehal Shah <nehal-bakulchandra.shah@amd.com>
|
||||||
|
M: Shyam Sundar S K <shyam-sundar.s-k@amd.com>
|
||||||
|
L: linux-i2c@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/i2c/busses/i2c-amd-mp2*
|
||||||
|
|
||||||
AMD POWERPLAY
|
AMD POWERPLAY
|
||||||
M: Rex Zhu <rex.zhu@amd.com>
|
M: Rex Zhu <rex.zhu@amd.com>
|
||||||
M: Evan Quan <evan.quan@amd.com>
|
M: Evan Quan <evan.quan@amd.com>
|
||||||
|
@ -2582,7 +2590,7 @@ F: include/linux/dmaengine.h
|
||||||
F: include/linux/async_tx.h
|
F: include/linux/async_tx.h
|
||||||
|
|
||||||
AT24 EEPROM DRIVER
|
AT24 EEPROM DRIVER
|
||||||
M: Bartosz Golaszewski <brgl@bgdev.pl>
|
M: Bartosz Golaszewski <bgolaszewski@baylibre.com>
|
||||||
L: linux-i2c@vger.kernel.org
|
L: linux-i2c@vger.kernel.org
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
@ -10207,7 +10215,8 @@ MICROCHIP I2C DRIVER
|
||||||
M: Ludovic Desroches <ludovic.desroches@microchip.com>
|
M: Ludovic Desroches <ludovic.desroches@microchip.com>
|
||||||
L: linux-i2c@vger.kernel.org
|
L: linux-i2c@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/i2c/busses/i2c-at91.c
|
F: drivers/i2c/busses/i2c-at91.h
|
||||||
|
F: drivers/i2c/busses/i2c-at91-*.c
|
||||||
|
|
||||||
MICROCHIP ISC DRIVER
|
MICROCHIP ISC DRIVER
|
||||||
M: Eugen Hristev <eugen.hristev@microchip.com>
|
M: Eugen Hristev <eugen.hristev@microchip.com>
|
||||||
|
|
|
@ -603,6 +603,23 @@ bailout:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We print a warning when we are not flagged to support atomic transfers but
|
||||||
|
* will try anyhow. That's what the I2C core would do as well. Sadly, we can't
|
||||||
|
* modify the algorithm struct at probe time because this struct is exported
|
||||||
|
* 'const'.
|
||||||
|
*/
|
||||||
|
static int bit_xfer_atomic(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||||
|
|
||||||
|
if (!adap->can_do_atomic)
|
||||||
|
dev_warn(&i2c_adap->dev, "not flagged for atomic transfers\n");
|
||||||
|
|
||||||
|
return bit_xfer(i2c_adap, msgs, num);
|
||||||
|
}
|
||||||
|
|
||||||
static u32 bit_func(struct i2c_adapter *adap)
|
static u32 bit_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
|
return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
|
||||||
|
@ -615,8 +632,9 @@ static u32 bit_func(struct i2c_adapter *adap)
|
||||||
/* -----exported algorithm data: ------------------------------------- */
|
/* -----exported algorithm data: ------------------------------------- */
|
||||||
|
|
||||||
const struct i2c_algorithm i2c_bit_algo = {
|
const struct i2c_algorithm i2c_bit_algo = {
|
||||||
.master_xfer = bit_xfer,
|
.master_xfer = bit_xfer,
|
||||||
.functionality = bit_func,
|
.master_xfer_atomic = bit_xfer_atomic,
|
||||||
|
.functionality = bit_func,
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL(i2c_bit_algo);
|
EXPORT_SYMBOL(i2c_bit_algo);
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,16 @@ config I2C_AMD8111
|
||||||
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-amd8111.
|
will be called i2c-amd8111.
|
||||||
|
|
||||||
|
config I2C_AMD_MP2
|
||||||
|
tristate "AMD MP2 PCIe"
|
||||||
|
depends on PCI && ACPI
|
||||||
|
help
|
||||||
|
If you say yes to this option, support will be included for the AMD
|
||||||
|
MP2 PCIe I2C adapter.
|
||||||
|
|
||||||
|
This driver can also be built as modules. If so, the modules will
|
||||||
|
be called i2c-amd-mp2-pci and i2c-amd-mp2-plat.
|
||||||
|
|
||||||
config I2C_HIX5HD2
|
config I2C_HIX5HD2
|
||||||
tristate "Hix5hd2 high-speed I2C driver"
|
tristate "Hix5hd2 high-speed I2C driver"
|
||||||
depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST
|
depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST
|
||||||
|
@ -176,6 +186,7 @@ config I2C_PIIX4
|
||||||
AMD Hudson-2
|
AMD Hudson-2
|
||||||
AMD ML
|
AMD ML
|
||||||
AMD CZ
|
AMD CZ
|
||||||
|
Hygon CZ
|
||||||
Serverworks OSB4
|
Serverworks OSB4
|
||||||
Serverworks CSB5
|
Serverworks CSB5
|
||||||
Serverworks CSB6
|
Serverworks CSB6
|
||||||
|
@ -388,6 +399,19 @@ config I2C_AT91
|
||||||
the latency to fill the transmission register is too long. If you
|
the latency to fill the transmission register is too long. If you
|
||||||
are facing this situation, use the i2c-gpio driver.
|
are facing this situation, use the i2c-gpio driver.
|
||||||
|
|
||||||
|
config I2C_AT91_SLAVE_EXPERIMENTAL
|
||||||
|
tristate "Microchip AT91 I2C experimental slave mode"
|
||||||
|
depends on I2C_AT91
|
||||||
|
select I2C_SLAVE
|
||||||
|
help
|
||||||
|
If you say yes to this option, support for the slave mode will be
|
||||||
|
added. Caution: do not use it for production. This feature has not
|
||||||
|
been tested in a heavy way, help wanted.
|
||||||
|
There are known bugs:
|
||||||
|
- It can hang, on a SAMA5D4, after several transfers.
|
||||||
|
- There are some mismtaches with a SAMA5D4 as slave and a SAMA5D2 as
|
||||||
|
master.
|
||||||
|
|
||||||
config I2C_AU1550
|
config I2C_AU1550
|
||||||
tristate "Au1550/Au1200/Au1300 SMBus interface"
|
tristate "Au1550/Au1200/Au1300 SMBus interface"
|
||||||
depends on MIPS_ALCHEMY
|
depends on MIPS_ALCHEMY
|
||||||
|
@ -425,6 +449,7 @@ config I2C_BCM_IPROC
|
||||||
tristate "Broadcom iProc I2C controller"
|
tristate "Broadcom iProc I2C controller"
|
||||||
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
||||||
default ARCH_BCM_IPROC
|
default ARCH_BCM_IPROC
|
||||||
|
select I2C_SLAVE
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the
|
If you say yes to this option, support will be included for the
|
||||||
Broadcom iProc I2C controller.
|
Broadcom iProc I2C controller.
|
||||||
|
|
|
@ -33,8 +33,13 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
|
||||||
|
|
||||||
# Embedded system I2C/SMBus host controller drivers
|
# Embedded system I2C/SMBus host controller drivers
|
||||||
obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
|
obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
|
||||||
|
obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
|
||||||
obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
|
obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
|
||||||
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
|
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
|
||||||
|
i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o
|
||||||
|
ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y)
|
||||||
|
i2c-at91-objs += i2c-at91-slave.o
|
||||||
|
endif
|
||||||
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
|
||||||
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
|
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
|
||||||
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
|
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
|
||||||
|
|
|
@ -0,0 +1,483 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||||
|
/*
|
||||||
|
* AMD MP2 PCIe communication driver
|
||||||
|
*
|
||||||
|
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||||
|
* Elie Morisse <syniurge@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "i2c-amd-mp2.h"
|
||||||
|
|
||||||
|
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||||
|
|
||||||
|
static void amd_mp2_c2p_mutex_lock(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
|
||||||
|
/* there is only one data mailbox for two i2c adapters */
|
||||||
|
mutex_lock(&privdata->c2p_lock);
|
||||||
|
privdata->c2p_lock_busid = i2c_common->bus_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amd_mp2_c2p_mutex_unlock(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
|
||||||
|
if (unlikely(privdata->c2p_lock_busid != i2c_common->bus_id)) {
|
||||||
|
dev_warn(ndev_dev(privdata),
|
||||||
|
"bus %d attempting to unlock C2P locked by bus %d\n",
|
||||||
|
i2c_common->bus_id, privdata->c2p_lock_busid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&privdata->c2p_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_mp2_cmd(struct amd_i2c_common *i2c_common,
|
||||||
|
union i2c_cmd_base i2c_cmd_base)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
void __iomem *reg;
|
||||||
|
|
||||||
|
i2c_common->reqcmd = i2c_cmd_base.s.i2c_cmd;
|
||||||
|
|
||||||
|
reg = privdata->mmio + ((i2c_cmd_base.s.bus_id == 1) ?
|
||||||
|
AMD_C2P_MSG1 : AMD_C2P_MSG0);
|
||||||
|
writel(i2c_cmd_base.ul, reg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
union i2c_cmd_base i2c_cmd_base;
|
||||||
|
|
||||||
|
dev_dbg(ndev_dev(privdata), "%s id: %d\n", __func__,
|
||||||
|
i2c_common->bus_id);
|
||||||
|
|
||||||
|
i2c_cmd_base.ul = 0;
|
||||||
|
i2c_cmd_base.s.i2c_cmd = enable ? i2c_enable : i2c_disable;
|
||||||
|
i2c_cmd_base.s.bus_id = i2c_common->bus_id;
|
||||||
|
i2c_cmd_base.s.i2c_speed = i2c_common->i2c_speed;
|
||||||
|
|
||||||
|
amd_mp2_c2p_mutex_lock(i2c_common);
|
||||||
|
|
||||||
|
return amd_mp2_cmd(i2c_common, i2c_cmd_base);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(amd_mp2_bus_enable_set);
|
||||||
|
|
||||||
|
static void amd_mp2_cmd_rw_fill(struct amd_i2c_common *i2c_common,
|
||||||
|
union i2c_cmd_base *i2c_cmd_base,
|
||||||
|
enum i2c_cmd reqcmd)
|
||||||
|
{
|
||||||
|
i2c_cmd_base->s.i2c_cmd = reqcmd;
|
||||||
|
i2c_cmd_base->s.bus_id = i2c_common->bus_id;
|
||||||
|
i2c_cmd_base->s.i2c_speed = i2c_common->i2c_speed;
|
||||||
|
i2c_cmd_base->s.slave_addr = i2c_common->msg->addr;
|
||||||
|
i2c_cmd_base->s.length = i2c_common->msg->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
union i2c_cmd_base i2c_cmd_base;
|
||||||
|
|
||||||
|
amd_mp2_cmd_rw_fill(i2c_common, &i2c_cmd_base, reqcmd);
|
||||||
|
amd_mp2_c2p_mutex_lock(i2c_common);
|
||||||
|
|
||||||
|
if (i2c_common->msg->len <= 32) {
|
||||||
|
i2c_cmd_base.s.mem_type = use_c2pmsg;
|
||||||
|
if (reqcmd == i2c_write)
|
||||||
|
memcpy_toio(privdata->mmio + AMD_C2P_MSG2,
|
||||||
|
i2c_common->msg->buf,
|
||||||
|
i2c_common->msg->len);
|
||||||
|
} else {
|
||||||
|
i2c_cmd_base.s.mem_type = use_dram;
|
||||||
|
writeq((u64)i2c_common->dma_addr,
|
||||||
|
privdata->mmio + AMD_C2P_MSG2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return amd_mp2_cmd(i2c_common, i2c_cmd_base);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(amd_mp2_rw);
|
||||||
|
|
||||||
|
static void amd_mp2_pci_check_rw_event(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
int len = i2c_common->eventval.r.length;
|
||||||
|
u32 slave_addr = i2c_common->eventval.r.slave_addr;
|
||||||
|
bool err = false;
|
||||||
|
|
||||||
|
if (unlikely(len != i2c_common->msg->len)) {
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"length %d in event doesn't match buffer length %d!\n",
|
||||||
|
len, i2c_common->msg->len);
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(slave_addr != i2c_common->msg->addr)) {
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"unexpected slave address %x (expected: %x)!\n",
|
||||||
|
slave_addr, i2c_common->msg->addr);
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
i2c_common->cmd_success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
enum status_type sts = i2c_common->eventval.r.status;
|
||||||
|
enum response_type res = i2c_common->eventval.r.response;
|
||||||
|
int len = i2c_common->eventval.r.length;
|
||||||
|
|
||||||
|
if (res != command_success) {
|
||||||
|
if (res != command_failed)
|
||||||
|
dev_err(ndev_dev(privdata), "invalid response to i2c command!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (i2c_common->reqcmd) {
|
||||||
|
case i2c_read:
|
||||||
|
if (sts == i2c_readcomplete_event) {
|
||||||
|
amd_mp2_pci_check_rw_event(i2c_common);
|
||||||
|
if (len <= 32)
|
||||||
|
memcpy_fromio(i2c_common->msg->buf,
|
||||||
|
privdata->mmio + AMD_C2P_MSG2,
|
||||||
|
len);
|
||||||
|
} else if (sts != i2c_readfail_event) {
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"invalid i2c status after read (%d)!\n", sts);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case i2c_write:
|
||||||
|
if (sts == i2c_writecomplete_event)
|
||||||
|
amd_mp2_pci_check_rw_event(i2c_common);
|
||||||
|
else if (sts != i2c_writefail_event)
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"invalid i2c status after write (%d)!\n", sts);
|
||||||
|
break;
|
||||||
|
case i2c_enable:
|
||||||
|
if (sts == i2c_busenable_complete)
|
||||||
|
i2c_common->cmd_success = true;
|
||||||
|
else if (sts != i2c_busenable_failed)
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"invalid i2c status after bus enable (%d)!\n",
|
||||||
|
sts);
|
||||||
|
break;
|
||||||
|
case i2c_disable:
|
||||||
|
if (sts == i2c_busdisable_complete)
|
||||||
|
i2c_common->cmd_success = true;
|
||||||
|
else if (sts != i2c_busdisable_failed)
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"invalid i2c status after bus disable (%d)!\n",
|
||||||
|
sts);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd_mp2_process_event(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
|
||||||
|
if (unlikely(i2c_common->reqcmd == i2c_none)) {
|
||||||
|
dev_warn(ndev_dev(privdata),
|
||||||
|
"received msg but no cmd was sent (bus = %d)!\n",
|
||||||
|
i2c_common->bus_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__amd_mp2_process_event(i2c_common);
|
||||||
|
|
||||||
|
i2c_common->reqcmd = i2c_none;
|
||||||
|
amd_mp2_c2p_mutex_unlock(i2c_common);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(amd_mp2_process_event);
|
||||||
|
|
||||||
|
static irqreturn_t amd_mp2_irq_isr(int irq, void *dev)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = dev;
|
||||||
|
struct amd_i2c_common *i2c_common;
|
||||||
|
u32 val;
|
||||||
|
unsigned int bus_id;
|
||||||
|
void __iomem *reg;
|
||||||
|
enum irqreturn ret = IRQ_NONE;
|
||||||
|
|
||||||
|
for (bus_id = 0; bus_id < 2; bus_id++) {
|
||||||
|
i2c_common = privdata->busses[bus_id];
|
||||||
|
if (!i2c_common)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
reg = privdata->mmio + ((bus_id == 0) ?
|
||||||
|
AMD_P2C_MSG1 : AMD_P2C_MSG2);
|
||||||
|
val = readl(reg);
|
||||||
|
if (val != 0) {
|
||||||
|
writel(0, reg);
|
||||||
|
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
|
||||||
|
i2c_common->eventval.ul = val;
|
||||||
|
i2c_common->cmd_completion(i2c_common);
|
||||||
|
|
||||||
|
ret = IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != IRQ_HANDLED) {
|
||||||
|
val = readl(privdata->mmio + AMD_P2C_MSG_INTEN);
|
||||||
|
if (val != 0) {
|
||||||
|
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
|
||||||
|
dev_warn(ndev_dev(privdata),
|
||||||
|
"received irq without message\n");
|
||||||
|
ret = IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
i2c_common->reqcmd = i2c_none;
|
||||||
|
amd_mp2_c2p_mutex_unlock(i2c_common);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(amd_mp2_rw_timeout);
|
||||||
|
|
||||||
|
int amd_mp2_register_cb(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
|
||||||
|
if (i2c_common->bus_id > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (privdata->busses[i2c_common->bus_id]) {
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"Bus %d already taken!\n", i2c_common->bus_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
privdata->busses[i2c_common->bus_id] = i2c_common;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(amd_mp2_register_cb);
|
||||||
|
|
||||||
|
int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
|
||||||
|
|
||||||
|
privdata->busses[i2c_common->bus_id] = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(amd_mp2_unregister_cb);
|
||||||
|
|
||||||
|
static void amd_mp2_clear_reg(struct amd_mp2_dev *privdata)
|
||||||
|
{
|
||||||
|
int reg;
|
||||||
|
|
||||||
|
for (reg = AMD_C2P_MSG0; reg <= AMD_C2P_MSG9; reg += 4)
|
||||||
|
writel(0, privdata->mmio + reg);
|
||||||
|
|
||||||
|
for (reg = AMD_P2C_MSG1; reg <= AMD_P2C_MSG2; reg += 4)
|
||||||
|
writel(0, privdata->mmio + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
|
||||||
|
struct pci_dev *pci_dev)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
pci_set_drvdata(pci_dev, privdata);
|
||||||
|
|
||||||
|
rc = pcim_enable_device(pci_dev);
|
||||||
|
if (rc) {
|
||||||
|
dev_err(ndev_dev(privdata), "Failed to enable MP2 PCI device\n");
|
||||||
|
goto err_pci_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pcim_iomap_regions(pci_dev, 1 << 2, pci_name(pci_dev));
|
||||||
|
if (rc) {
|
||||||
|
dev_err(ndev_dev(privdata), "I/O memory remapping failed\n");
|
||||||
|
goto err_pci_enable;
|
||||||
|
}
|
||||||
|
privdata->mmio = pcim_iomap_table(pci_dev)[2];
|
||||||
|
|
||||||
|
pci_set_master(pci_dev);
|
||||||
|
|
||||||
|
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));
|
||||||
|
if (rc) {
|
||||||
|
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
|
||||||
|
if (rc)
|
||||||
|
goto err_dma_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up intx irq */
|
||||||
|
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
|
||||||
|
pci_intx(pci_dev, 1);
|
||||||
|
rc = devm_request_irq(&pci_dev->dev, pci_dev->irq, amd_mp2_irq_isr,
|
||||||
|
IRQF_SHARED, dev_name(&pci_dev->dev), privdata);
|
||||||
|
if (rc)
|
||||||
|
dev_err(&pci_dev->dev, "Failure requesting irq %i: %d\n",
|
||||||
|
pci_dev->irq, rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
err_dma_mask:
|
||||||
|
pci_clear_master(pci_dev);
|
||||||
|
err_pci_enable:
|
||||||
|
pci_set_drvdata(pci_dev, NULL);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_mp2_pci_probe(struct pci_dev *pci_dev,
|
||||||
|
const struct pci_device_id *id)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
privdata = devm_kzalloc(&pci_dev->dev, sizeof(*privdata), GFP_KERNEL);
|
||||||
|
if (!privdata)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rc = amd_mp2_pci_init(privdata, pci_dev);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
mutex_init(&privdata->c2p_lock);
|
||||||
|
privdata->pci_dev = pci_dev;
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
|
||||||
|
pm_runtime_use_autosuspend(&pci_dev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&pci_dev->dev);
|
||||||
|
pm_runtime_allow(&pci_dev->dev);
|
||||||
|
|
||||||
|
privdata->probed = true;
|
||||||
|
|
||||||
|
dev_info(&pci_dev->dev, "MP2 device registered.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amd_mp2_pci_remove(struct pci_dev *pci_dev)
|
||||||
|
{
|
||||||
|
struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
|
||||||
|
|
||||||
|
pm_runtime_forbid(&pci_dev->dev);
|
||||||
|
pm_runtime_get_noresume(&pci_dev->dev);
|
||||||
|
|
||||||
|
pci_intx(pci_dev, 0);
|
||||||
|
pci_clear_master(pci_dev);
|
||||||
|
|
||||||
|
amd_mp2_clear_reg(privdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int amd_mp2_pci_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
|
||||||
|
struct amd_i2c_common *i2c_common;
|
||||||
|
unsigned int bus_id;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for (bus_id = 0; bus_id < 2; bus_id++) {
|
||||||
|
i2c_common = privdata->busses[bus_id];
|
||||||
|
if (i2c_common)
|
||||||
|
i2c_common->suspend(i2c_common);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pci_save_state(pci_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"pci_save_state failed = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_disable_device(pci_dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_mp2_pci_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
|
||||||
|
struct amd_i2c_common *i2c_common;
|
||||||
|
unsigned int bus_id;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pci_restore_state(pci_dev);
|
||||||
|
ret = pci_enable_device(pci_dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(ndev_dev(privdata),
|
||||||
|
"pci_enable_device failed = %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (bus_id = 0; bus_id < 2; bus_id++) {
|
||||||
|
i2c_common = privdata->busses[bus_id];
|
||||||
|
if (i2c_common) {
|
||||||
|
ret = i2c_common->resume(i2c_common);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UNIVERSAL_DEV_PM_OPS(amd_mp2_pci_pm_ops, amd_mp2_pci_suspend,
|
||||||
|
amd_mp2_pci_resume, NULL);
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
static const struct pci_device_id amd_mp2_pci_tbl[] = {
|
||||||
|
{PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2)},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
|
||||||
|
|
||||||
|
static struct pci_driver amd_mp2_pci_driver = {
|
||||||
|
.name = "i2c_amd_mp2",
|
||||||
|
.id_table = amd_mp2_pci_tbl,
|
||||||
|
.probe = amd_mp2_pci_probe,
|
||||||
|
.remove = amd_mp2_pci_remove,
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.driver = {
|
||||||
|
.pm = &amd_mp2_pci_pm_ops,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
module_pci_driver(amd_mp2_pci_driver);
|
||||||
|
|
||||||
|
static int amd_mp2_device_match(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct amd_mp2_dev *amd_mp2_find_device(void)
|
||||||
|
{
|
||||||
|
struct device *dev;
|
||||||
|
struct pci_dev *pci_dev;
|
||||||
|
|
||||||
|
dev = driver_find_device(&amd_mp2_pci_driver.driver, NULL, NULL,
|
||||||
|
amd_mp2_device_match);
|
||||||
|
if (!dev)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pci_dev = to_pci_dev(dev);
|
||||||
|
return (struct amd_mp2_dev *)pci_get_drvdata(pci_dev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(amd_mp2_find_device);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("AMD(R) PCI-E MP2 I2C Controller Driver");
|
||||||
|
MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
|
||||||
|
MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
|
||||||
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
@ -0,0 +1,367 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||||
|
/*
|
||||||
|
* AMD MP2 platform driver
|
||||||
|
*
|
||||||
|
* Setup the I2C adapters enumerated in the ACPI namespace.
|
||||||
|
* MP2 controllers have 2 separate busses, up to 2 I2C adapters may be listed.
|
||||||
|
*
|
||||||
|
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
|
||||||
|
* Elie Morisse <syniurge@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "i2c-amd-mp2.h"
|
||||||
|
|
||||||
|
#define AMD_MP2_I2C_MAX_RW_LENGTH ((1 << 12) - 1)
|
||||||
|
#define AMD_I2C_TIMEOUT (msecs_to_jiffies(250))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct amd_i2c_dev - MP2 bus/i2c adapter context
|
||||||
|
* @common: shared context with the MP2 PCI driver
|
||||||
|
* @pdev: platform driver node
|
||||||
|
* @adap: i2c adapter
|
||||||
|
* @cmd_complete: xfer completion object
|
||||||
|
*/
|
||||||
|
struct amd_i2c_dev {
|
||||||
|
struct amd_i2c_common common;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct i2c_adapter adap;
|
||||||
|
struct completion cmd_complete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define amd_i2c_dev_common(__common) \
|
||||||
|
container_of(__common, struct amd_i2c_dev, common)
|
||||||
|
|
||||||
|
static int i2c_amd_dma_map(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
|
||||||
|
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
|
||||||
|
enum dma_data_direction dma_direction =
|
||||||
|
i2c_common->msg->flags & I2C_M_RD ?
|
||||||
|
DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||||
|
|
||||||
|
i2c_common->dma_buf = i2c_get_dma_safe_msg_buf(i2c_common->msg, 0);
|
||||||
|
i2c_common->dma_addr = dma_map_single(dev_pci, i2c_common->dma_buf,
|
||||||
|
i2c_common->msg->len,
|
||||||
|
dma_direction);
|
||||||
|
|
||||||
|
if (unlikely(dma_mapping_error(dev_pci, i2c_common->dma_addr))) {
|
||||||
|
dev_err(&i2c_dev->pdev->dev,
|
||||||
|
"Error while mapping dma buffer %p\n",
|
||||||
|
i2c_common->dma_buf);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_amd_dma_unmap(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
|
||||||
|
enum dma_data_direction dma_direction =
|
||||||
|
i2c_common->msg->flags & I2C_M_RD ?
|
||||||
|
DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||||
|
|
||||||
|
dma_unmap_single(dev_pci, i2c_common->dma_addr,
|
||||||
|
i2c_common->msg->len, dma_direction);
|
||||||
|
|
||||||
|
i2c_put_dma_safe_msg_buf(i2c_common->dma_buf, i2c_common->msg, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_amd_start_cmd(struct amd_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct amd_i2c_common *i2c_common = &i2c_dev->common;
|
||||||
|
|
||||||
|
reinit_completion(&i2c_dev->cmd_complete);
|
||||||
|
i2c_common->cmd_success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_amd_cmd_completion(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
|
||||||
|
union i2c_event *event = &i2c_common->eventval;
|
||||||
|
|
||||||
|
if (event->r.status == i2c_readcomplete_event)
|
||||||
|
dev_dbg(&i2c_dev->pdev->dev, "%s readdata:%*ph\n",
|
||||||
|
__func__, event->r.length,
|
||||||
|
i2c_common->msg->buf);
|
||||||
|
|
||||||
|
complete(&i2c_dev->cmd_complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_amd_check_cmd_completion(struct amd_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct amd_i2c_common *i2c_common = &i2c_dev->common;
|
||||||
|
unsigned long timeout;
|
||||||
|
|
||||||
|
timeout = wait_for_completion_timeout(&i2c_dev->cmd_complete,
|
||||||
|
i2c_dev->adap.timeout);
|
||||||
|
|
||||||
|
if ((i2c_common->reqcmd == i2c_read ||
|
||||||
|
i2c_common->reqcmd == i2c_write) &&
|
||||||
|
i2c_common->msg->len > 32)
|
||||||
|
i2c_amd_dma_unmap(i2c_common);
|
||||||
|
|
||||||
|
if (timeout == 0) {
|
||||||
|
amd_mp2_rw_timeout(i2c_common);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
amd_mp2_process_event(i2c_common);
|
||||||
|
|
||||||
|
if (!i2c_common->cmd_success)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_amd_enable_set(struct amd_i2c_dev *i2c_dev, bool enable)
|
||||||
|
{
|
||||||
|
struct amd_i2c_common *i2c_common = &i2c_dev->common;
|
||||||
|
|
||||||
|
i2c_amd_start_cmd(i2c_dev);
|
||||||
|
amd_mp2_bus_enable_set(i2c_common, enable);
|
||||||
|
|
||||||
|
return i2c_amd_check_cmd_completion(i2c_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_amd_xfer_msg(struct amd_i2c_dev *i2c_dev, struct i2c_msg *pmsg)
|
||||||
|
{
|
||||||
|
struct amd_i2c_common *i2c_common = &i2c_dev->common;
|
||||||
|
|
||||||
|
i2c_amd_start_cmd(i2c_dev);
|
||||||
|
i2c_common->msg = pmsg;
|
||||||
|
|
||||||
|
if (pmsg->len > 32)
|
||||||
|
if (i2c_amd_dma_map(i2c_common))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (pmsg->flags & I2C_M_RD)
|
||||||
|
amd_mp2_rw(i2c_common, i2c_read);
|
||||||
|
else
|
||||||
|
amd_mp2_rw(i2c_common, i2c_write);
|
||||||
|
|
||||||
|
return i2c_amd_check_cmd_completion(i2c_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
|
{
|
||||||
|
struct amd_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||||
|
int i;
|
||||||
|
struct i2c_msg *pmsg;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* the adapter might have been deleted while waiting for the bus lock */
|
||||||
|
if (unlikely(!i2c_dev->common.mp2_dev))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
amd_mp2_pm_runtime_get(i2c_dev->common.mp2_dev);
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
pmsg = &msgs[i];
|
||||||
|
err = i2c_amd_xfer_msg(i2c_dev, pmsg);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
amd_mp2_pm_runtime_put(i2c_dev->common.mp2_dev);
|
||||||
|
return err ? err : num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 i2c_amd_func(struct i2c_adapter *a)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm i2c_amd_algorithm = {
|
||||||
|
.master_xfer = i2c_amd_xfer,
|
||||||
|
.functionality = i2c_amd_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int i2c_amd_suspend(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
|
||||||
|
|
||||||
|
i2c_amd_enable_set(i2c_dev, false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_amd_resume(struct amd_i2c_common *i2c_common)
|
||||||
|
{
|
||||||
|
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
|
||||||
|
|
||||||
|
return i2c_amd_enable_set(i2c_dev, true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static enum speed_enum i2c_amd_get_bus_speed(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
u32 acpi_speed;
|
||||||
|
int i;
|
||||||
|
static const u32 supported_speeds[] = {
|
||||||
|
0, 100000, 400000, 1000000, 1400000, 3400000
|
||||||
|
};
|
||||||
|
|
||||||
|
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
|
||||||
|
/* round down to the lowest standard speed */
|
||||||
|
for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) {
|
||||||
|
if (acpi_speed < supported_speeds[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
acpi_speed = supported_speeds[i - 1];
|
||||||
|
|
||||||
|
switch (acpi_speed) {
|
||||||
|
case 100000:
|
||||||
|
return speed100k;
|
||||||
|
case 400000:
|
||||||
|
return speed400k;
|
||||||
|
case 1000000:
|
||||||
|
return speed1000k;
|
||||||
|
case 1400000:
|
||||||
|
return speed1400k;
|
||||||
|
case 3400000:
|
||||||
|
return speed3400k;
|
||||||
|
default:
|
||||||
|
return speed400k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_adapter_quirks amd_i2c_dev_quirks = {
|
||||||
|
.max_read_len = AMD_MP2_I2C_MAX_RW_LENGTH,
|
||||||
|
.max_write_len = AMD_MP2_I2C_MAX_RW_LENGTH,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int i2c_amd_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct amd_i2c_dev *i2c_dev;
|
||||||
|
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
|
||||||
|
struct acpi_device *adev;
|
||||||
|
struct amd_mp2_dev *mp2_dev;
|
||||||
|
const char *uid;
|
||||||
|
|
||||||
|
if (acpi_bus_get_device(handle, &adev))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* The ACPI namespace doesn't contain information about which MP2 PCI
|
||||||
|
* device an AMDI0011 ACPI device is related to, so assume that there's
|
||||||
|
* only one MP2 PCI device per system.
|
||||||
|
*/
|
||||||
|
mp2_dev = amd_mp2_find_device();
|
||||||
|
if (!mp2_dev || !mp2_dev->probed)
|
||||||
|
/* The MP2 PCI device should get probed later */
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
|
||||||
|
if (!i2c_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
i2c_dev->common.mp2_dev = mp2_dev;
|
||||||
|
i2c_dev->pdev = pdev;
|
||||||
|
platform_set_drvdata(pdev, i2c_dev);
|
||||||
|
|
||||||
|
i2c_dev->common.cmd_completion = &i2c_amd_cmd_completion;
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
i2c_dev->common.suspend = &i2c_amd_suspend;
|
||||||
|
i2c_dev->common.resume = &i2c_amd_resume;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uid = adev->pnp.unique_id;
|
||||||
|
if (!uid) {
|
||||||
|
dev_err(&pdev->dev, "missing UID/bus id!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (strcmp(uid, "0") == 0) {
|
||||||
|
i2c_dev->common.bus_id = 0;
|
||||||
|
} else if (strcmp(uid, "1") == 0) {
|
||||||
|
i2c_dev->common.bus_id = 1;
|
||||||
|
} else {
|
||||||
|
dev_err(&pdev->dev, "incorrect UID/bus id \"%s\"!\n", uid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
dev_dbg(&pdev->dev, "bus id is %u\n", i2c_dev->common.bus_id);
|
||||||
|
|
||||||
|
/* Register the adapter */
|
||||||
|
amd_mp2_pm_runtime_get(mp2_dev);
|
||||||
|
|
||||||
|
i2c_dev->common.reqcmd = i2c_none;
|
||||||
|
if (amd_mp2_register_cb(&i2c_dev->common))
|
||||||
|
return -EINVAL;
|
||||||
|
device_link_add(&i2c_dev->pdev->dev, &mp2_dev->pci_dev->dev,
|
||||||
|
DL_FLAG_AUTOREMOVE_CONSUMER);
|
||||||
|
|
||||||
|
i2c_dev->common.i2c_speed = i2c_amd_get_bus_speed(pdev);
|
||||||
|
|
||||||
|
/* Setup i2c adapter description */
|
||||||
|
i2c_dev->adap.owner = THIS_MODULE;
|
||||||
|
i2c_dev->adap.algo = &i2c_amd_algorithm;
|
||||||
|
i2c_dev->adap.quirks = &amd_i2c_dev_quirks;
|
||||||
|
i2c_dev->adap.dev.parent = &pdev->dev;
|
||||||
|
i2c_dev->adap.algo_data = i2c_dev;
|
||||||
|
i2c_dev->adap.timeout = AMD_I2C_TIMEOUT;
|
||||||
|
ACPI_COMPANION_SET(&i2c_dev->adap.dev, ACPI_COMPANION(&pdev->dev));
|
||||||
|
i2c_dev->adap.dev.of_node = pdev->dev.of_node;
|
||||||
|
snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
|
||||||
|
"AMD MP2 i2c bus %u", i2c_dev->common.bus_id);
|
||||||
|
i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
|
||||||
|
|
||||||
|
init_completion(&i2c_dev->cmd_complete);
|
||||||
|
|
||||||
|
/* Enable the bus */
|
||||||
|
if (i2c_amd_enable_set(i2c_dev, true))
|
||||||
|
dev_err(&pdev->dev, "initial bus enable failed\n");
|
||||||
|
|
||||||
|
/* Attach to the i2c layer */
|
||||||
|
ret = i2c_add_adapter(&i2c_dev->adap);
|
||||||
|
|
||||||
|
amd_mp2_pm_runtime_put(mp2_dev);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_amd_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct amd_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||||
|
struct amd_i2c_common *i2c_common = &i2c_dev->common;
|
||||||
|
|
||||||
|
i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
|
i2c_amd_enable_set(i2c_dev, false);
|
||||||
|
amd_mp2_unregister_cb(i2c_common);
|
||||||
|
i2c_common->mp2_dev = NULL;
|
||||||
|
|
||||||
|
i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
|
||||||
|
i2c_del_adapter(&i2c_dev->adap);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct acpi_device_id i2c_amd_acpi_match[] = {
|
||||||
|
{ "AMDI0011" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match);
|
||||||
|
|
||||||
|
static struct platform_driver i2c_amd_plat_driver = {
|
||||||
|
.probe = i2c_amd_probe,
|
||||||
|
.remove = i2c_amd_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "i2c_amd_mp2",
|
||||||
|
.acpi_match_table = ACPI_PTR(i2c_amd_acpi_match),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(i2c_amd_plat_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("AMD(R) MP2 I2C Platform Driver");
|
||||||
|
MODULE_AUTHOR("Nehal Shah <nehal-bakulchandra.shah@amd.com>");
|
||||||
|
MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
|
||||||
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
@ -0,0 +1,219 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||||
|
/*
|
||||||
|
* AMD MP2 I2C adapter driver
|
||||||
|
*
|
||||||
|
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||||
|
* Elie Morisse <syniurge@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I2C_AMD_PCI_MP2_H
|
||||||
|
#define I2C_AMD_PCI_MP2_H
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
|
#define PCI_DEVICE_ID_AMD_MP2 0x15E6
|
||||||
|
|
||||||
|
struct amd_i2c_common;
|
||||||
|
struct amd_mp2_dev;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* MP2 C2P Message Registers */
|
||||||
|
AMD_C2P_MSG0 = 0x10500, /* MP2 Message for I2C0 */
|
||||||
|
AMD_C2P_MSG1 = 0x10504, /* MP2 Message for I2C1 */
|
||||||
|
AMD_C2P_MSG2 = 0x10508, /* DRAM Address Lo / Data 0 */
|
||||||
|
AMD_C2P_MSG3 = 0x1050c, /* DRAM Address HI / Data 1 */
|
||||||
|
AMD_C2P_MSG4 = 0x10510, /* Data 2 */
|
||||||
|
AMD_C2P_MSG5 = 0x10514, /* Data 3 */
|
||||||
|
AMD_C2P_MSG6 = 0x10518, /* Data 4 */
|
||||||
|
AMD_C2P_MSG7 = 0x1051c, /* Data 5 */
|
||||||
|
AMD_C2P_MSG8 = 0x10520, /* Data 6 */
|
||||||
|
AMD_C2P_MSG9 = 0x10524, /* Data 7 */
|
||||||
|
|
||||||
|
/* MP2 P2C Message Registers */
|
||||||
|
AMD_P2C_MSG0 = 0x10680, /* Do not use */
|
||||||
|
AMD_P2C_MSG1 = 0x10684, /* I2C0 interrupt register */
|
||||||
|
AMD_P2C_MSG2 = 0x10688, /* I2C1 interrupt register */
|
||||||
|
AMD_P2C_MSG3 = 0x1068C, /* MP2 debug info */
|
||||||
|
AMD_P2C_MSG_INTEN = 0x10690, /* MP2 interrupt gen register */
|
||||||
|
AMD_P2C_MSG_INTSTS = 0x10694, /* Interrupt status */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Command register data structures */
|
||||||
|
|
||||||
|
#define i2c_none (-1)
|
||||||
|
enum i2c_cmd {
|
||||||
|
i2c_read = 0,
|
||||||
|
i2c_write,
|
||||||
|
i2c_enable,
|
||||||
|
i2c_disable,
|
||||||
|
number_of_sensor_discovered,
|
||||||
|
is_mp2_active,
|
||||||
|
invalid_cmd = 0xF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum speed_enum {
|
||||||
|
speed100k = 0,
|
||||||
|
speed400k = 1,
|
||||||
|
speed1000k = 2,
|
||||||
|
speed1400k = 3,
|
||||||
|
speed3400k = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mem_type {
|
||||||
|
use_dram = 0,
|
||||||
|
use_c2pmsg = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* union i2c_cmd_base : bit access of C2P commands
|
||||||
|
* @i2c_cmd: bit 0..3 i2c R/W command
|
||||||
|
* @bus_id: bit 4..7 i2c bus index
|
||||||
|
* @slave_addr: bit 8..15 slave address
|
||||||
|
* @length: bit 16..27 read/write length
|
||||||
|
* @i2c_speed: bit 28..30 bus speed
|
||||||
|
* @mem_type: bit 31 0-DRAM; 1-C2P msg o/p
|
||||||
|
*/
|
||||||
|
union i2c_cmd_base {
|
||||||
|
u32 ul;
|
||||||
|
struct {
|
||||||
|
enum i2c_cmd i2c_cmd : 4;
|
||||||
|
u8 bus_id : 4;
|
||||||
|
u32 slave_addr : 8;
|
||||||
|
u32 length : 12;
|
||||||
|
enum speed_enum i2c_speed : 3;
|
||||||
|
enum mem_type mem_type : 1;
|
||||||
|
} s;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum response_type {
|
||||||
|
invalid_response = 0,
|
||||||
|
command_success = 1,
|
||||||
|
command_failed = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum status_type {
|
||||||
|
i2c_readcomplete_event = 0,
|
||||||
|
i2c_readfail_event = 1,
|
||||||
|
i2c_writecomplete_event = 2,
|
||||||
|
i2c_writefail_event = 3,
|
||||||
|
i2c_busenable_complete = 4,
|
||||||
|
i2c_busenable_failed = 5,
|
||||||
|
i2c_busdisable_complete = 6,
|
||||||
|
i2c_busdisable_failed = 7,
|
||||||
|
invalid_data_length = 8,
|
||||||
|
invalid_slave_address = 9,
|
||||||
|
invalid_i2cbus_id = 10,
|
||||||
|
invalid_dram_addr = 11,
|
||||||
|
invalid_command = 12,
|
||||||
|
mp2_active = 13,
|
||||||
|
numberof_sensors_discovered_resp = 14,
|
||||||
|
i2c_bus_notinitialized
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* union i2c_event : bit access of P2C events
|
||||||
|
* @response: bit 0..1 i2c response type
|
||||||
|
* @status: bit 2..6 status_type
|
||||||
|
* @mem_type: bit 7 0-DRAM; 1-C2P msg o/p
|
||||||
|
* @bus_id: bit 8..11 i2c bus id
|
||||||
|
* @length: bit 12..23 message length
|
||||||
|
* @slave_addr: bit 24-31 slave address
|
||||||
|
*/
|
||||||
|
union i2c_event {
|
||||||
|
u32 ul;
|
||||||
|
struct {
|
||||||
|
enum response_type response : 2;
|
||||||
|
enum status_type status : 5;
|
||||||
|
enum mem_type mem_type : 1;
|
||||||
|
u8 bus_id : 4;
|
||||||
|
u32 length : 12;
|
||||||
|
u32 slave_addr : 8;
|
||||||
|
} r;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct amd_i2c_common - per bus/i2c adapter context, shared
|
||||||
|
* between the pci and the platform driver
|
||||||
|
* @eventval: MP2 event value set by the IRQ handler
|
||||||
|
* @mp2_dev: MP2 pci device this adapter is part of
|
||||||
|
* @msg: i2c message
|
||||||
|
* @cmd_completion: function called by the IRQ handler to signal
|
||||||
|
* the platform driver
|
||||||
|
* @reqcmd: requested i2c command type
|
||||||
|
* @cmd_success: set to true if the MP2 responded to a command with
|
||||||
|
* the expected status and response type
|
||||||
|
* @bus_id: bus index
|
||||||
|
* @i2c_speed: i2c bus speed determined by the slowest slave
|
||||||
|
* @dma_buf: if msg length > 32, holds the DMA buffer virtual address
|
||||||
|
* @dma_addr: if msg length > 32, holds the DMA buffer address
|
||||||
|
*/
|
||||||
|
struct amd_i2c_common {
|
||||||
|
union i2c_event eventval;
|
||||||
|
struct amd_mp2_dev *mp2_dev;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
void (*cmd_completion)(struct amd_i2c_common *i2c_common);
|
||||||
|
enum i2c_cmd reqcmd;
|
||||||
|
u8 cmd_success;
|
||||||
|
u8 bus_id;
|
||||||
|
enum speed_enum i2c_speed;
|
||||||
|
u8 *dma_buf;
|
||||||
|
dma_addr_t dma_addr;
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
int (*suspend)(struct amd_i2c_common *i2c_common);
|
||||||
|
int (*resume)(struct amd_i2c_common *i2c_common);
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct amd_mp2_dev - per PCI device context
|
||||||
|
* @pci_dev: PCI driver node
|
||||||
|
* @busses: MP2 devices may have up to two busses,
|
||||||
|
* each bus corresponding to an i2c adapter
|
||||||
|
* @mmio: iommapped registers
|
||||||
|
* @c2p_lock: controls access to the C2P mailbox shared between
|
||||||
|
* the two adapters
|
||||||
|
* @c2p_lock_busid: id of the adapter which locked c2p_lock
|
||||||
|
*/
|
||||||
|
struct amd_mp2_dev {
|
||||||
|
struct pci_dev *pci_dev;
|
||||||
|
struct amd_i2c_common *busses[2];
|
||||||
|
void __iomem *mmio;
|
||||||
|
struct mutex c2p_lock;
|
||||||
|
u8 c2p_lock_busid;
|
||||||
|
unsigned int probed;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ndev_pdev(ndev) ((ndev)->pci_dev)
|
||||||
|
#define ndev_name(ndev) pci_name(ndev_pdev(ndev))
|
||||||
|
#define ndev_dev(ndev) (&ndev_pdev(ndev)->dev)
|
||||||
|
#define work_amd_i2c_common(__work) \
|
||||||
|
container_of(__work, struct amd_i2c_common, work.work)
|
||||||
|
|
||||||
|
/* PCIe communication driver */
|
||||||
|
|
||||||
|
int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd);
|
||||||
|
int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable);
|
||||||
|
|
||||||
|
void amd_mp2_process_event(struct amd_i2c_common *i2c_common);
|
||||||
|
|
||||||
|
void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common);
|
||||||
|
|
||||||
|
int amd_mp2_register_cb(struct amd_i2c_common *i2c_common);
|
||||||
|
int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common);
|
||||||
|
|
||||||
|
struct amd_mp2_dev *amd_mp2_find_device(void);
|
||||||
|
|
||||||
|
static inline void amd_mp2_pm_runtime_get(struct amd_mp2_dev *mp2_dev)
|
||||||
|
{
|
||||||
|
pm_runtime_get_sync(&mp2_dev->pci_dev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void amd_mp2_pm_runtime_put(struct amd_mp2_dev *mp2_dev)
|
||||||
|
{
|
||||||
|
pm_runtime_mark_last_busy(&mp2_dev->pci_dev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&mp2_dev->pci_dev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,376 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Weinmann Medical GmbH
|
||||||
|
* Author: Nikolaus Voss <n.voss@weinmann.de>
|
||||||
|
*
|
||||||
|
* Evolved from original work by:
|
||||||
|
* Copyright (C) 2004 Rick Bronson
|
||||||
|
* Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
|
||||||
|
*
|
||||||
|
* Borrowed heavily from original work by:
|
||||||
|
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
|
#include "i2c-at91.h"
|
||||||
|
|
||||||
|
unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
|
||||||
|
{
|
||||||
|
return readl_relaxed(dev->base + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
|
||||||
|
{
|
||||||
|
writel_relaxed(val, dev->base + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
|
||||||
|
{
|
||||||
|
at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void at91_twi_irq_save(struct at91_twi_dev *dev)
|
||||||
|
{
|
||||||
|
dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
|
||||||
|
at91_disable_twi_interrupts(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void at91_twi_irq_restore(struct at91_twi_dev *dev)
|
||||||
|
{
|
||||||
|
at91_twi_write(dev, AT91_TWI_IER, dev->imr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void at91_init_twi_bus(struct at91_twi_dev *dev)
|
||||||
|
{
|
||||||
|
at91_disable_twi_interrupts(dev);
|
||||||
|
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
|
||||||
|
if (dev->slave_detected)
|
||||||
|
at91_init_twi_bus_slave(dev);
|
||||||
|
else
|
||||||
|
at91_init_twi_bus_master(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct at91_twi_pdata at91rm9200_config = {
|
||||||
|
.clk_max_div = 5,
|
||||||
|
.clk_offset = 3,
|
||||||
|
.has_unre_flag = true,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct at91_twi_pdata at91sam9261_config = {
|
||||||
|
.clk_max_div = 5,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = false,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct at91_twi_pdata at91sam9260_config = {
|
||||||
|
.clk_max_div = 7,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = false,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct at91_twi_pdata at91sam9g20_config = {
|
||||||
|
.clk_max_div = 7,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = false,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct at91_twi_pdata at91sam9g10_config = {
|
||||||
|
.clk_max_div = 7,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = false,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct platform_device_id at91_twi_devtypes[] = {
|
||||||
|
{
|
||||||
|
.name = "i2c-at91rm9200",
|
||||||
|
.driver_data = (unsigned long) &at91rm9200_config,
|
||||||
|
}, {
|
||||||
|
.name = "i2c-at91sam9261",
|
||||||
|
.driver_data = (unsigned long) &at91sam9261_config,
|
||||||
|
}, {
|
||||||
|
.name = "i2c-at91sam9260",
|
||||||
|
.driver_data = (unsigned long) &at91sam9260_config,
|
||||||
|
}, {
|
||||||
|
.name = "i2c-at91sam9g20",
|
||||||
|
.driver_data = (unsigned long) &at91sam9g20_config,
|
||||||
|
}, {
|
||||||
|
.name = "i2c-at91sam9g10",
|
||||||
|
.driver_data = (unsigned long) &at91sam9g10_config,
|
||||||
|
}, {
|
||||||
|
/* sentinel */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(CONFIG_OF)
|
||||||
|
static struct at91_twi_pdata at91sam9x5_config = {
|
||||||
|
.clk_max_div = 7,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = false,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct at91_twi_pdata sama5d4_config = {
|
||||||
|
.clk_max_div = 7,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = false,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct at91_twi_pdata sama5d2_config = {
|
||||||
|
.clk_max_div = 7,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = true,
|
||||||
|
.has_alt_cmd = true,
|
||||||
|
.has_hold_field = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id atmel_twi_dt_ids[] = {
|
||||||
|
{
|
||||||
|
.compatible = "atmel,at91rm9200-i2c",
|
||||||
|
.data = &at91rm9200_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,at91sam9260-i2c",
|
||||||
|
.data = &at91sam9260_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,at91sam9261-i2c",
|
||||||
|
.data = &at91sam9261_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,at91sam9g20-i2c",
|
||||||
|
.data = &at91sam9g20_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,at91sam9g10-i2c",
|
||||||
|
.data = &at91sam9g10_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,at91sam9x5-i2c",
|
||||||
|
.data = &at91sam9x5_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,sama5d4-i2c",
|
||||||
|
.data = &sama5d4_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,sama5d2-i2c",
|
||||||
|
.data = &sama5d2_config,
|
||||||
|
}, {
|
||||||
|
/* sentinel */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct at91_twi_pdata *at91_twi_get_driver_data(
|
||||||
|
struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
if (pdev->dev.of_node) {
|
||||||
|
const struct of_device_id *match;
|
||||||
|
match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
|
||||||
|
if (!match)
|
||||||
|
return NULL;
|
||||||
|
return (struct at91_twi_pdata *)match->data;
|
||||||
|
}
|
||||||
|
return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_twi_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *dev;
|
||||||
|
struct resource *mem;
|
||||||
|
int rc;
|
||||||
|
u32 phy_addr;
|
||||||
|
|
||||||
|
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
||||||
|
if (!dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dev->dev = &pdev->dev;
|
||||||
|
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!mem)
|
||||||
|
return -ENODEV;
|
||||||
|
phy_addr = mem->start;
|
||||||
|
|
||||||
|
dev->pdata = at91_twi_get_driver_data(pdev);
|
||||||
|
if (!dev->pdata)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
dev->base = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
|
if (IS_ERR(dev->base))
|
||||||
|
return PTR_ERR(dev->base);
|
||||||
|
|
||||||
|
dev->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (dev->irq < 0)
|
||||||
|
return dev->irq;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, dev);
|
||||||
|
|
||||||
|
dev->clk = devm_clk_get(dev->dev, NULL);
|
||||||
|
if (IS_ERR(dev->clk)) {
|
||||||
|
dev_err(dev->dev, "no clock defined\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
clk_prepare_enable(dev->clk);
|
||||||
|
|
||||||
|
snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
|
||||||
|
i2c_set_adapdata(&dev->adapter, dev);
|
||||||
|
dev->adapter.owner = THIS_MODULE;
|
||||||
|
dev->adapter.class = I2C_CLASS_DEPRECATED;
|
||||||
|
dev->adapter.dev.parent = dev->dev;
|
||||||
|
dev->adapter.nr = pdev->id;
|
||||||
|
dev->adapter.timeout = AT91_I2C_TIMEOUT;
|
||||||
|
dev->adapter.dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
dev->slave_detected = i2c_detect_slave_mode(&pdev->dev);
|
||||||
|
|
||||||
|
if (dev->slave_detected)
|
||||||
|
rc = at91_twi_probe_slave(pdev, phy_addr, dev);
|
||||||
|
else
|
||||||
|
rc = at91_twi_probe_master(pdev, phy_addr, dev);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
at91_init_twi_bus(dev);
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
|
||||||
|
pm_runtime_use_autosuspend(dev->dev);
|
||||||
|
pm_runtime_set_active(dev->dev);
|
||||||
|
pm_runtime_enable(dev->dev);
|
||||||
|
|
||||||
|
rc = i2c_add_numbered_adapter(&dev->adapter);
|
||||||
|
if (rc) {
|
||||||
|
clk_disable_unprepare(dev->clk);
|
||||||
|
|
||||||
|
pm_runtime_disable(dev->dev);
|
||||||
|
pm_runtime_set_suspended(dev->dev);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
|
||||||
|
at91_twi_read(dev, AT91_TWI_VER));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_twi_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
i2c_del_adapter(&dev->adapter);
|
||||||
|
clk_disable_unprepare(dev->clk);
|
||||||
|
|
||||||
|
pm_runtime_disable(dev->dev);
|
||||||
|
pm_runtime_set_suspended(dev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
|
static int at91_twi_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
clk_disable_unprepare(twi_dev->clk);
|
||||||
|
|
||||||
|
pinctrl_pm_select_sleep_state(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_twi_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
|
||||||
|
return clk_prepare_enable(twi_dev->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_twi_suspend_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
if (!pm_runtime_status_suspended(dev))
|
||||||
|
at91_twi_runtime_suspend(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_twi_resume_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!pm_runtime_status_suspended(dev)) {
|
||||||
|
ret = at91_twi_runtime_resume(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_request_autosuspend(dev);
|
||||||
|
|
||||||
|
at91_init_twi_bus(twi_dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops at91_twi_pm = {
|
||||||
|
.suspend_noirq = at91_twi_suspend_noirq,
|
||||||
|
.resume_noirq = at91_twi_resume_noirq,
|
||||||
|
.runtime_suspend = at91_twi_runtime_suspend,
|
||||||
|
.runtime_resume = at91_twi_runtime_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define at91_twi_pm_ops (&at91_twi_pm)
|
||||||
|
#else
|
||||||
|
#define at91_twi_pm_ops NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct platform_driver at91_twi_driver = {
|
||||||
|
.probe = at91_twi_probe,
|
||||||
|
.remove = at91_twi_remove,
|
||||||
|
.id_table = at91_twi_devtypes,
|
||||||
|
.driver = {
|
||||||
|
.name = "at91_i2c",
|
||||||
|
.of_match_table = of_match_ptr(atmel_twi_dt_ids),
|
||||||
|
.pm = at91_twi_pm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init at91_twi_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&at91_twi_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit at91_twi_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&at91_twi_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
subsys_initcall(at91_twi_init);
|
||||||
|
module_exit(at91_twi_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
|
||||||
|
MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:at91_i2c");
|
|
@ -1,3 +1,4 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
|
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
|
||||||
*
|
*
|
||||||
|
@ -10,11 +11,6 @@
|
||||||
*
|
*
|
||||||
* Borrowed heavily from original work by:
|
* Borrowed heavily from original work by:
|
||||||
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
|
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
|
||||||
*
|
|
||||||
* 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/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -25,159 +21,16 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/platform_data/dma-atmel.h>
|
#include <linux/platform_data/dma-atmel.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
|
||||||
|
|
||||||
#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */
|
#include "i2c-at91.h"
|
||||||
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
|
|
||||||
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
|
|
||||||
#define AUTOSUSPEND_TIMEOUT 2000
|
|
||||||
#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
|
|
||||||
|
|
||||||
/* AT91 TWI register definitions */
|
void at91_init_twi_bus_master(struct at91_twi_dev *dev)
|
||||||
#define AT91_TWI_CR 0x0000 /* Control Register */
|
|
||||||
#define AT91_TWI_START BIT(0) /* Send a Start Condition */
|
|
||||||
#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */
|
|
||||||
#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */
|
|
||||||
#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */
|
|
||||||
#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */
|
|
||||||
#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */
|
|
||||||
#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */
|
|
||||||
#define AT91_TWI_SWRST BIT(7) /* Software Reset */
|
|
||||||
#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */
|
|
||||||
#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */
|
|
||||||
#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */
|
|
||||||
#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */
|
|
||||||
#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */
|
|
||||||
#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */
|
|
||||||
#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */
|
|
||||||
|
|
||||||
#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
|
|
||||||
#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
|
|
||||||
#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
|
|
||||||
|
|
||||||
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
|
|
||||||
|
|
||||||
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
|
|
||||||
#define AT91_TWI_CWGR_HOLD_MAX 0x1f
|
|
||||||
#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
|
|
||||||
|
|
||||||
#define AT91_TWI_SR 0x0020 /* Status Register */
|
|
||||||
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
|
|
||||||
#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
|
|
||||||
#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
|
|
||||||
#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
|
|
||||||
#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
|
|
||||||
#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
|
|
||||||
#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
|
|
||||||
|
|
||||||
#define AT91_TWI_INT_MASK \
|
|
||||||
(AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK)
|
|
||||||
|
|
||||||
#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
|
|
||||||
#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
|
|
||||||
#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */
|
|
||||||
#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
|
|
||||||
#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
|
|
||||||
|
|
||||||
#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */
|
|
||||||
#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff)
|
|
||||||
#define AT91_TWI_ACR_DIR BIT(8)
|
|
||||||
|
|
||||||
#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */
|
|
||||||
#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0)
|
|
||||||
#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0)
|
|
||||||
#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4)
|
|
||||||
#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4)
|
|
||||||
#define AT91_TWI_ONE_DATA 0x0
|
|
||||||
#define AT91_TWI_TWO_DATA 0x1
|
|
||||||
#define AT91_TWI_FOUR_DATA 0x2
|
|
||||||
|
|
||||||
#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */
|
|
||||||
|
|
||||||
#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */
|
|
||||||
#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */
|
|
||||||
#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */
|
|
||||||
#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */
|
|
||||||
|
|
||||||
#define AT91_TWI_VER 0x00fc /* Version Register */
|
|
||||||
|
|
||||||
struct at91_twi_pdata {
|
|
||||||
unsigned clk_max_div;
|
|
||||||
unsigned clk_offset;
|
|
||||||
bool has_unre_flag;
|
|
||||||
bool has_alt_cmd;
|
|
||||||
bool has_hold_field;
|
|
||||||
struct at_dma_slave dma_slave;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct at91_twi_dma {
|
|
||||||
struct dma_chan *chan_rx;
|
|
||||||
struct dma_chan *chan_tx;
|
|
||||||
struct scatterlist sg[2];
|
|
||||||
struct dma_async_tx_descriptor *data_desc;
|
|
||||||
enum dma_data_direction direction;
|
|
||||||
bool buf_mapped;
|
|
||||||
bool xfer_in_progress;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct at91_twi_dev {
|
|
||||||
struct device *dev;
|
|
||||||
void __iomem *base;
|
|
||||||
struct completion cmd_complete;
|
|
||||||
struct clk *clk;
|
|
||||||
u8 *buf;
|
|
||||||
size_t buf_len;
|
|
||||||
struct i2c_msg *msg;
|
|
||||||
int irq;
|
|
||||||
unsigned imr;
|
|
||||||
unsigned transfer_status;
|
|
||||||
struct i2c_adapter adapter;
|
|
||||||
unsigned twi_cwgr_reg;
|
|
||||||
struct at91_twi_pdata *pdata;
|
|
||||||
bool use_dma;
|
|
||||||
bool use_alt_cmd;
|
|
||||||
bool recv_len_abort;
|
|
||||||
u32 fifo_size;
|
|
||||||
struct at91_twi_dma dma;
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
|
|
||||||
{
|
{
|
||||||
return readl_relaxed(dev->base + reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
|
|
||||||
{
|
|
||||||
writel_relaxed(val, dev->base + reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
|
|
||||||
{
|
|
||||||
at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void at91_twi_irq_save(struct at91_twi_dev *dev)
|
|
||||||
{
|
|
||||||
dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
|
|
||||||
at91_disable_twi_interrupts(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void at91_twi_irq_restore(struct at91_twi_dev *dev)
|
|
||||||
{
|
|
||||||
at91_twi_write(dev, AT91_TWI_IER, dev->imr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void at91_init_twi_bus(struct at91_twi_dev *dev)
|
|
||||||
{
|
|
||||||
at91_disable_twi_interrupts(dev);
|
|
||||||
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
|
|
||||||
/* FIFO should be enabled immediately after the software reset */
|
/* FIFO should be enabled immediately after the software reset */
|
||||||
if (dev->fifo_size)
|
if (dev->fifo_size)
|
||||||
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN);
|
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN);
|
||||||
|
@ -190,16 +43,18 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
|
||||||
* Calculate symmetric clock as stated in datasheet:
|
* Calculate symmetric clock as stated in datasheet:
|
||||||
* twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
|
* twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
|
||||||
*/
|
*/
|
||||||
static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
|
static void at91_calc_twi_clock(struct at91_twi_dev *dev)
|
||||||
{
|
{
|
||||||
int ckdiv, cdiv, div, hold = 0;
|
int ckdiv, cdiv, div, hold = 0;
|
||||||
struct at91_twi_pdata *pdata = dev->pdata;
|
struct at91_twi_pdata *pdata = dev->pdata;
|
||||||
int offset = pdata->clk_offset;
|
int offset = pdata->clk_offset;
|
||||||
int max_ckdiv = pdata->clk_max_div;
|
int max_ckdiv = pdata->clk_max_div;
|
||||||
u32 twd_hold_time_ns = 0;
|
struct i2c_timings timings, *t = &timings;
|
||||||
|
|
||||||
|
i2c_parse_fw_timings(dev->dev, t, true);
|
||||||
|
|
||||||
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
|
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
|
||||||
2 * twi_clk) - offset);
|
2 * t->bus_freq_hz) - offset);
|
||||||
ckdiv = fls(div >> 8);
|
ckdiv = fls(div >> 8);
|
||||||
cdiv = div >> ckdiv;
|
cdiv = div >> ckdiv;
|
||||||
|
|
||||||
|
@ -211,15 +66,12 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->has_hold_field) {
|
if (pdata->has_hold_field) {
|
||||||
of_property_read_u32(dev->dev->of_node, "i2c-sda-hold-time-ns",
|
|
||||||
&twd_hold_time_ns);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hold time = HOLD + 3 x T_peripheral_clock
|
* hold time = HOLD + 3 x T_peripheral_clock
|
||||||
* Use clk rate in kHz to prevent overflows when computing
|
* Use clk rate in kHz to prevent overflows when computing
|
||||||
* hold.
|
* hold.
|
||||||
*/
|
*/
|
||||||
hold = DIV_ROUND_UP(twd_hold_time_ns
|
hold = DIV_ROUND_UP(t->sda_hold_ns
|
||||||
* (clk_get_rate(dev->clk) / 1000), 1000000);
|
* (clk_get_rate(dev->clk) / 1000), 1000000);
|
||||||
hold -= 3;
|
hold -= 3;
|
||||||
if (hold < 0)
|
if (hold < 0)
|
||||||
|
@ -236,7 +88,7 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
|
||||||
| AT91_TWI_CWGR_HOLD(hold);
|
| AT91_TWI_CWGR_HOLD(hold);
|
||||||
|
|
||||||
dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
|
dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
|
||||||
cdiv, ckdiv, hold, twd_hold_time_ns);
|
cdiv, ckdiv, hold, t->sda_hold_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
|
static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
|
||||||
|
@ -833,124 +685,6 @@ static const struct i2c_algorithm at91_twi_algorithm = {
|
||||||
.functionality = at91_twi_func,
|
.functionality = at91_twi_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91rm9200_config = {
|
|
||||||
.clk_max_div = 5,
|
|
||||||
.clk_offset = 3,
|
|
||||||
.has_unre_flag = true,
|
|
||||||
.has_alt_cmd = false,
|
|
||||||
.has_hold_field = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9261_config = {
|
|
||||||
.clk_max_div = 5,
|
|
||||||
.clk_offset = 4,
|
|
||||||
.has_unre_flag = false,
|
|
||||||
.has_alt_cmd = false,
|
|
||||||
.has_hold_field = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9260_config = {
|
|
||||||
.clk_max_div = 7,
|
|
||||||
.clk_offset = 4,
|
|
||||||
.has_unre_flag = false,
|
|
||||||
.has_alt_cmd = false,
|
|
||||||
.has_hold_field = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9g20_config = {
|
|
||||||
.clk_max_div = 7,
|
|
||||||
.clk_offset = 4,
|
|
||||||
.has_unre_flag = false,
|
|
||||||
.has_alt_cmd = false,
|
|
||||||
.has_hold_field = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9g10_config = {
|
|
||||||
.clk_max_div = 7,
|
|
||||||
.clk_offset = 4,
|
|
||||||
.has_unre_flag = false,
|
|
||||||
.has_alt_cmd = false,
|
|
||||||
.has_hold_field = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct platform_device_id at91_twi_devtypes[] = {
|
|
||||||
{
|
|
||||||
.name = "i2c-at91rm9200",
|
|
||||||
.driver_data = (unsigned long) &at91rm9200_config,
|
|
||||||
}, {
|
|
||||||
.name = "i2c-at91sam9261",
|
|
||||||
.driver_data = (unsigned long) &at91sam9261_config,
|
|
||||||
}, {
|
|
||||||
.name = "i2c-at91sam9260",
|
|
||||||
.driver_data = (unsigned long) &at91sam9260_config,
|
|
||||||
}, {
|
|
||||||
.name = "i2c-at91sam9g20",
|
|
||||||
.driver_data = (unsigned long) &at91sam9g20_config,
|
|
||||||
}, {
|
|
||||||
.name = "i2c-at91sam9g10",
|
|
||||||
.driver_data = (unsigned long) &at91sam9g10_config,
|
|
||||||
}, {
|
|
||||||
/* sentinel */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(CONFIG_OF)
|
|
||||||
static struct at91_twi_pdata at91sam9x5_config = {
|
|
||||||
.clk_max_div = 7,
|
|
||||||
.clk_offset = 4,
|
|
||||||
.has_unre_flag = false,
|
|
||||||
.has_alt_cmd = false,
|
|
||||||
.has_hold_field = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct at91_twi_pdata sama5d4_config = {
|
|
||||||
.clk_max_div = 7,
|
|
||||||
.clk_offset = 4,
|
|
||||||
.has_unre_flag = false,
|
|
||||||
.has_alt_cmd = false,
|
|
||||||
.has_hold_field = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct at91_twi_pdata sama5d2_config = {
|
|
||||||
.clk_max_div = 7,
|
|
||||||
.clk_offset = 4,
|
|
||||||
.has_unre_flag = true,
|
|
||||||
.has_alt_cmd = true,
|
|
||||||
.has_hold_field = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct of_device_id atmel_twi_dt_ids[] = {
|
|
||||||
{
|
|
||||||
.compatible = "atmel,at91rm9200-i2c",
|
|
||||||
.data = &at91rm9200_config,
|
|
||||||
} , {
|
|
||||||
.compatible = "atmel,at91sam9260-i2c",
|
|
||||||
.data = &at91sam9260_config,
|
|
||||||
} , {
|
|
||||||
.compatible = "atmel,at91sam9261-i2c",
|
|
||||||
.data = &at91sam9261_config,
|
|
||||||
} , {
|
|
||||||
.compatible = "atmel,at91sam9g20-i2c",
|
|
||||||
.data = &at91sam9g20_config,
|
|
||||||
} , {
|
|
||||||
.compatible = "atmel,at91sam9g10-i2c",
|
|
||||||
.data = &at91sam9g10_config,
|
|
||||||
}, {
|
|
||||||
.compatible = "atmel,at91sam9x5-i2c",
|
|
||||||
.data = &at91sam9x5_config,
|
|
||||||
}, {
|
|
||||||
.compatible = "atmel,sama5d4-i2c",
|
|
||||||
.data = &sama5d4_config,
|
|
||||||
}, {
|
|
||||||
.compatible = "atmel,sama5d2-i2c",
|
|
||||||
.data = &sama5d2_config,
|
|
||||||
}, {
|
|
||||||
/* sentinel */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
|
static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -1033,74 +767,24 @@ error:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct at91_twi_pdata *at91_twi_get_driver_data(
|
int at91_twi_probe_master(struct platform_device *pdev,
|
||||||
struct platform_device *pdev)
|
u32 phy_addr, struct at91_twi_dev *dev)
|
||||||
{
|
{
|
||||||
if (pdev->dev.of_node) {
|
|
||||||
const struct of_device_id *match;
|
|
||||||
match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
|
|
||||||
if (!match)
|
|
||||||
return NULL;
|
|
||||||
return (struct at91_twi_pdata *)match->data;
|
|
||||||
}
|
|
||||||
return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int at91_twi_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct at91_twi_dev *dev;
|
|
||||||
struct resource *mem;
|
|
||||||
int rc;
|
int rc;
|
||||||
u32 phy_addr;
|
|
||||||
u32 bus_clk_rate;
|
|
||||||
|
|
||||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
|
||||||
if (!dev)
|
|
||||||
return -ENOMEM;
|
|
||||||
init_completion(&dev->cmd_complete);
|
init_completion(&dev->cmd_complete);
|
||||||
dev->dev = &pdev->dev;
|
|
||||||
|
|
||||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
if (!mem)
|
|
||||||
return -ENODEV;
|
|
||||||
phy_addr = mem->start;
|
|
||||||
|
|
||||||
dev->pdata = at91_twi_get_driver_data(pdev);
|
|
||||||
if (!dev->pdata)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
dev->base = devm_ioremap_resource(&pdev->dev, mem);
|
|
||||||
if (IS_ERR(dev->base))
|
|
||||||
return PTR_ERR(dev->base);
|
|
||||||
|
|
||||||
dev->irq = platform_get_irq(pdev, 0);
|
|
||||||
if (dev->irq < 0)
|
|
||||||
return dev->irq;
|
|
||||||
|
|
||||||
rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0,
|
rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0,
|
||||||
dev_name(dev->dev), dev);
|
dev_name(dev->dev), dev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
|
dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, dev);
|
|
||||||
|
|
||||||
dev->clk = devm_clk_get(dev->dev, NULL);
|
|
||||||
if (IS_ERR(dev->clk)) {
|
|
||||||
dev_err(dev->dev, "no clock defined\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
rc = clk_prepare_enable(dev->clk);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (dev->dev->of_node) {
|
if (dev->dev->of_node) {
|
||||||
rc = at91_twi_configure_dma(dev, phy_addr);
|
rc = at91_twi_configure_dma(dev, phy_addr);
|
||||||
if (rc == -EPROBE_DEFER) {
|
if (rc == -EPROBE_DEFER)
|
||||||
clk_disable_unprepare(dev->clk);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
|
if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
|
||||||
|
@ -1108,144 +792,10 @@ static int at91_twi_probe(struct platform_device *pdev)
|
||||||
dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size);
|
dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = of_property_read_u32(dev->dev->of_node, "clock-frequency",
|
at91_calc_twi_clock(dev);
|
||||||
&bus_clk_rate);
|
|
||||||
if (rc)
|
|
||||||
bus_clk_rate = DEFAULT_TWI_CLK_HZ;
|
|
||||||
|
|
||||||
at91_calc_twi_clock(dev, bus_clk_rate);
|
|
||||||
at91_init_twi_bus(dev);
|
|
||||||
|
|
||||||
snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
|
|
||||||
i2c_set_adapdata(&dev->adapter, dev);
|
|
||||||
dev->adapter.owner = THIS_MODULE;
|
|
||||||
dev->adapter.class = I2C_CLASS_DEPRECATED;
|
|
||||||
dev->adapter.algo = &at91_twi_algorithm;
|
dev->adapter.algo = &at91_twi_algorithm;
|
||||||
dev->adapter.quirks = &at91_twi_quirks;
|
dev->adapter.quirks = &at91_twi_quirks;
|
||||||
dev->adapter.dev.parent = dev->dev;
|
|
||||||
dev->adapter.nr = pdev->id;
|
|
||||||
dev->adapter.timeout = AT91_I2C_TIMEOUT;
|
|
||||||
dev->adapter.dev.of_node = pdev->dev.of_node;
|
|
||||||
|
|
||||||
pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
|
|
||||||
pm_runtime_use_autosuspend(dev->dev);
|
|
||||||
pm_runtime_set_active(dev->dev);
|
|
||||||
pm_runtime_enable(dev->dev);
|
|
||||||
|
|
||||||
rc = i2c_add_numbered_adapter(&dev->adapter);
|
|
||||||
if (rc) {
|
|
||||||
clk_disable_unprepare(dev->clk);
|
|
||||||
|
|
||||||
pm_runtime_disable(dev->dev);
|
|
||||||
pm_runtime_set_suspended(dev->dev);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
|
|
||||||
at91_twi_read(dev, AT91_TWI_VER));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int at91_twi_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct at91_twi_dev *dev = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
i2c_del_adapter(&dev->adapter);
|
|
||||||
clk_disable_unprepare(dev->clk);
|
|
||||||
|
|
||||||
pm_runtime_disable(dev->dev);
|
|
||||||
pm_runtime_set_suspended(dev->dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
|
|
||||||
static int at91_twi_runtime_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
clk_disable_unprepare(twi_dev->clk);
|
|
||||||
|
|
||||||
pinctrl_pm_select_sleep_state(dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int at91_twi_runtime_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
pinctrl_pm_select_default_state(dev);
|
|
||||||
|
|
||||||
return clk_prepare_enable(twi_dev->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int at91_twi_suspend_noirq(struct device *dev)
|
|
||||||
{
|
|
||||||
if (!pm_runtime_status_suspended(dev))
|
|
||||||
at91_twi_runtime_suspend(dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int at91_twi_resume_noirq(struct device *dev)
|
|
||||||
{
|
|
||||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!pm_runtime_status_suspended(dev)) {
|
|
||||||
ret = at91_twi_runtime_resume(dev);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
pm_runtime_mark_last_busy(dev);
|
|
||||||
pm_request_autosuspend(dev);
|
|
||||||
|
|
||||||
at91_init_twi_bus(twi_dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct dev_pm_ops at91_twi_pm = {
|
|
||||||
.suspend_noirq = at91_twi_suspend_noirq,
|
|
||||||
.resume_noirq = at91_twi_resume_noirq,
|
|
||||||
.runtime_suspend = at91_twi_runtime_suspend,
|
|
||||||
.runtime_resume = at91_twi_runtime_resume,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define at91_twi_pm_ops (&at91_twi_pm)
|
|
||||||
#else
|
|
||||||
#define at91_twi_pm_ops NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct platform_driver at91_twi_driver = {
|
|
||||||
.probe = at91_twi_probe,
|
|
||||||
.remove = at91_twi_remove,
|
|
||||||
.id_table = at91_twi_devtypes,
|
|
||||||
.driver = {
|
|
||||||
.name = "at91_i2c",
|
|
||||||
.of_match_table = of_match_ptr(atmel_twi_dt_ids),
|
|
||||||
.pm = at91_twi_pm_ops,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init at91_twi_init(void)
|
|
||||||
{
|
|
||||||
return platform_driver_register(&at91_twi_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit at91_twi_exit(void)
|
|
||||||
{
|
|
||||||
platform_driver_unregister(&at91_twi_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
subsys_initcall(at91_twi_init);
|
|
||||||
module_exit(at91_twi_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
|
|
||||||
MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_ALIAS("platform:at91_i2c");
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* i2c slave support for Atmel's AT91 Two-Wire Interface (TWI)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Juergen Fitschen <me@jue.yt>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
|
#include "i2c-at91.h"
|
||||||
|
|
||||||
|
static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *dev = dev_id;
|
||||||
|
const unsigned status = at91_twi_read(dev, AT91_TWI_SR);
|
||||||
|
const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR);
|
||||||
|
u8 value;
|
||||||
|
|
||||||
|
if (!irqstatus)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
/* slave address has been detected on I2C bus */
|
||||||
|
if (irqstatus & AT91_TWI_SVACC) {
|
||||||
|
if (status & AT91_TWI_SVREAD) {
|
||||||
|
i2c_slave_event(dev->slave,
|
||||||
|
I2C_SLAVE_READ_REQUESTED, &value);
|
||||||
|
writeb_relaxed(value, dev->base + AT91_TWI_THR);
|
||||||
|
at91_twi_write(dev, AT91_TWI_IER,
|
||||||
|
AT91_TWI_TXRDY | AT91_TWI_EOSACC);
|
||||||
|
} else {
|
||||||
|
i2c_slave_event(dev->slave,
|
||||||
|
I2C_SLAVE_WRITE_REQUESTED, &value);
|
||||||
|
at91_twi_write(dev, AT91_TWI_IER,
|
||||||
|
AT91_TWI_RXRDY | AT91_TWI_EOSACC);
|
||||||
|
}
|
||||||
|
at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* byte transmitted to remote master */
|
||||||
|
if (irqstatus & AT91_TWI_TXRDY) {
|
||||||
|
i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value);
|
||||||
|
writeb_relaxed(value, dev->base + AT91_TWI_THR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* byte received from remote master */
|
||||||
|
if (irqstatus & AT91_TWI_RXRDY) {
|
||||||
|
value = readb_relaxed(dev->base + AT91_TWI_RHR);
|
||||||
|
i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* master sent stop */
|
||||||
|
if (irqstatus & AT91_TWI_EOSACC) {
|
||||||
|
at91_twi_write(dev, AT91_TWI_IDR,
|
||||||
|
AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC);
|
||||||
|
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
|
||||||
|
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_reg_slave(struct i2c_client *slave)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
|
||||||
|
|
||||||
|
if (dev->slave)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (slave->flags & I2C_CLIENT_TEN)
|
||||||
|
return -EAFNOSUPPORT;
|
||||||
|
|
||||||
|
/* Make sure twi_clk doesn't get turned off! */
|
||||||
|
pm_runtime_get_sync(dev->dev);
|
||||||
|
|
||||||
|
dev->slave = slave;
|
||||||
|
dev->smr = AT91_TWI_SMR_SADR(slave->addr);
|
||||||
|
|
||||||
|
at91_init_twi_bus(dev);
|
||||||
|
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
|
||||||
|
|
||||||
|
dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int at91_unreg_slave(struct i2c_client *slave)
|
||||||
|
{
|
||||||
|
struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
|
||||||
|
|
||||||
|
WARN_ON(!dev->slave);
|
||||||
|
|
||||||
|
dev_info(dev->dev, "leaving slave mode\n");
|
||||||
|
|
||||||
|
dev->slave = NULL;
|
||||||
|
dev->smr = 0;
|
||||||
|
|
||||||
|
at91_init_twi_bus(dev);
|
||||||
|
|
||||||
|
pm_runtime_put(dev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 at91_twi_func(struct i2c_adapter *adapter)
|
||||||
|
{
|
||||||
|
return I2C_FUNC_SLAVE | I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
|
||||||
|
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_algorithm at91_twi_algorithm_slave = {
|
||||||
|
.reg_slave = at91_reg_slave,
|
||||||
|
.unreg_slave = at91_unreg_slave,
|
||||||
|
.functionality = at91_twi_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
int at91_twi_probe_slave(struct platform_device *pdev,
|
||||||
|
u32 phy_addr, struct at91_twi_dev *dev)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave,
|
||||||
|
0, dev_name(dev->dev), dev);
|
||||||
|
if (rc) {
|
||||||
|
dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->adapter.algo = &at91_twi_algorithm_slave;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void at91_init_twi_bus_slave(struct at91_twi_dev *dev)
|
||||||
|
{
|
||||||
|
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS);
|
||||||
|
if (dev->slave_detected && dev->smr) {
|
||||||
|
at91_twi_write(dev, AT91_TWI_SMR, dev->smr);
|
||||||
|
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Weinmann Medical GmbH
|
||||||
|
* Author: Nikolaus Voss <n.voss@weinmann.de>
|
||||||
|
*
|
||||||
|
* Evolved from original work by:
|
||||||
|
* Copyright (C) 2004 Rick Bronson
|
||||||
|
* Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
|
||||||
|
*
|
||||||
|
* Borrowed heavily from original work by:
|
||||||
|
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/platform_data/dma-atmel.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
|
||||||
|
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
|
||||||
|
#define AUTOSUSPEND_TIMEOUT 2000
|
||||||
|
#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
|
||||||
|
|
||||||
|
/* AT91 TWI register definitions */
|
||||||
|
#define AT91_TWI_CR 0x0000 /* Control Register */
|
||||||
|
#define AT91_TWI_START BIT(0) /* Send a Start Condition */
|
||||||
|
#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */
|
||||||
|
#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */
|
||||||
|
#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */
|
||||||
|
#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */
|
||||||
|
#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */
|
||||||
|
#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */
|
||||||
|
#define AT91_TWI_SWRST BIT(7) /* Software Reset */
|
||||||
|
#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */
|
||||||
|
#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */
|
||||||
|
#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */
|
||||||
|
#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */
|
||||||
|
#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */
|
||||||
|
#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */
|
||||||
|
#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */
|
||||||
|
|
||||||
|
#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
|
||||||
|
#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
|
||||||
|
#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
|
||||||
|
|
||||||
|
#define AT91_TWI_SMR 0x0008 /* Slave Mode Register */
|
||||||
|
#define AT91_TWI_SMR_SADR_MAX 0x007f
|
||||||
|
#define AT91_TWI_SMR_SADR(x) (((x) & AT91_TWI_SMR_SADR_MAX) << 16)
|
||||||
|
|
||||||
|
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
|
||||||
|
|
||||||
|
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
|
||||||
|
#define AT91_TWI_CWGR_HOLD_MAX 0x1f
|
||||||
|
#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
|
||||||
|
|
||||||
|
#define AT91_TWI_SR 0x0020 /* Status Register */
|
||||||
|
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
|
||||||
|
#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
|
||||||
|
#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
|
||||||
|
#define AT91_TWI_SVREAD BIT(3) /* Slave Read */
|
||||||
|
#define AT91_TWI_SVACC BIT(4) /* Slave Access */
|
||||||
|
#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
|
||||||
|
#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
|
||||||
|
#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
|
||||||
|
#define AT91_TWI_EOSACC BIT(11) /* End Of Slave Access */
|
||||||
|
#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
|
||||||
|
|
||||||
|
#define AT91_TWI_INT_MASK \
|
||||||
|
(AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \
|
||||||
|
| AT91_TWI_SVACC | AT91_TWI_EOSACC)
|
||||||
|
|
||||||
|
#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
|
||||||
|
#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
|
||||||
|
#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */
|
||||||
|
#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
|
||||||
|
#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
|
||||||
|
|
||||||
|
#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */
|
||||||
|
#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff)
|
||||||
|
#define AT91_TWI_ACR_DIR BIT(8)
|
||||||
|
|
||||||
|
#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */
|
||||||
|
#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0)
|
||||||
|
#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0)
|
||||||
|
#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4)
|
||||||
|
#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4)
|
||||||
|
#define AT91_TWI_ONE_DATA 0x0
|
||||||
|
#define AT91_TWI_TWO_DATA 0x1
|
||||||
|
#define AT91_TWI_FOUR_DATA 0x2
|
||||||
|
|
||||||
|
#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */
|
||||||
|
|
||||||
|
#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */
|
||||||
|
#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */
|
||||||
|
#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */
|
||||||
|
#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */
|
||||||
|
|
||||||
|
#define AT91_TWI_VER 0x00fc /* Version Register */
|
||||||
|
|
||||||
|
struct at91_twi_pdata {
|
||||||
|
unsigned clk_max_div;
|
||||||
|
unsigned clk_offset;
|
||||||
|
bool has_unre_flag;
|
||||||
|
bool has_alt_cmd;
|
||||||
|
bool has_hold_field;
|
||||||
|
struct at_dma_slave dma_slave;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct at91_twi_dma {
|
||||||
|
struct dma_chan *chan_rx;
|
||||||
|
struct dma_chan *chan_tx;
|
||||||
|
struct scatterlist sg[2];
|
||||||
|
struct dma_async_tx_descriptor *data_desc;
|
||||||
|
enum dma_data_direction direction;
|
||||||
|
bool buf_mapped;
|
||||||
|
bool xfer_in_progress;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct at91_twi_dev {
|
||||||
|
struct device *dev;
|
||||||
|
void __iomem *base;
|
||||||
|
struct completion cmd_complete;
|
||||||
|
struct clk *clk;
|
||||||
|
u8 *buf;
|
||||||
|
size_t buf_len;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
int irq;
|
||||||
|
unsigned imr;
|
||||||
|
unsigned transfer_status;
|
||||||
|
struct i2c_adapter adapter;
|
||||||
|
unsigned twi_cwgr_reg;
|
||||||
|
struct at91_twi_pdata *pdata;
|
||||||
|
bool use_dma;
|
||||||
|
bool use_alt_cmd;
|
||||||
|
bool recv_len_abort;
|
||||||
|
u32 fifo_size;
|
||||||
|
struct at91_twi_dma dma;
|
||||||
|
bool slave_detected;
|
||||||
|
#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
|
||||||
|
unsigned smr;
|
||||||
|
struct i2c_client *slave;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg);
|
||||||
|
void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val);
|
||||||
|
void at91_disable_twi_interrupts(struct at91_twi_dev *dev);
|
||||||
|
void at91_twi_irq_save(struct at91_twi_dev *dev);
|
||||||
|
void at91_twi_irq_restore(struct at91_twi_dev *dev);
|
||||||
|
void at91_init_twi_bus(struct at91_twi_dev *dev);
|
||||||
|
|
||||||
|
void at91_init_twi_bus_master(struct at91_twi_dev *dev);
|
||||||
|
int at91_twi_probe_master(struct platform_device *pdev, u32 phy_addr,
|
||||||
|
struct at91_twi_dev *dev);
|
||||||
|
|
||||||
|
#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
|
||||||
|
void at91_init_twi_bus_slave(struct at91_twi_dev *dev);
|
||||||
|
int at91_twi_probe_slave(struct platform_device *pdev, u32 phy_addr,
|
||||||
|
struct at91_twi_dev *dev);
|
||||||
|
|
||||||
|
#else
|
||||||
|
static inline void at91_init_twi_bus_slave(struct at91_twi_dev *dev) {}
|
||||||
|
static inline int at91_twi_probe_slave(struct platform_device *pdev,
|
||||||
|
u32 phy_addr, struct at91_twi_dev *dev)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -99,6 +99,7 @@
|
||||||
* @adapter: core i2c abstraction
|
* @adapter: core i2c abstraction
|
||||||
* @i2c_clk: clock reference for i2c input clock
|
* @i2c_clk: clock reference for i2c input clock
|
||||||
* @bus_clk_rate: current i2c bus clock rate
|
* @bus_clk_rate: current i2c bus clock rate
|
||||||
|
* @last: a flag indicating is this is last message in transfer
|
||||||
*/
|
*/
|
||||||
struct axxia_i2c_dev {
|
struct axxia_i2c_dev {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
@ -112,6 +113,7 @@ struct axxia_i2c_dev {
|
||||||
struct i2c_adapter adapter;
|
struct i2c_adapter adapter;
|
||||||
struct clk *i2c_clk;
|
struct clk *i2c_clk;
|
||||||
u32 bus_clk_rate;
|
u32 bus_clk_rate;
|
||||||
|
bool last;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask)
|
static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask)
|
||||||
|
@ -324,15 +326,14 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
|
||||||
/* Stop completed */
|
/* Stop completed */
|
||||||
i2c_int_disable(idev, ~MST_STATUS_TSS);
|
i2c_int_disable(idev, ~MST_STATUS_TSS);
|
||||||
complete(&idev->msg_complete);
|
complete(&idev->msg_complete);
|
||||||
} else if (status & MST_STATUS_SNS) {
|
} else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) {
|
||||||
/* Transfer done */
|
/* Transfer done */
|
||||||
i2c_int_disable(idev, ~MST_STATUS_TSS);
|
int mask = idev->last ? ~0 : ~MST_STATUS_TSS;
|
||||||
|
|
||||||
|
i2c_int_disable(idev, mask);
|
||||||
if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len)
|
if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len)
|
||||||
axxia_i2c_empty_rx_fifo(idev);
|
axxia_i2c_empty_rx_fifo(idev);
|
||||||
complete(&idev->msg_complete);
|
complete(&idev->msg_complete);
|
||||||
} else if (status & MST_STATUS_SS) {
|
|
||||||
/* Auto/Sequence transfer done */
|
|
||||||
complete(&idev->msg_complete);
|
|
||||||
} else if (status & MST_STATUS_TSS) {
|
} else if (status & MST_STATUS_TSS) {
|
||||||
/* Transfer timeout */
|
/* Transfer timeout */
|
||||||
idev->msg_err = -ETIMEDOUT;
|
idev->msg_err = -ETIMEDOUT;
|
||||||
|
@ -405,6 +406,7 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
|
||||||
idev->msg_r = &msgs[1];
|
idev->msg_r = &msgs[1];
|
||||||
idev->msg_xfrd = 0;
|
idev->msg_xfrd = 0;
|
||||||
idev->msg_xfrd_r = 0;
|
idev->msg_xfrd_r = 0;
|
||||||
|
idev->last = true;
|
||||||
axxia_i2c_fill_tx_fifo(idev);
|
axxia_i2c_fill_tx_fifo(idev);
|
||||||
|
|
||||||
writel(CMD_SEQUENCE, idev->base + MST_COMMAND);
|
writel(CMD_SEQUENCE, idev->base + MST_COMMAND);
|
||||||
|
@ -415,10 +417,6 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
|
||||||
time_left = wait_for_completion_timeout(&idev->msg_complete,
|
time_left = wait_for_completion_timeout(&idev->msg_complete,
|
||||||
I2C_XFER_TIMEOUT);
|
I2C_XFER_TIMEOUT);
|
||||||
|
|
||||||
i2c_int_disable(idev, int_mask);
|
|
||||||
|
|
||||||
axxia_i2c_empty_rx_fifo(idev);
|
|
||||||
|
|
||||||
if (idev->msg_err == -ENXIO) {
|
if (idev->msg_err == -ENXIO) {
|
||||||
if (axxia_i2c_handle_seq_nak(idev))
|
if (axxia_i2c_handle_seq_nak(idev))
|
||||||
axxia_i2c_init(idev);
|
axxia_i2c_init(idev);
|
||||||
|
@ -438,9 +436,10 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
|
||||||
return idev->msg_err;
|
return idev->msg_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
|
static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg,
|
||||||
|
bool last)
|
||||||
{
|
{
|
||||||
u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS;
|
u32 int_mask = MST_STATUS_ERR;
|
||||||
u32 rx_xfer, tx_xfer;
|
u32 rx_xfer, tx_xfer;
|
||||||
unsigned long time_left;
|
unsigned long time_left;
|
||||||
unsigned int wt_value;
|
unsigned int wt_value;
|
||||||
|
@ -449,6 +448,7 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
|
||||||
idev->msg_r = msg;
|
idev->msg_r = msg;
|
||||||
idev->msg_xfrd = 0;
|
idev->msg_xfrd = 0;
|
||||||
idev->msg_xfrd_r = 0;
|
idev->msg_xfrd_r = 0;
|
||||||
|
idev->last = last;
|
||||||
reinit_completion(&idev->msg_complete);
|
reinit_completion(&idev->msg_complete);
|
||||||
|
|
||||||
axxia_i2c_set_addr(idev, msg);
|
axxia_i2c_set_addr(idev, msg);
|
||||||
|
@ -478,8 +478,13 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
|
||||||
if (idev->msg_err)
|
if (idev->msg_err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Start manual mode */
|
if (!last) {
|
||||||
writel(CMD_MANUAL, idev->base + MST_COMMAND);
|
writel(CMD_MANUAL, idev->base + MST_COMMAND);
|
||||||
|
int_mask |= MST_STATUS_SNS;
|
||||||
|
} else {
|
||||||
|
writel(CMD_AUTO, idev->base + MST_COMMAND);
|
||||||
|
int_mask |= MST_STATUS_SS;
|
||||||
|
}
|
||||||
|
|
||||||
writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL);
|
writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL);
|
||||||
|
|
||||||
|
@ -507,28 +512,6 @@ out:
|
||||||
return idev->msg_err;
|
return idev->msg_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
|
|
||||||
{
|
|
||||||
u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC | MST_STATUS_TSS;
|
|
||||||
unsigned long time_left;
|
|
||||||
|
|
||||||
reinit_completion(&idev->msg_complete);
|
|
||||||
|
|
||||||
/* Issue stop */
|
|
||||||
writel(0xb, idev->base + MST_COMMAND);
|
|
||||||
i2c_int_enable(idev, int_mask);
|
|
||||||
time_left = wait_for_completion_timeout(&idev->msg_complete,
|
|
||||||
I2C_STOP_TIMEOUT);
|
|
||||||
i2c_int_disable(idev, int_mask);
|
|
||||||
if (time_left == 0)
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
|
|
||||||
if (readl(idev->base + MST_COMMAND) & CMD_BUSY)
|
|
||||||
dev_warn(idev->dev, "busy after stop\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function checks if the msgs[] array contains messages compatible with
|
/* This function checks if the msgs[] array contains messages compatible with
|
||||||
* Sequence mode of operation. This mode assumes there will be exactly one
|
* Sequence mode of operation. This mode assumes there will be exactly one
|
||||||
* write of non-zero length followed by exactly one read of non-zero length,
|
* write of non-zero length followed by exactly one read of non-zero length,
|
||||||
|
@ -558,9 +541,7 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
i2c_int_enable(idev, MST_STATUS_TSS);
|
i2c_int_enable(idev, MST_STATUS_TSS);
|
||||||
|
|
||||||
for (i = 0; ret == 0 && i < num; ++i)
|
for (i = 0; ret == 0 && i < num; ++i)
|
||||||
ret = axxia_i2c_xfer_msg(idev, &msgs[i]);
|
ret = axxia_i2c_xfer_msg(idev, &msgs[i], i == (num - 1));
|
||||||
|
|
||||||
axxia_i2c_stop(idev);
|
|
||||||
|
|
||||||
return ret ? : i;
|
return ret ? : i;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -165,7 +165,6 @@ static const struct bsc_clk_param bsc_clk[] = {
|
||||||
struct brcmstb_i2c_dev {
|
struct brcmstb_i2c_dev {
|
||||||
struct device *device;
|
struct device *device;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
void __iomem *irq_base;
|
|
||||||
int irq;
|
int irq;
|
||||||
struct bsc_regs *bsc_regmap;
|
struct bsc_regs *bsc_regmap;
|
||||||
struct i2c_adapter adapter;
|
struct i2c_adapter adapter;
|
||||||
|
|
|
@ -251,13 +251,27 @@ 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 ret;
|
||||||
|
|
||||||
if (IS_ERR(dev->clk))
|
if (IS_ERR(dev->clk))
|
||||||
return PTR_ERR(dev->clk);
|
return PTR_ERR(dev->clk);
|
||||||
|
|
||||||
if (prepare)
|
if (prepare) {
|
||||||
return clk_prepare_enable(dev->clk);
|
/* Optional interface clock */
|
||||||
|
ret = clk_prepare_enable(dev->pclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(dev->clk);
|
||||||
|
if (ret)
|
||||||
|
clk_disable_unprepare(dev->pclk);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
clk_disable_unprepare(dev->clk);
|
clk_disable_unprepare(dev->clk);
|
||||||
|
clk_disable_unprepare(dev->pclk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_dw_prepare_clk);
|
EXPORT_SYMBOL_GPL(i2c_dw_prepare_clk);
|
||||||
|
|
|
@ -177,6 +177,7 @@
|
||||||
* @base: IO registers pointer
|
* @base: IO registers pointer
|
||||||
* @cmd_complete: tx completion indicator
|
* @cmd_complete: tx completion indicator
|
||||||
* @clk: input reference clock
|
* @clk: input reference clock
|
||||||
|
* @pclk: clock required to access the registers
|
||||||
* @slave: represent an I2C slave device
|
* @slave: represent an I2C slave device
|
||||||
* @cmd_err: run time hadware error code
|
* @cmd_err: run time hadware error code
|
||||||
* @msgs: points to an array of messages currently being transferred
|
* @msgs: points to an array of messages currently being transferred
|
||||||
|
@ -227,6 +228,7 @@ struct dw_i2c_dev {
|
||||||
void __iomem *ext;
|
void __iomem *ext;
|
||||||
struct completion cmd_complete;
|
struct completion cmd_complete;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
struct clk *pclk;
|
||||||
struct reset_control *rst;
|
struct reset_control *rst;
|
||||||
struct i2c_client *slave;
|
struct i2c_client *slave;
|
||||||
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
|
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
|
||||||
|
|
|
@ -344,6 +344,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
else
|
else
|
||||||
i2c_dw_configure_master(dev);
|
i2c_dw_configure_master(dev);
|
||||||
|
|
||||||
|
/* Optional interface clock */
|
||||||
|
dev->pclk = devm_clk_get_optional(&pdev->dev, "pclk");
|
||||||
|
if (IS_ERR(dev->pclk))
|
||||||
|
return PTR_ERR(dev->pclk);
|
||||||
|
|
||||||
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)) {
|
||||||
u64 clk_khz;
|
u64 clk_khz;
|
||||||
|
|
|
@ -413,6 +413,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl))
|
if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl))
|
||||||
dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing");
|
dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing");
|
||||||
|
else
|
||||||
|
bit_data->can_do_atomic = true;
|
||||||
|
|
||||||
bit_data->setsda = i2c_gpio_setsda_val;
|
bit_data->setsda = i2c_gpio_setsda_val;
|
||||||
bit_data->setscl = i2c_gpio_setscl_val;
|
bit_data->setscl = i2c_gpio_setscl_val;
|
||||||
|
|
|
@ -639,8 +639,7 @@ static int lpi2c_imx_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
|
||||||
static int lpi2c_runtime_suspend(struct device *dev)
|
|
||||||
{
|
{
|
||||||
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
|
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
@ -650,7 +649,7 @@ static int lpi2c_runtime_suspend(struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lpi2c_runtime_resume(struct device *dev)
|
static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
|
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -671,10 +670,6 @@ static const struct dev_pm_ops lpi2c_pm_ops = {
|
||||||
SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend,
|
SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend,
|
||||||
lpi2c_runtime_resume, NULL)
|
lpi2c_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
#define IMX_LPI2C_PM (&lpi2c_pm_ops)
|
|
||||||
#else
|
|
||||||
#define IMX_LPI2C_PM NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct platform_driver lpi2c_imx_driver = {
|
static struct platform_driver lpi2c_imx_driver = {
|
||||||
.probe = lpi2c_imx_probe,
|
.probe = lpi2c_imx_probe,
|
||||||
|
@ -682,7 +677,7 @@ static struct platform_driver lpi2c_imx_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
.of_match_table = lpi2c_imx_of_match,
|
.of_match_table = lpi2c_imx_of_match,
|
||||||
.pm = IMX_LPI2C_PM,
|
.pm = &lpi2c_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/acpi.h>
|
|
||||||
|
|
||||||
/* SCH SMBus address offsets */
|
/* SCH SMBus address offsets */
|
||||||
#define SMBHSTCNT (0 + sch_smba)
|
#define SMBHSTCNT (0 + sch_smba)
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#define I2C_RS_TRANSFER (1 << 4)
|
#define I2C_RS_TRANSFER (1 << 4)
|
||||||
|
#define I2C_ARB_LOST (1 << 3)
|
||||||
#define I2C_HS_NACKERR (1 << 2)
|
#define I2C_HS_NACKERR (1 << 2)
|
||||||
#define I2C_ACKERR (1 << 1)
|
#define I2C_ACKERR (1 << 1)
|
||||||
#define I2C_TRANSAC_COMP (1 << 0)
|
#define I2C_TRANSAC_COMP (1 << 0)
|
||||||
|
@ -76,6 +77,8 @@
|
||||||
#define I2C_CONTROL_DIR_CHANGE (0x1 << 4)
|
#define I2C_CONTROL_DIR_CHANGE (0x1 << 4)
|
||||||
#define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5)
|
#define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5)
|
||||||
#define I2C_CONTROL_TRANSFER_LEN_CHANGE (0x1 << 6)
|
#define I2C_CONTROL_TRANSFER_LEN_CHANGE (0x1 << 6)
|
||||||
|
#define I2C_CONTROL_DMAACK_EN (0x1 << 8)
|
||||||
|
#define I2C_CONTROL_ASYNC_MODE (0x1 << 9)
|
||||||
#define I2C_CONTROL_WRAPPER (0x1 << 0)
|
#define I2C_CONTROL_WRAPPER (0x1 << 0)
|
||||||
|
|
||||||
#define I2C_DRV_NAME "i2c-mt65xx"
|
#define I2C_DRV_NAME "i2c-mt65xx"
|
||||||
|
@ -106,40 +109,97 @@ enum mtk_trans_op {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum I2C_REGS_OFFSET {
|
enum I2C_REGS_OFFSET {
|
||||||
OFFSET_DATA_PORT = 0x0,
|
OFFSET_DATA_PORT,
|
||||||
OFFSET_SLAVE_ADDR = 0x04,
|
OFFSET_SLAVE_ADDR,
|
||||||
OFFSET_INTR_MASK = 0x08,
|
OFFSET_INTR_MASK,
|
||||||
OFFSET_INTR_STAT = 0x0c,
|
OFFSET_INTR_STAT,
|
||||||
OFFSET_CONTROL = 0x10,
|
OFFSET_CONTROL,
|
||||||
OFFSET_TRANSFER_LEN = 0x14,
|
OFFSET_TRANSFER_LEN,
|
||||||
OFFSET_TRANSAC_LEN = 0x18,
|
OFFSET_TRANSAC_LEN,
|
||||||
OFFSET_DELAY_LEN = 0x1c,
|
OFFSET_DELAY_LEN,
|
||||||
OFFSET_TIMING = 0x20,
|
OFFSET_TIMING,
|
||||||
OFFSET_START = 0x24,
|
OFFSET_START,
|
||||||
OFFSET_EXT_CONF = 0x28,
|
OFFSET_EXT_CONF,
|
||||||
OFFSET_FIFO_STAT = 0x30,
|
OFFSET_FIFO_STAT,
|
||||||
OFFSET_FIFO_THRESH = 0x34,
|
OFFSET_FIFO_THRESH,
|
||||||
OFFSET_FIFO_ADDR_CLR = 0x38,
|
OFFSET_FIFO_ADDR_CLR,
|
||||||
OFFSET_IO_CONFIG = 0x40,
|
OFFSET_IO_CONFIG,
|
||||||
OFFSET_RSV_DEBUG = 0x44,
|
OFFSET_RSV_DEBUG,
|
||||||
OFFSET_HS = 0x48,
|
OFFSET_HS,
|
||||||
OFFSET_SOFTRESET = 0x50,
|
OFFSET_SOFTRESET,
|
||||||
OFFSET_DCM_EN = 0x54,
|
OFFSET_DCM_EN,
|
||||||
OFFSET_PATH_DIR = 0x60,
|
OFFSET_PATH_DIR,
|
||||||
OFFSET_DEBUGSTAT = 0x64,
|
OFFSET_DEBUGSTAT,
|
||||||
OFFSET_DEBUGCTRL = 0x68,
|
OFFSET_DEBUGCTRL,
|
||||||
OFFSET_TRANSFER_LEN_AUX = 0x6c,
|
OFFSET_TRANSFER_LEN_AUX,
|
||||||
OFFSET_CLOCK_DIV = 0x70,
|
OFFSET_CLOCK_DIV,
|
||||||
|
OFFSET_LTIMING,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u16 mt_i2c_regs_v1[] = {
|
||||||
|
[OFFSET_DATA_PORT] = 0x0,
|
||||||
|
[OFFSET_SLAVE_ADDR] = 0x4,
|
||||||
|
[OFFSET_INTR_MASK] = 0x8,
|
||||||
|
[OFFSET_INTR_STAT] = 0xc,
|
||||||
|
[OFFSET_CONTROL] = 0x10,
|
||||||
|
[OFFSET_TRANSFER_LEN] = 0x14,
|
||||||
|
[OFFSET_TRANSAC_LEN] = 0x18,
|
||||||
|
[OFFSET_DELAY_LEN] = 0x1c,
|
||||||
|
[OFFSET_TIMING] = 0x20,
|
||||||
|
[OFFSET_START] = 0x24,
|
||||||
|
[OFFSET_EXT_CONF] = 0x28,
|
||||||
|
[OFFSET_FIFO_STAT] = 0x30,
|
||||||
|
[OFFSET_FIFO_THRESH] = 0x34,
|
||||||
|
[OFFSET_FIFO_ADDR_CLR] = 0x38,
|
||||||
|
[OFFSET_IO_CONFIG] = 0x40,
|
||||||
|
[OFFSET_RSV_DEBUG] = 0x44,
|
||||||
|
[OFFSET_HS] = 0x48,
|
||||||
|
[OFFSET_SOFTRESET] = 0x50,
|
||||||
|
[OFFSET_DCM_EN] = 0x54,
|
||||||
|
[OFFSET_PATH_DIR] = 0x60,
|
||||||
|
[OFFSET_DEBUGSTAT] = 0x64,
|
||||||
|
[OFFSET_DEBUGCTRL] = 0x68,
|
||||||
|
[OFFSET_TRANSFER_LEN_AUX] = 0x6c,
|
||||||
|
[OFFSET_CLOCK_DIV] = 0x70,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u16 mt_i2c_regs_v2[] = {
|
||||||
|
[OFFSET_DATA_PORT] = 0x0,
|
||||||
|
[OFFSET_SLAVE_ADDR] = 0x4,
|
||||||
|
[OFFSET_INTR_MASK] = 0x8,
|
||||||
|
[OFFSET_INTR_STAT] = 0xc,
|
||||||
|
[OFFSET_CONTROL] = 0x10,
|
||||||
|
[OFFSET_TRANSFER_LEN] = 0x14,
|
||||||
|
[OFFSET_TRANSAC_LEN] = 0x18,
|
||||||
|
[OFFSET_DELAY_LEN] = 0x1c,
|
||||||
|
[OFFSET_TIMING] = 0x20,
|
||||||
|
[OFFSET_START] = 0x24,
|
||||||
|
[OFFSET_EXT_CONF] = 0x28,
|
||||||
|
[OFFSET_LTIMING] = 0x2c,
|
||||||
|
[OFFSET_HS] = 0x30,
|
||||||
|
[OFFSET_IO_CONFIG] = 0x34,
|
||||||
|
[OFFSET_FIFO_ADDR_CLR] = 0x38,
|
||||||
|
[OFFSET_TRANSFER_LEN_AUX] = 0x44,
|
||||||
|
[OFFSET_CLOCK_DIV] = 0x48,
|
||||||
|
[OFFSET_SOFTRESET] = 0x50,
|
||||||
|
[OFFSET_DEBUGSTAT] = 0xe0,
|
||||||
|
[OFFSET_DEBUGCTRL] = 0xe8,
|
||||||
|
[OFFSET_FIFO_STAT] = 0xf4,
|
||||||
|
[OFFSET_FIFO_THRESH] = 0xf8,
|
||||||
|
[OFFSET_DCM_EN] = 0xf88,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mtk_i2c_compatible {
|
struct mtk_i2c_compatible {
|
||||||
const struct i2c_adapter_quirks *quirks;
|
const struct i2c_adapter_quirks *quirks;
|
||||||
|
const u16 *regs;
|
||||||
unsigned char pmic_i2c: 1;
|
unsigned char pmic_i2c: 1;
|
||||||
unsigned char dcm: 1;
|
unsigned char dcm: 1;
|
||||||
unsigned char auto_restart: 1;
|
unsigned char auto_restart: 1;
|
||||||
unsigned char aux_len_reg: 1;
|
unsigned char aux_len_reg: 1;
|
||||||
unsigned char support_33bits: 1;
|
unsigned char support_33bits: 1;
|
||||||
unsigned char timing_adjust: 1;
|
unsigned char timing_adjust: 1;
|
||||||
|
unsigned char dma_sync: 1;
|
||||||
|
unsigned char ltiming_adjust: 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mtk_i2c {
|
struct mtk_i2c {
|
||||||
|
@ -153,6 +213,7 @@ struct mtk_i2c {
|
||||||
struct clk *clk_main; /* main clock for i2c bus */
|
struct clk *clk_main; /* main clock for i2c bus */
|
||||||
struct clk *clk_dma; /* DMA clock for i2c via DMA */
|
struct clk *clk_dma; /* DMA clock for i2c via DMA */
|
||||||
struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
|
struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
|
||||||
|
struct clk *clk_arb; /* Arbitrator clock for i2c */
|
||||||
bool have_pmic; /* can use i2c pins from PMIC */
|
bool have_pmic; /* can use i2c pins from PMIC */
|
||||||
bool use_push_pull; /* IO config push-pull mode */
|
bool use_push_pull; /* IO config push-pull mode */
|
||||||
|
|
||||||
|
@ -162,6 +223,7 @@ struct mtk_i2c {
|
||||||
enum mtk_trans_op op;
|
enum mtk_trans_op op;
|
||||||
u16 timing_reg;
|
u16 timing_reg;
|
||||||
u16 high_speed_reg;
|
u16 high_speed_reg;
|
||||||
|
u16 ltiming_reg;
|
||||||
unsigned char auto_restart;
|
unsigned char auto_restart;
|
||||||
bool ignore_restart_irq;
|
bool ignore_restart_irq;
|
||||||
const struct mtk_i2c_compatible *dev_comp;
|
const struct mtk_i2c_compatible *dev_comp;
|
||||||
|
@ -181,51 +243,78 @@ static const struct i2c_adapter_quirks mt7622_i2c_quirks = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_i2c_compatible mt2712_compat = {
|
static const struct mtk_i2c_compatible mt2712_compat = {
|
||||||
|
.regs = mt_i2c_regs_v1,
|
||||||
.pmic_i2c = 0,
|
.pmic_i2c = 0,
|
||||||
.dcm = 1,
|
.dcm = 1,
|
||||||
.auto_restart = 1,
|
.auto_restart = 1,
|
||||||
.aux_len_reg = 1,
|
.aux_len_reg = 1,
|
||||||
.support_33bits = 1,
|
.support_33bits = 1,
|
||||||
.timing_adjust = 1,
|
.timing_adjust = 1,
|
||||||
|
.dma_sync = 0,
|
||||||
|
.ltiming_adjust = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_i2c_compatible mt6577_compat = {
|
static const struct mtk_i2c_compatible mt6577_compat = {
|
||||||
.quirks = &mt6577_i2c_quirks,
|
.quirks = &mt6577_i2c_quirks,
|
||||||
|
.regs = mt_i2c_regs_v1,
|
||||||
.pmic_i2c = 0,
|
.pmic_i2c = 0,
|
||||||
.dcm = 1,
|
.dcm = 1,
|
||||||
.auto_restart = 0,
|
.auto_restart = 0,
|
||||||
.aux_len_reg = 0,
|
.aux_len_reg = 0,
|
||||||
.support_33bits = 0,
|
.support_33bits = 0,
|
||||||
.timing_adjust = 0,
|
.timing_adjust = 0,
|
||||||
|
.dma_sync = 0,
|
||||||
|
.ltiming_adjust = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_i2c_compatible mt6589_compat = {
|
static const struct mtk_i2c_compatible mt6589_compat = {
|
||||||
.quirks = &mt6577_i2c_quirks,
|
.quirks = &mt6577_i2c_quirks,
|
||||||
|
.regs = mt_i2c_regs_v1,
|
||||||
.pmic_i2c = 1,
|
.pmic_i2c = 1,
|
||||||
.dcm = 0,
|
.dcm = 0,
|
||||||
.auto_restart = 0,
|
.auto_restart = 0,
|
||||||
.aux_len_reg = 0,
|
.aux_len_reg = 0,
|
||||||
.support_33bits = 0,
|
.support_33bits = 0,
|
||||||
.timing_adjust = 0,
|
.timing_adjust = 0,
|
||||||
|
.dma_sync = 0,
|
||||||
|
.ltiming_adjust = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_i2c_compatible mt7622_compat = {
|
static const struct mtk_i2c_compatible mt7622_compat = {
|
||||||
.quirks = &mt7622_i2c_quirks,
|
.quirks = &mt7622_i2c_quirks,
|
||||||
|
.regs = mt_i2c_regs_v1,
|
||||||
.pmic_i2c = 0,
|
.pmic_i2c = 0,
|
||||||
.dcm = 1,
|
.dcm = 1,
|
||||||
.auto_restart = 1,
|
.auto_restart = 1,
|
||||||
.aux_len_reg = 1,
|
.aux_len_reg = 1,
|
||||||
.support_33bits = 0,
|
.support_33bits = 0,
|
||||||
.timing_adjust = 0,
|
.timing_adjust = 0,
|
||||||
|
.dma_sync = 0,
|
||||||
|
.ltiming_adjust = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_i2c_compatible mt8173_compat = {
|
static const struct mtk_i2c_compatible mt8173_compat = {
|
||||||
|
.regs = mt_i2c_regs_v1,
|
||||||
.pmic_i2c = 0,
|
.pmic_i2c = 0,
|
||||||
.dcm = 1,
|
.dcm = 1,
|
||||||
.auto_restart = 1,
|
.auto_restart = 1,
|
||||||
.aux_len_reg = 1,
|
.aux_len_reg = 1,
|
||||||
.support_33bits = 1,
|
.support_33bits = 1,
|
||||||
.timing_adjust = 0,
|
.timing_adjust = 0,
|
||||||
|
.dma_sync = 0,
|
||||||
|
.ltiming_adjust = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mtk_i2c_compatible mt8183_compat = {
|
||||||
|
.regs = mt_i2c_regs_v2,
|
||||||
|
.pmic_i2c = 0,
|
||||||
|
.dcm = 0,
|
||||||
|
.auto_restart = 1,
|
||||||
|
.aux_len_reg = 1,
|
||||||
|
.support_33bits = 1,
|
||||||
|
.timing_adjust = 1,
|
||||||
|
.dma_sync = 1,
|
||||||
|
.ltiming_adjust = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id mtk_i2c_of_match[] = {
|
static const struct of_device_id mtk_i2c_of_match[] = {
|
||||||
|
@ -234,10 +323,22 @@ static const struct of_device_id mtk_i2c_of_match[] = {
|
||||||
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
|
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
|
||||||
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
|
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
|
||||||
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
|
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
|
||||||
|
{ .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, mtk_i2c_of_match);
|
MODULE_DEVICE_TABLE(of, mtk_i2c_of_match);
|
||||||
|
|
||||||
|
static u16 mtk_i2c_readw(struct mtk_i2c *i2c, enum I2C_REGS_OFFSET reg)
|
||||||
|
{
|
||||||
|
return readw(i2c->base + i2c->dev_comp->regs[reg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val,
|
||||||
|
enum I2C_REGS_OFFSET reg)
|
||||||
|
{
|
||||||
|
writew(val, i2c->base + i2c->dev_comp->regs[reg]);
|
||||||
|
}
|
||||||
|
|
||||||
static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
|
static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -255,8 +356,18 @@ static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_pmic;
|
goto err_pmic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i2c->clk_arb) {
|
||||||
|
ret = clk_prepare_enable(i2c->clk_arb);
|
||||||
|
if (ret)
|
||||||
|
goto err_arb;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_arb:
|
||||||
|
if (i2c->have_pmic)
|
||||||
|
clk_disable_unprepare(i2c->clk_pmic);
|
||||||
err_pmic:
|
err_pmic:
|
||||||
clk_disable_unprepare(i2c->clk_main);
|
clk_disable_unprepare(i2c->clk_main);
|
||||||
err_main:
|
err_main:
|
||||||
|
@ -267,6 +378,9 @@ err_main:
|
||||||
|
|
||||||
static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
|
static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
|
||||||
{
|
{
|
||||||
|
if (i2c->clk_arb)
|
||||||
|
clk_disable_unprepare(i2c->clk_arb);
|
||||||
|
|
||||||
if (i2c->have_pmic)
|
if (i2c->have_pmic)
|
||||||
clk_disable_unprepare(i2c->clk_pmic);
|
clk_disable_unprepare(i2c->clk_pmic);
|
||||||
|
|
||||||
|
@ -278,31 +392,36 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
|
||||||
{
|
{
|
||||||
u16 control_reg;
|
u16 control_reg;
|
||||||
|
|
||||||
writew(I2C_SOFT_RST, i2c->base + OFFSET_SOFTRESET);
|
mtk_i2c_writew(i2c, I2C_SOFT_RST, OFFSET_SOFTRESET);
|
||||||
|
|
||||||
/* Set ioconfig */
|
/* Set ioconfig */
|
||||||
if (i2c->use_push_pull)
|
if (i2c->use_push_pull)
|
||||||
writew(I2C_IO_CONFIG_PUSH_PULL, i2c->base + OFFSET_IO_CONFIG);
|
mtk_i2c_writew(i2c, I2C_IO_CONFIG_PUSH_PULL, OFFSET_IO_CONFIG);
|
||||||
else
|
else
|
||||||
writew(I2C_IO_CONFIG_OPEN_DRAIN, i2c->base + OFFSET_IO_CONFIG);
|
mtk_i2c_writew(i2c, I2C_IO_CONFIG_OPEN_DRAIN, OFFSET_IO_CONFIG);
|
||||||
|
|
||||||
if (i2c->dev_comp->dcm)
|
if (i2c->dev_comp->dcm)
|
||||||
writew(I2C_DCM_DISABLE, i2c->base + OFFSET_DCM_EN);
|
mtk_i2c_writew(i2c, I2C_DCM_DISABLE, OFFSET_DCM_EN);
|
||||||
|
|
||||||
if (i2c->dev_comp->timing_adjust)
|
if (i2c->dev_comp->timing_adjust)
|
||||||
writew(I2C_DEFAULT_CLK_DIV - 1, i2c->base + OFFSET_CLOCK_DIV);
|
mtk_i2c_writew(i2c, I2C_DEFAULT_CLK_DIV - 1, OFFSET_CLOCK_DIV);
|
||||||
|
|
||||||
writew(i2c->timing_reg, i2c->base + OFFSET_TIMING);
|
mtk_i2c_writew(i2c, i2c->timing_reg, OFFSET_TIMING);
|
||||||
writew(i2c->high_speed_reg, i2c->base + OFFSET_HS);
|
mtk_i2c_writew(i2c, i2c->high_speed_reg, OFFSET_HS);
|
||||||
|
if (i2c->dev_comp->ltiming_adjust)
|
||||||
|
mtk_i2c_writew(i2c, i2c->ltiming_reg, OFFSET_LTIMING);
|
||||||
|
|
||||||
/* If use i2c pin from PMIC mt6397 side, need set PATH_DIR first */
|
/* If use i2c pin from PMIC mt6397 side, need set PATH_DIR first */
|
||||||
if (i2c->have_pmic)
|
if (i2c->have_pmic)
|
||||||
writew(I2C_CONTROL_WRAPPER, i2c->base + OFFSET_PATH_DIR);
|
mtk_i2c_writew(i2c, I2C_CONTROL_WRAPPER, OFFSET_PATH_DIR);
|
||||||
|
|
||||||
control_reg = I2C_CONTROL_ACKERR_DET_EN |
|
control_reg = I2C_CONTROL_ACKERR_DET_EN |
|
||||||
I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN;
|
I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN;
|
||||||
writew(control_reg, i2c->base + OFFSET_CONTROL);
|
if (i2c->dev_comp->dma_sync)
|
||||||
writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN);
|
control_reg |= I2C_CONTROL_DMAACK_EN | I2C_CONTROL_ASYNC_MODE;
|
||||||
|
|
||||||
|
mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
|
||||||
|
mtk_i2c_writew(i2c, I2C_DELAY_LEN, OFFSET_DELAY_LEN);
|
||||||
|
|
||||||
writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
|
writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
|
||||||
udelay(50);
|
udelay(50);
|
||||||
|
@ -390,6 +509,8 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
|
||||||
unsigned int clk_src;
|
unsigned int clk_src;
|
||||||
unsigned int step_cnt;
|
unsigned int step_cnt;
|
||||||
unsigned int sample_cnt;
|
unsigned int sample_cnt;
|
||||||
|
unsigned int l_step_cnt;
|
||||||
|
unsigned int l_sample_cnt;
|
||||||
unsigned int target_speed;
|
unsigned int target_speed;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -399,11 +520,11 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
|
||||||
if (target_speed > MAX_FS_MODE_SPEED) {
|
if (target_speed > MAX_FS_MODE_SPEED) {
|
||||||
/* Set master code speed register */
|
/* Set master code speed register */
|
||||||
ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED,
|
ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED,
|
||||||
&step_cnt, &sample_cnt);
|
&l_step_cnt, &l_sample_cnt);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
i2c->timing_reg = (sample_cnt << 8) | step_cnt;
|
i2c->timing_reg = (l_sample_cnt << 8) | l_step_cnt;
|
||||||
|
|
||||||
/* Set the high speed mode register */
|
/* Set the high speed mode register */
|
||||||
ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
|
ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
|
||||||
|
@ -413,6 +534,10 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
|
||||||
|
|
||||||
i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
|
i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
|
||||||
(sample_cnt << 12) | (step_cnt << 8);
|
(sample_cnt << 12) | (step_cnt << 8);
|
||||||
|
|
||||||
|
if (i2c->dev_comp->ltiming_adjust)
|
||||||
|
i2c->ltiming_reg = (l_sample_cnt << 6) | l_step_cnt |
|
||||||
|
(sample_cnt << 12) | (step_cnt << 9);
|
||||||
} else {
|
} else {
|
||||||
ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
|
ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
|
||||||
&step_cnt, &sample_cnt);
|
&step_cnt, &sample_cnt);
|
||||||
|
@ -423,6 +548,9 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
|
||||||
|
|
||||||
/* Disable the high speed transaction */
|
/* Disable the high speed transaction */
|
||||||
i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
|
i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
|
||||||
|
|
||||||
|
if (i2c->dev_comp->ltiming_adjust)
|
||||||
|
i2c->ltiming_reg = (sample_cnt << 6) | step_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -454,7 +582,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||||
|
|
||||||
reinit_completion(&i2c->msg_complete);
|
reinit_completion(&i2c->msg_complete);
|
||||||
|
|
||||||
control_reg = readw(i2c->base + OFFSET_CONTROL) &
|
control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) &
|
||||||
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
|
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
|
||||||
if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1))
|
if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1))
|
||||||
control_reg |= I2C_CONTROL_RS;
|
control_reg |= I2C_CONTROL_RS;
|
||||||
|
@ -462,40 +590,41 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||||
if (i2c->op == I2C_MASTER_WRRD)
|
if (i2c->op == I2C_MASTER_WRRD)
|
||||||
control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS;
|
control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS;
|
||||||
|
|
||||||
writew(control_reg, i2c->base + OFFSET_CONTROL);
|
mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
|
||||||
|
|
||||||
/* set start condition */
|
/* set start condition */
|
||||||
if (i2c->speed_hz <= I2C_DEFAULT_SPEED)
|
if (i2c->speed_hz <= I2C_DEFAULT_SPEED)
|
||||||
writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF);
|
mtk_i2c_writew(i2c, I2C_ST_START_CON, OFFSET_EXT_CONF);
|
||||||
else
|
else
|
||||||
writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
|
mtk_i2c_writew(i2c, I2C_FS_START_CON, OFFSET_EXT_CONF);
|
||||||
|
|
||||||
addr_reg = i2c_8bit_addr_from_msg(msgs);
|
addr_reg = i2c_8bit_addr_from_msg(msgs);
|
||||||
writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR);
|
mtk_i2c_writew(i2c, addr_reg, OFFSET_SLAVE_ADDR);
|
||||||
|
|
||||||
/* Clear interrupt status */
|
/* Clear interrupt status */
|
||||||
writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
|
mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
|
||||||
I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT);
|
I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_STAT);
|
||||||
writew(I2C_FIFO_ADDR_CLR, i2c->base + OFFSET_FIFO_ADDR_CLR);
|
|
||||||
|
mtk_i2c_writew(i2c, I2C_FIFO_ADDR_CLR, OFFSET_FIFO_ADDR_CLR);
|
||||||
|
|
||||||
/* Enable interrupt */
|
/* Enable interrupt */
|
||||||
writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
|
mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
|
||||||
I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK);
|
I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_MASK);
|
||||||
|
|
||||||
/* Set transfer and transaction len */
|
/* Set transfer and transaction len */
|
||||||
if (i2c->op == I2C_MASTER_WRRD) {
|
if (i2c->op == I2C_MASTER_WRRD) {
|
||||||
if (i2c->dev_comp->aux_len_reg) {
|
if (i2c->dev_comp->aux_len_reg) {
|
||||||
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
|
mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
|
||||||
writew((msgs + 1)->len, i2c->base +
|
mtk_i2c_writew(i2c, (msgs + 1)->len,
|
||||||
OFFSET_TRANSFER_LEN_AUX);
|
OFFSET_TRANSFER_LEN_AUX);
|
||||||
} else {
|
} else {
|
||||||
writew(msgs->len | ((msgs + 1)->len) << 8,
|
mtk_i2c_writew(i2c, msgs->len | ((msgs + 1)->len) << 8,
|
||||||
i2c->base + OFFSET_TRANSFER_LEN);
|
OFFSET_TRANSFER_LEN);
|
||||||
}
|
}
|
||||||
writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
|
mtk_i2c_writew(i2c, I2C_WRRD_TRANAC_VALUE, OFFSET_TRANSAC_LEN);
|
||||||
} else {
|
} else {
|
||||||
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
|
mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
|
||||||
writew(num, i2c->base + OFFSET_TRANSAC_LEN);
|
mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare buffer data to start transfer */
|
/* Prepare buffer data to start transfer */
|
||||||
|
@ -607,14 +736,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||||
if (left_num >= 1)
|
if (left_num >= 1)
|
||||||
start_reg |= I2C_RS_MUL_CNFG;
|
start_reg |= I2C_RS_MUL_CNFG;
|
||||||
}
|
}
|
||||||
writew(start_reg, i2c->base + OFFSET_START);
|
mtk_i2c_writew(i2c, start_reg, OFFSET_START);
|
||||||
|
|
||||||
ret = wait_for_completion_timeout(&i2c->msg_complete,
|
ret = wait_for_completion_timeout(&i2c->msg_complete,
|
||||||
i2c->adap.timeout);
|
i2c->adap.timeout);
|
||||||
|
|
||||||
/* Clear interrupt mask */
|
/* Clear interrupt mask */
|
||||||
writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
|
mtk_i2c_writew(i2c, ~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
|
||||||
I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK);
|
I2C_ARB_LOST | I2C_TRANSAC_COMP), OFFSET_INTR_MASK);
|
||||||
|
|
||||||
if (i2c->op == I2C_MASTER_WR) {
|
if (i2c->op == I2C_MASTER_WR) {
|
||||||
dma_unmap_single(i2c->dev, wpaddr,
|
dma_unmap_single(i2c->dev, wpaddr,
|
||||||
|
@ -724,8 +853,8 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
|
||||||
if (i2c->auto_restart)
|
if (i2c->auto_restart)
|
||||||
restart_flag = I2C_RS_TRANSFER;
|
restart_flag = I2C_RS_TRANSFER;
|
||||||
|
|
||||||
intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
|
intr_stat = mtk_i2c_readw(i2c, OFFSET_INTR_STAT);
|
||||||
writew(intr_stat, i2c->base + OFFSET_INTR_STAT);
|
mtk_i2c_writew(i2c, intr_stat, OFFSET_INTR_STAT);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* when occurs ack error, i2c controller generate two interrupts
|
* when occurs ack error, i2c controller generate two interrupts
|
||||||
|
@ -737,8 +866,8 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
|
||||||
if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
|
if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
|
||||||
i2c->ignore_restart_irq = false;
|
i2c->ignore_restart_irq = false;
|
||||||
i2c->irq_stat = 0;
|
i2c->irq_stat = 0;
|
||||||
writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START,
|
mtk_i2c_writew(i2c, I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG |
|
||||||
i2c->base + OFFSET_START);
|
I2C_TRANSAC_START, OFFSET_START);
|
||||||
} else {
|
} else {
|
||||||
if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
|
if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
|
||||||
complete(&i2c->msg_complete);
|
complete(&i2c->msg_complete);
|
||||||
|
@ -839,6 +968,10 @@ static int mtk_i2c_probe(struct platform_device *pdev)
|
||||||
return PTR_ERR(i2c->clk_dma);
|
return PTR_ERR(i2c->clk_dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i2c->clk_arb = devm_clk_get(&pdev->dev, "arb");
|
||||||
|
if (IS_ERR(i2c->clk_arb))
|
||||||
|
i2c->clk_arb = NULL;
|
||||||
|
|
||||||
clk = i2c->clk_main;
|
clk = i2c->clk_main;
|
||||||
if (i2c->have_pmic) {
|
if (i2c->have_pmic) {
|
||||||
i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
|
i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
|
||||||
|
|
|
@ -1070,8 +1070,7 @@ static int nmk_i2c_remove(struct amba_device *adev)
|
||||||
/* disable the controller */
|
/* disable the controller */
|
||||||
i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
|
i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
|
||||||
clk_disable_unprepare(dev->clk);
|
clk_disable_unprepare(dev->clk);
|
||||||
if (res)
|
release_mem_region(res->start, resource_size(res));
|
||||||
release_mem_region(res->start, resource_size(res));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,6 @@
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
|
|
||||||
#define OCORES_FLAG_POLL BIT(0)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 'process_lock' exists because ocores_process() and ocores_process_timeout()
|
* 'process_lock' exists because ocores_process() and ocores_process_timeout()
|
||||||
* can't run in parallel.
|
* can't run in parallel.
|
||||||
|
@ -37,7 +35,6 @@ struct ocores_i2c {
|
||||||
int iobase;
|
int iobase;
|
||||||
u32 reg_shift;
|
u32 reg_shift;
|
||||||
u32 reg_io_width;
|
u32 reg_io_width;
|
||||||
unsigned long flags;
|
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct i2c_msg *msg;
|
struct i2c_msg *msg;
|
||||||
|
@ -403,11 +400,7 @@ static int ocores_xfer_polling(struct i2c_adapter *adap,
|
||||||
static int ocores_xfer(struct i2c_adapter *adap,
|
static int ocores_xfer(struct i2c_adapter *adap,
|
||||||
struct i2c_msg *msgs, int num)
|
struct i2c_msg *msgs, int num)
|
||||||
{
|
{
|
||||||
struct ocores_i2c *i2c = i2c_get_adapdata(adap);
|
return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, false);
|
||||||
|
|
||||||
if (i2c->flags & OCORES_FLAG_POLL)
|
|
||||||
return ocores_xfer_polling(adap, msgs, num);
|
|
||||||
return ocores_xfer_core(i2c, msgs, num, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
|
static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
|
||||||
|
@ -447,8 +440,9 @@ static u32 ocores_func(struct i2c_adapter *adap)
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm ocores_algorithm = {
|
static struct i2c_algorithm ocores_algorithm = {
|
||||||
.master_xfer = ocores_xfer,
|
.master_xfer = ocores_xfer,
|
||||||
|
.master_xfer_atomic = ocores_xfer_polling,
|
||||||
.functionality = ocores_func,
|
.functionality = ocores_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -673,13 +667,13 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq == -ENXIO) {
|
if (irq == -ENXIO) {
|
||||||
i2c->flags |= OCORES_FLAG_POLL;
|
ocores_algorithm.master_xfer = ocores_xfer_polling;
|
||||||
} else {
|
} else {
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(i2c->flags & OCORES_FLAG_POLL)) {
|
if (ocores_algorithm.master_xfer != ocores_xfer_polling) {
|
||||||
ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
|
ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
|
||||||
pdev->name, i2c);
|
pdev->name, i2c);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
|
@ -269,6 +269,8 @@ static const u8 reg_map_ip_v2[] = {
|
||||||
[OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
|
[OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int omap_i2c_xfer_data(struct omap_i2c_dev *omap);
|
||||||
|
|
||||||
static inline void omap_i2c_write_reg(struct omap_i2c_dev *omap,
|
static inline void omap_i2c_write_reg(struct omap_i2c_dev *omap,
|
||||||
int reg, u16 val)
|
int reg, u16 val)
|
||||||
{
|
{
|
||||||
|
@ -648,15 +650,28 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *omap, u8 size, bool is_rx)
|
||||||
(1000 * omap->speed / 8);
|
(1000 * omap->speed / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void omap_i2c_wait(struct omap_i2c_dev *omap)
|
||||||
|
{
|
||||||
|
u16 stat;
|
||||||
|
u16 mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
|
||||||
|
count++;
|
||||||
|
} while (!(stat & mask) && count < 5);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Low level master read/write transaction.
|
* Low level master read/write transaction.
|
||||||
*/
|
*/
|
||||||
static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
||||||
struct i2c_msg *msg, int stop)
|
struct i2c_msg *msg, int stop, bool polling)
|
||||||
{
|
{
|
||||||
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
|
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
u16 w;
|
u16 w;
|
||||||
|
int ret;
|
||||||
|
|
||||||
dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
|
dev_dbg(omap->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);
|
||||||
|
@ -680,7 +695,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
||||||
w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
|
w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
|
||||||
omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w);
|
omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w);
|
||||||
|
|
||||||
reinit_completion(&omap->cmd_complete);
|
if (!polling)
|
||||||
|
reinit_completion(&omap->cmd_complete);
|
||||||
omap->cmd_err = 0;
|
omap->cmd_err = 0;
|
||||||
|
|
||||||
w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
|
w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
|
||||||
|
@ -732,8 +748,18 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
||||||
* REVISIT: We should abort the transfer on signals, but the bus goes
|
* REVISIT: We should abort the transfer on signals, but the bus goes
|
||||||
* into arbitration and we're currently unable to recover from it.
|
* into arbitration and we're currently unable to recover from it.
|
||||||
*/
|
*/
|
||||||
timeout = wait_for_completion_timeout(&omap->cmd_complete,
|
if (!polling) {
|
||||||
OMAP_I2C_TIMEOUT);
|
timeout = wait_for_completion_timeout(&omap->cmd_complete,
|
||||||
|
OMAP_I2C_TIMEOUT);
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
omap_i2c_wait(omap);
|
||||||
|
ret = omap_i2c_xfer_data(omap);
|
||||||
|
} while (ret == -EAGAIN);
|
||||||
|
|
||||||
|
timeout = !ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
dev_err(omap->dev, "controller timed out\n");
|
dev_err(omap->dev, "controller timed out\n");
|
||||||
omap_i2c_reset(omap);
|
omap_i2c_reset(omap);
|
||||||
|
@ -772,7 +798,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
||||||
* to do the work during IRQ processing.
|
* to do the work during IRQ processing.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
omap_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg msgs[], int num,
|
||||||
|
bool polling)
|
||||||
{
|
{
|
||||||
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
|
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
|
||||||
int i;
|
int i;
|
||||||
|
@ -794,7 +821,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
omap->set_mpu_wkup_lat(omap->dev, omap->latency);
|
omap->set_mpu_wkup_lat(omap->dev, omap->latency);
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
|
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)),
|
||||||
|
polling);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -813,6 +841,18 @@ out:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
omap_i2c_xfer_irq(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
|
{
|
||||||
|
return omap_i2c_xfer_common(adap, msgs, num, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
omap_i2c_xfer_polling(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
|
{
|
||||||
|
return omap_i2c_xfer_common(adap, msgs, num, true);
|
||||||
|
}
|
||||||
|
|
||||||
static u32
|
static u32
|
||||||
omap_i2c_func(struct i2c_adapter *adap)
|
omap_i2c_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
|
@ -1035,10 +1075,8 @@ omap_i2c_isr(int irq, void *dev_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t
|
static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
|
||||||
omap_i2c_isr_thread(int this_irq, void *dev_id)
|
|
||||||
{
|
{
|
||||||
struct omap_i2c_dev *omap = dev_id;
|
|
||||||
u16 bits;
|
u16 bits;
|
||||||
u16 stat;
|
u16 stat;
|
||||||
int err = 0, count = 0;
|
int err = 0, count = 0;
|
||||||
|
@ -1056,7 +1094,8 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
|
||||||
|
|
||||||
if (!stat) {
|
if (!stat) {
|
||||||
/* my work here is done */
|
/* my work here is done */
|
||||||
goto out;
|
err = -EAGAIN;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat);
|
dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat);
|
||||||
|
@ -1165,14 +1204,25 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
|
||||||
}
|
}
|
||||||
} while (stat);
|
} while (stat);
|
||||||
|
|
||||||
omap_i2c_complete_cmd(omap, err);
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t
|
||||||
|
omap_i2c_isr_thread(int this_irq, void *dev_id)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct omap_i2c_dev *omap = dev_id;
|
||||||
|
|
||||||
|
ret = omap_i2c_xfer_data(omap);
|
||||||
|
if (ret != -EAGAIN)
|
||||||
|
omap_i2c_complete_cmd(omap, ret);
|
||||||
|
|
||||||
out:
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm omap_i2c_algo = {
|
static const struct i2c_algorithm omap_i2c_algo = {
|
||||||
.master_xfer = omap_i2c_xfer,
|
.master_xfer = omap_i2c_xfer_irq,
|
||||||
|
.master_xfer_atomic = omap_i2c_xfer_polling,
|
||||||
.functionality = omap_i2c_func,
|
.functionality = omap_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
|
Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
|
||||||
ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800
|
ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800
|
||||||
AMD Hudson-2, ML, CZ
|
AMD Hudson-2, ML, CZ
|
||||||
|
Hygon CZ
|
||||||
SMSC Victory66
|
SMSC Victory66
|
||||||
|
|
||||||
Note: we assume there can only be one device, with one or more
|
Note: we assume there can only be one device, with one or more
|
||||||
|
@ -289,7 +290,9 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||||
PIIX4_dev->revision >= 0x41) ||
|
PIIX4_dev->revision >= 0x41) ||
|
||||||
(PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
|
(PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||||
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
|
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
|
||||||
PIIX4_dev->revision >= 0x49))
|
PIIX4_dev->revision >= 0x49) ||
|
||||||
|
(PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON &&
|
||||||
|
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS))
|
||||||
smb_en = 0x00;
|
smb_en = 0x00;
|
||||||
else
|
else
|
||||||
smb_en = (aux) ? 0x28 : 0x2c;
|
smb_en = (aux) ? 0x28 : 0x2c;
|
||||||
|
@ -361,7 +364,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||||
piix4_smba, i2ccfg >> 4);
|
piix4_smba, i2ccfg >> 4);
|
||||||
|
|
||||||
/* Find which register is used for port selection */
|
/* Find which register is used for port selection */
|
||||||
if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD) {
|
if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD ||
|
||||||
|
PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON) {
|
||||||
switch (PIIX4_dev->device) {
|
switch (PIIX4_dev->device) {
|
||||||
case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS:
|
case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS:
|
||||||
piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ;
|
piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ;
|
||||||
|
@ -794,6 +798,7 @@ static const struct pci_device_id piix4_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
|
||||||
|
{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
||||||
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
|
PCI_DEVICE_ID_SERVERWORKS_OSB4) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
|
||||||
|
@ -904,11 +909,13 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
|
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
|
||||||
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||||
dev->revision >= 0x40) ||
|
dev->revision >= 0x40) ||
|
||||||
dev->vendor == PCI_VENDOR_ID_AMD) {
|
dev->vendor == PCI_VENDOR_ID_AMD ||
|
||||||
|
dev->vendor == PCI_VENDOR_ID_HYGON) {
|
||||||
bool notify_imc = false;
|
bool notify_imc = false;
|
||||||
is_sb800 = true;
|
is_sb800 = true;
|
||||||
|
|
||||||
if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
if ((dev->vendor == PCI_VENDOR_ID_AMD ||
|
||||||
|
dev->vendor == PCI_VENDOR_ID_HYGON) &&
|
||||||
dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) {
|
dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) {
|
||||||
u8 imc;
|
u8 imc;
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
/* ICFBSCR */
|
/* ICFBSCR */
|
||||||
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
|
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
|
||||||
|
|
||||||
|
#define RCAR_MIN_DMA_LEN 8
|
||||||
|
|
||||||
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
|
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
|
||||||
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
|
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
|
||||||
|
@ -398,7 +399,7 @@ static void rcar_i2c_dma_callback(void *data)
|
||||||
rcar_i2c_dma_unmap(priv);
|
rcar_i2c_dma_unmap(priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||||
{
|
{
|
||||||
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||||
struct i2c_msg *msg = priv->msg;
|
struct i2c_msg *msg = priv->msg;
|
||||||
|
@ -412,9 +413,9 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
/* Do various checks to see if DMA is feasible at all */
|
/* Do various checks to see if DMA is feasible at all */
|
||||||
if (IS_ERR(chan) || msg->len < 8 || !(msg->flags & I2C_M_DMA_SAFE) ||
|
if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
|
||||||
(read && priv->flags & ID_P_NO_RXDMA))
|
!(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (read) {
|
if (read) {
|
||||||
/*
|
/*
|
||||||
|
@ -434,7 +435,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||||
dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
|
dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
|
||||||
if (dma_mapping_error(chan->device->dev, dma_addr)) {
|
if (dma_mapping_error(chan->device->dev, dma_addr)) {
|
||||||
dev_dbg(dev, "dma map failed, using PIO\n");
|
dev_dbg(dev, "dma map failed, using PIO\n");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sg_dma_len(&priv->sg) = len;
|
sg_dma_len(&priv->sg) = len;
|
||||||
|
@ -448,7 +449,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||||
if (!txdesc) {
|
if (!txdesc) {
|
||||||
dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
|
dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
|
||||||
rcar_i2c_cleanup_dma(priv);
|
rcar_i2c_cleanup_dma(priv);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
txdesc->callback = rcar_i2c_dma_callback;
|
txdesc->callback = rcar_i2c_dma_callback;
|
||||||
|
@ -458,7 +459,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||||
if (dma_submit_error(cookie)) {
|
if (dma_submit_error(cookie)) {
|
||||||
dev_dbg(dev, "submitting dma failed, using PIO\n");
|
dev_dbg(dev, "submitting dma failed, using PIO\n");
|
||||||
rcar_i2c_cleanup_dma(priv);
|
rcar_i2c_cleanup_dma(priv);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable DMA Master Received/Transmitted */
|
/* Enable DMA Master Received/Transmitted */
|
||||||
|
@ -468,6 +469,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||||
rcar_i2c_write(priv, ICDMAER, TMDMAE);
|
rcar_i2c_write(priv, ICDMAER, TMDMAE);
|
||||||
|
|
||||||
dma_async_issue_pending(chan);
|
dma_async_issue_pending(chan);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
|
@ -478,6 +480,10 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
if (!(msr & MDE))
|
if (!(msr & MDE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Check if DMA can be enabled and take over */
|
||||||
|
if (priv->pos == 1 && rcar_i2c_dma(priv))
|
||||||
|
return;
|
||||||
|
|
||||||
if (priv->pos < msg->len) {
|
if (priv->pos < msg->len) {
|
||||||
/*
|
/*
|
||||||
* Prepare next data to ICRXTX register.
|
* Prepare next data to ICRXTX register.
|
||||||
|
@ -488,13 +494,6 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
*/
|
*/
|
||||||
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
|
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
|
||||||
priv->pos++;
|
priv->pos++;
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to use DMA to transmit the rest of the data if
|
|
||||||
* address transfer phase just finished.
|
|
||||||
*/
|
|
||||||
if (msr & MAT)
|
|
||||||
rcar_i2c_dma(priv);
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* The last data was pushed to ICRXTX on _PREV_ empty irq.
|
* The last data was pushed to ICRXTX on _PREV_ empty irq.
|
||||||
|
@ -921,6 +920,9 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||||
struct i2c_timings i2c_t;
|
struct i2c_timings i2c_t;
|
||||||
int irq, ret;
|
int irq, ret;
|
||||||
|
|
||||||
|
/* Otherwise logic will break because some bytes must always use PIO */
|
||||||
|
BUILD_BUG_ON_MSG(RCAR_MIN_DMA_LEN < 3, "Invalid min DMA length");
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#define RIIC_ICCR1 0x00
|
#define RIIC_ICCR1 0x00
|
||||||
#define RIIC_ICCR2 0x04
|
#define RIIC_ICCR2 0x04
|
||||||
|
@ -112,12 +113,10 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
{
|
{
|
||||||
struct riic_dev *riic = i2c_get_adapdata(adap);
|
struct riic_dev *riic = i2c_get_adapdata(adap);
|
||||||
unsigned long time_left;
|
unsigned long time_left;
|
||||||
int i, ret;
|
int i;
|
||||||
u8 start_bit;
|
u8 start_bit;
|
||||||
|
|
||||||
ret = clk_prepare_enable(riic->clk);
|
pm_runtime_get_sync(adap->dev.parent);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (readb(riic->base + RIIC_ICCR2) & ICCR2_BBSY) {
|
if (readb(riic->base + RIIC_ICCR2) & ICCR2_BBSY) {
|
||||||
riic->err = -EBUSY;
|
riic->err = -EBUSY;
|
||||||
|
@ -150,7 +149,7 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
clk_disable_unprepare(riic->clk);
|
pm_runtime_put(adap->dev.parent);
|
||||||
|
|
||||||
return riic->err ?: num;
|
return riic->err ?: num;
|
||||||
}
|
}
|
||||||
|
@ -281,20 +280,18 @@ static const struct i2c_algorithm riic_algo = {
|
||||||
|
|
||||||
static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret = 0;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
int total_ticks, cks, brl, brh;
|
int total_ticks, cks, brl, brh;
|
||||||
|
|
||||||
ret = clk_prepare_enable(riic->clk);
|
pm_runtime_get_sync(riic->adapter.dev.parent);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (t->bus_freq_hz > 400000) {
|
if (t->bus_freq_hz > 400000) {
|
||||||
dev_err(&riic->adapter.dev,
|
dev_err(&riic->adapter.dev,
|
||||||
"unsupported bus speed (%dHz). 400000 max\n",
|
"unsupported bus speed (%dHz). 400000 max\n",
|
||||||
t->bus_freq_hz);
|
t->bus_freq_hz);
|
||||||
clk_disable_unprepare(riic->clk);
|
ret = -EINVAL;
|
||||||
return -EINVAL;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
rate = clk_get_rate(riic->clk);
|
rate = clk_get_rate(riic->clk);
|
||||||
|
@ -332,8 +329,8 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||||
if (brl > (0x1F + 3)) {
|
if (brl > (0x1F + 3)) {
|
||||||
dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
|
dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
|
||||||
(unsigned long)t->bus_freq_hz);
|
(unsigned long)t->bus_freq_hz);
|
||||||
clk_disable_unprepare(riic->clk);
|
ret = -EINVAL;
|
||||||
return -EINVAL;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
brh = total_ticks - brl;
|
brh = total_ticks - brl;
|
||||||
|
@ -378,9 +375,9 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
|
||||||
|
|
||||||
riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
|
riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
|
||||||
|
|
||||||
clk_disable_unprepare(riic->clk);
|
out:
|
||||||
|
pm_runtime_put(riic->adapter.dev.parent);
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct riic_irq_desc riic_irqs[] = {
|
static struct riic_irq_desc riic_irqs[] = {
|
||||||
|
@ -439,28 +436,36 @@ static int riic_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
|
i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
|
||||||
|
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
ret = riic_init_hw(riic, &i2c_t);
|
ret = riic_init_hw(riic, &i2c_t);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto out;
|
||||||
|
|
||||||
|
|
||||||
ret = i2c_add_adapter(adap);
|
ret = i2c_add_adapter(adap);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto out;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, riic);
|
platform_set_drvdata(pdev, riic);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "registered with %dHz bus speed\n",
|
dev_info(&pdev->dev, "registered with %dHz bus speed\n",
|
||||||
i2c_t.bus_freq_hz);
|
i2c_t.bus_freq_hz);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int riic_i2c_remove(struct platform_device *pdev)
|
static int riic_i2c_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct riic_dev *riic = platform_get_drvdata(pdev);
|
struct riic_dev *riic = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
writeb(0, riic->base + RIIC_ICIER);
|
writeb(0, riic->base + RIIC_ICIER);
|
||||||
|
pm_runtime_put(&pdev->dev);
|
||||||
i2c_del_adapter(&riic->adapter);
|
i2c_del_adapter(&riic->adapter);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -476,8 +476,12 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
|
||||||
|
|
||||||
list_add_tail(&v->node,
|
list_add_tail(&v->node,
|
||||||
&solutions);
|
&solutions);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p_prev == p)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -328,12 +328,6 @@ static int stu300_start_and_await_event(struct stu300_dev *dev,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (unlikely(irqs_disabled())) {
|
|
||||||
/* TODO: implement polling for this case if need be. */
|
|
||||||
WARN(1, "irqs are disabled, cannot poll for event\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lock command issue, fill in an event we wait for */
|
/* Lock command issue, fill in an event we wait for */
|
||||||
spin_lock_irq(&dev->cmd_issue_lock);
|
spin_lock_irq(&dev->cmd_issue_lock);
|
||||||
init_completion(&dev->cmd_complete);
|
init_completion(&dev->cmd_complete);
|
||||||
|
@ -380,13 +374,6 @@ static int stu300_await_event(struct stu300_dev *dev,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (unlikely(irqs_disabled())) {
|
|
||||||
/* TODO: implement polling for this case if need be. */
|
|
||||||
dev_err(&dev->pdev->dev, "irqs are disabled on this "
|
|
||||||
"system!\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Is it already here? */
|
/* Is it already here? */
|
||||||
spin_lock_irq(&dev->cmd_issue_lock);
|
spin_lock_irq(&dev->cmd_issue_lock);
|
||||||
dev->cmd_err = STU300_ERROR_NONE;
|
dev->cmd_err = STU300_ERROR_NONE;
|
||||||
|
@ -846,6 +833,13 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stu300_xfer_todo(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
|
{
|
||||||
|
/* TODO: implement polling for this case if need be. */
|
||||||
|
WARN(1, "%s: atomic transfers not implemented\n", dev_name(&adap->dev));
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 stu300_func(struct i2c_adapter *adap)
|
static u32 stu300_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
/* This is the simplest thing you can think of... */
|
/* This is the simplest thing you can think of... */
|
||||||
|
@ -853,8 +847,9 @@ static u32 stu300_func(struct i2c_adapter *adap)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm stu300_algo = {
|
static const struct i2c_algorithm stu300_algo = {
|
||||||
.master_xfer = stu300_xfer,
|
.master_xfer = stu300_xfer,
|
||||||
.functionality = stu300_func,
|
.master_xfer_atomic = stu300_xfer_todo,
|
||||||
|
.functionality = stu300_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct i2c_adapter_quirks stu300_quirks = {
|
static const struct i2c_adapter_quirks stu300_quirks = {
|
||||||
|
|
|
@ -207,7 +207,8 @@ static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num)
|
||||||
|
|
||||||
static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
|
static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
|
||||||
struct mrq_i2c_request *request,
|
struct mrq_i2c_request *request,
|
||||||
struct mrq_i2c_response *response)
|
struct mrq_i2c_response *response,
|
||||||
|
bool atomic)
|
||||||
{
|
{
|
||||||
struct tegra_bpmp_message msg;
|
struct tegra_bpmp_message msg;
|
||||||
int err;
|
int err;
|
||||||
|
@ -222,7 +223,7 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
|
||||||
msg.rx.data = response;
|
msg.rx.data = response;
|
||||||
msg.rx.size = sizeof(*response);
|
msg.rx.size = sizeof(*response);
|
||||||
|
|
||||||
if (irqs_disabled())
|
if (atomic)
|
||||||
err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
|
err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
|
||||||
else
|
else
|
||||||
err = tegra_bpmp_transfer(i2c->bpmp, &msg);
|
err = tegra_bpmp_transfer(i2c->bpmp, &msg);
|
||||||
|
@ -230,8 +231,9 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
|
static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
|
||||||
struct i2c_msg *msgs, int num)
|
struct i2c_msg *msgs, int num,
|
||||||
|
bool atomic)
|
||||||
{
|
{
|
||||||
struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
|
struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
|
||||||
struct mrq_i2c_response response;
|
struct mrq_i2c_response response;
|
||||||
|
@ -253,7 +255,7 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response);
|
err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(i2c->dev, "failed to transfer message: %d\n", err);
|
dev_err(i2c->dev, "failed to transfer message: %d\n", err);
|
||||||
return err;
|
return err;
|
||||||
|
@ -268,6 +270,18 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
|
struct i2c_msg *msgs, int num)
|
||||||
|
{
|
||||||
|
return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_bpmp_i2c_xfer_atomic(struct i2c_adapter *adapter,
|
||||||
|
struct i2c_msg *msgs, int num)
|
||||||
|
{
|
||||||
|
return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, true);
|
||||||
|
}
|
||||||
|
|
||||||
static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
|
static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
|
||||||
{
|
{
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
|
||||||
|
@ -276,6 +290,7 @@ static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
|
||||||
|
|
||||||
static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
|
static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
|
||||||
.master_xfer = tegra_bpmp_i2c_xfer,
|
.master_xfer = tegra_bpmp_i2c_xfer,
|
||||||
|
.master_xfer_atomic = tegra_bpmp_i2c_xfer_atomic,
|
||||||
.functionality = tegra_bpmp_i2c_func,
|
.functionality = tegra_bpmp_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1871,8 +1871,10 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
|
|
||||||
if (WARN_ON(!msgs || num < 1))
|
if (WARN_ON(!msgs || num < 1))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (WARN_ON(test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)))
|
|
||||||
return -ESHUTDOWN;
|
ret = __i2c_check_suspended(adap);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
|
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
@ -1894,7 +1896,11 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
/* Retry automatically on arbitration loss */
|
/* Retry automatically on arbitration loss */
|
||||||
orig_jiffies = jiffies;
|
orig_jiffies = jiffies;
|
||||||
for (ret = 0, try = 0; try <= adap->retries; try++) {
|
for (ret = 0, try = 0; try <= adap->retries; try++) {
|
||||||
ret = adap->algo->master_xfer(adap, msgs, num);
|
if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
|
||||||
|
ret = adap->algo->master_xfer_atomic(adap, msgs, num);
|
||||||
|
else
|
||||||
|
ret = adap->algo->master_xfer(adap, msgs, num);
|
||||||
|
|
||||||
if (ret != -EAGAIN)
|
if (ret != -EAGAIN)
|
||||||
break;
|
break;
|
||||||
if (time_after(jiffies, orig_jiffies + adap->timeout))
|
if (time_after(jiffies, orig_jiffies + adap->timeout))
|
||||||
|
@ -1950,14 +1956,9 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
* one (discarding status on the second message) or errno
|
* one (discarding status on the second message) or errno
|
||||||
* (discarding status on the first one).
|
* (discarding status on the first one).
|
||||||
*/
|
*/
|
||||||
if (in_atomic() || irqs_disabled()) {
|
ret = __i2c_lock_bus_helper(adap);
|
||||||
ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);
|
if (ret)
|
||||||
if (!ret)
|
return ret;
|
||||||
/* I2C activity is ongoing. */
|
|
||||||
return -EAGAIN;
|
|
||||||
} else {
|
|
||||||
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = __i2c_transfer(adap, msgs, num);
|
ret = __i2c_transfer(adap, msgs, num);
|
||||||
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
|
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include <linux/i2c-smbus.h>
|
#include <linux/i2c-smbus.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "i2c-core.h"
|
||||||
|
|
||||||
#define CREATE_TRACE_POINTS
|
#define CREATE_TRACE_POINTS
|
||||||
#include <trace/events/smbus.h>
|
#include <trace/events/smbus.h>
|
||||||
|
|
||||||
|
@ -530,7 +532,10 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
{
|
{
|
||||||
s32 res;
|
s32 res;
|
||||||
|
|
||||||
i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
|
res = __i2c_lock_bus_helper(adapter);
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
|
res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
|
||||||
command, protocol, data);
|
command, protocol, data);
|
||||||
i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
|
i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
|
||||||
|
@ -543,10 +548,17 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
unsigned short flags, char read_write,
|
unsigned short flags, char read_write,
|
||||||
u8 command, int protocol, union i2c_smbus_data *data)
|
u8 command, int protocol, union i2c_smbus_data *data)
|
||||||
{
|
{
|
||||||
|
int (*xfer_func)(struct i2c_adapter *adap, u16 addr,
|
||||||
|
unsigned short flags, char read_write,
|
||||||
|
u8 command, int size, union i2c_smbus_data *data);
|
||||||
unsigned long orig_jiffies;
|
unsigned long orig_jiffies;
|
||||||
int try;
|
int try;
|
||||||
s32 res;
|
s32 res;
|
||||||
|
|
||||||
|
res = __i2c_check_suspended(adapter);
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
/* If enabled, the following two tracepoints are conditional on
|
/* If enabled, the following two tracepoints are conditional on
|
||||||
* read_write and protocol.
|
* read_write and protocol.
|
||||||
*/
|
*/
|
||||||
|
@ -557,13 +569,20 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
|
|
||||||
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
|
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
|
||||||
|
|
||||||
if (adapter->algo->smbus_xfer) {
|
xfer_func = adapter->algo->smbus_xfer;
|
||||||
|
if (i2c_in_atomic_xfer_mode()) {
|
||||||
|
if (adapter->algo->smbus_xfer_atomic)
|
||||||
|
xfer_func = adapter->algo->smbus_xfer_atomic;
|
||||||
|
else if (adapter->algo->master_xfer_atomic)
|
||||||
|
xfer_func = NULL; /* fallback to I2C emulation */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xfer_func) {
|
||||||
/* Retry automatically on arbitration loss */
|
/* Retry automatically on arbitration loss */
|
||||||
orig_jiffies = jiffies;
|
orig_jiffies = jiffies;
|
||||||
for (res = 0, try = 0; try <= adapter->retries; try++) {
|
for (res = 0, try = 0; try <= adapter->retries; try++) {
|
||||||
res = adapter->algo->smbus_xfer(adapter, addr, flags,
|
res = xfer_func(adapter, addr, flags, read_write,
|
||||||
read_write, command,
|
command, protocol, data);
|
||||||
protocol, data);
|
|
||||||
if (res != -EAGAIN)
|
if (res != -EAGAIN)
|
||||||
break;
|
break;
|
||||||
if (time_after(jiffies,
|
if (time_after(jiffies,
|
||||||
|
|
|
@ -29,6 +29,42 @@ extern int __i2c_first_dynamic_bus_num;
|
||||||
|
|
||||||
int i2c_check_7bit_addr_validity_strict(unsigned short addr);
|
int i2c_check_7bit_addr_validity_strict(unsigned short addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only allow atomic transfers for very late communication, e.g. to send
|
||||||
|
* the powerdown command to a PMIC. Atomic transfers are a corner case and not
|
||||||
|
* for generic use!
|
||||||
|
*/
|
||||||
|
static inline bool i2c_in_atomic_xfer_mode(void)
|
||||||
|
{
|
||||||
|
return system_state > SYSTEM_RUNNING && irqs_disabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (i2c_in_atomic_xfer_mode()) {
|
||||||
|
WARN(!adap->algo->master_xfer_atomic && !adap->algo->smbus_xfer_atomic,
|
||||||
|
"No atomic I2C transfer handler for '%s'\n", dev_name(&adap->dev));
|
||||||
|
ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT) ? 0 : -EAGAIN;
|
||||||
|
} else {
|
||||||
|
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int __i2c_check_suspended(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
if (test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)) {
|
||||||
|
if (!test_and_set_bit(I2C_ALF_SUSPEND_REPORTED, &adap->locked_flags))
|
||||||
|
dev_WARN(&adap->dev, "Transfer while suspended\n");
|
||||||
|
return -ESHUTDOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
const struct acpi_device_id *
|
const struct acpi_device_id *
|
||||||
i2c_acpi_match_device(const struct acpi_device_id *matches,
|
i2c_acpi_match_device(const struct acpi_device_id *matches,
|
||||||
|
|
|
@ -310,12 +310,18 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
|
||||||
else
|
else
|
||||||
priv->algo.master_xfer = __i2c_mux_master_xfer;
|
priv->algo.master_xfer = __i2c_mux_master_xfer;
|
||||||
}
|
}
|
||||||
|
if (parent->algo->master_xfer_atomic)
|
||||||
|
priv->algo.master_xfer_atomic = priv->algo.master_xfer;
|
||||||
|
|
||||||
if (parent->algo->smbus_xfer) {
|
if (parent->algo->smbus_xfer) {
|
||||||
if (muxc->mux_locked)
|
if (muxc->mux_locked)
|
||||||
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
|
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
|
||||||
else
|
else
|
||||||
priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
|
priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
|
||||||
}
|
}
|
||||||
|
if (parent->algo->smbus_xfer_atomic)
|
||||||
|
priv->algo.smbus_xfer_atomic = priv->algo.smbus_xfer;
|
||||||
|
|
||||||
priv->algo.functionality = i2c_mux_functionality;
|
priv->algo.functionality = i2c_mux_functionality;
|
||||||
|
|
||||||
/* Now fill out new adapter structure */
|
/* Now fill out new adapter structure */
|
||||||
|
|
|
@ -99,6 +99,8 @@ static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 ne
|
||||||
|
|
||||||
/* Now fill out current adapter structure. cur_chan must be up to date */
|
/* Now fill out current adapter structure. cur_chan must be up to date */
|
||||||
priv->algo.master_xfer = i2c_demux_master_xfer;
|
priv->algo.master_xfer = i2c_demux_master_xfer;
|
||||||
|
if (adap->algo->master_xfer_atomic)
|
||||||
|
priv->algo.master_xfer_atomic = i2c_demux_master_xfer;
|
||||||
priv->algo.functionality = i2c_demux_functionality;
|
priv->algo.functionality = i2c_demux_functionality;
|
||||||
|
|
||||||
snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
|
snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
|
||||||
|
@ -219,8 +221,8 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv)
|
priv = devm_kzalloc(&pdev->dev, struct_size(priv, chan, num_chan),
|
||||||
+ num_chan * sizeof(struct i2c_demux_pinctrl_chan), GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
|
||||||
props = devm_kcalloc(&pdev->dev, num_chan, sizeof(*props), GFP_KERNEL);
|
props = devm_kcalloc(&pdev->dev, num_chan, sizeof(*props), GFP_KERNEL);
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include <linux/i2c-mux.h>
|
#include <linux/i2c-mux.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_data/pca954x.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -287,10 +286,8 @@ static int pca9541_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap = client->adapter;
|
struct i2c_adapter *adap = client->adapter;
|
||||||
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
|
||||||
struct i2c_mux_core *muxc;
|
struct i2c_mux_core *muxc;
|
||||||
struct pca9541 *data;
|
struct pca9541 *data;
|
||||||
int force;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
|
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||||
|
@ -306,9 +303,6 @@ static int pca9541_probe(struct i2c_client *client,
|
||||||
|
|
||||||
/* Create mux adapter */
|
/* Create mux adapter */
|
||||||
|
|
||||||
force = 0;
|
|
||||||
if (pdata)
|
|
||||||
force = pdata->modes[0].adap_id;
|
|
||||||
muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data),
|
muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data),
|
||||||
I2C_MUX_ARBITRATOR,
|
I2C_MUX_ARBITRATOR,
|
||||||
pca9541_select_chan, pca9541_release_chan);
|
pca9541_select_chan, pca9541_release_chan);
|
||||||
|
@ -320,7 +314,7 @@ static int pca9541_probe(struct i2c_client *client,
|
||||||
|
|
||||||
i2c_set_clientdata(client, muxc);
|
i2c_set_clientdata(client, muxc);
|
||||||
|
|
||||||
ret = i2c_mux_add_adapter(muxc, force, 0, 0);
|
ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,10 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/platform_data/pca954x.h>
|
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <dt-bindings/mux/mux.h>
|
||||||
|
|
||||||
#define PCA954X_MAX_NCHANS 8
|
#define PCA954X_MAX_NCHANS 8
|
||||||
|
|
||||||
|
@ -85,7 +85,9 @@ struct pca954x {
|
||||||
const struct chip_desc *chip;
|
const struct chip_desc *chip;
|
||||||
|
|
||||||
u8 last_chan; /* last register value */
|
u8 last_chan; /* last register value */
|
||||||
u8 deselect;
|
/* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
|
||||||
|
s8 idle_state;
|
||||||
|
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
|
||||||
struct irq_domain *irq;
|
struct irq_domain *irq;
|
||||||
|
@ -254,15 +256,71 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
|
||||||
{
|
{
|
||||||
struct pca954x *data = i2c_mux_priv(muxc);
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
struct i2c_client *client = data->client;
|
struct i2c_client *client = data->client;
|
||||||
|
s8 idle_state;
|
||||||
|
|
||||||
if (!(data->deselect & (1 << chan)))
|
idle_state = READ_ONCE(data->idle_state);
|
||||||
return 0;
|
if (idle_state >= 0)
|
||||||
|
/* Set the mux back to a predetermined channel */
|
||||||
|
return pca954x_select_chan(muxc, idle_state);
|
||||||
|
|
||||||
/* Deselect active channel */
|
if (idle_state == MUX_IDLE_DISCONNECT) {
|
||||||
data->last_chan = 0;
|
/* Deselect active channel */
|
||||||
return pca954x_reg_write(muxc->parent, client, data->last_chan);
|
data->last_chan = 0;
|
||||||
|
return pca954x_reg_write(muxc->parent, client,
|
||||||
|
data->last_chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise leave as-is */
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t idle_state_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
|
return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t idle_state_store(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
int val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtoint(buf, 0, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
|
||||||
|
(val < 0 || val >= data->chip->nchans))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
|
WRITE_ONCE(data->idle_state, val);
|
||||||
|
/*
|
||||||
|
* Set the mux into a state consistent with the new
|
||||||
|
* idle_state.
|
||||||
|
*/
|
||||||
|
if (data->last_chan || val != MUX_IDLE_DISCONNECT)
|
||||||
|
ret = pca954x_deselect_mux(muxc, 0);
|
||||||
|
|
||||||
|
i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
|
return ret < 0 ? ret : count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR_RW(idle_state);
|
||||||
|
|
||||||
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
|
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct pca954x *data = dev_id;
|
struct pca954x *data = dev_id;
|
||||||
|
@ -329,8 +387,11 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
|
||||||
static void pca954x_cleanup(struct i2c_mux_core *muxc)
|
static void pca954x_cleanup(struct i2c_mux_core *muxc)
|
||||||
{
|
{
|
||||||
struct pca954x *data = i2c_mux_priv(muxc);
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
int c, irq;
|
int c, irq;
|
||||||
|
|
||||||
|
device_remove_file(&client->dev, &dev_attr_idle_state);
|
||||||
|
|
||||||
if (data->irq) {
|
if (data->irq) {
|
||||||
for (c = 0; c < data->chip->nchans; c++) {
|
for (c = 0; c < data->chip->nchans; c++) {
|
||||||
irq = irq_find_mapping(data->irq, c);
|
irq = irq_find_mapping(data->irq, c);
|
||||||
|
@ -348,14 +409,13 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap = client->adapter;
|
struct i2c_adapter *adap = client->adapter;
|
||||||
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
struct device_node *np = dev->of_node;
|
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;
|
|
||||||
struct i2c_mux_core *muxc;
|
struct i2c_mux_core *muxc;
|
||||||
struct pca954x *data;
|
struct pca954x *data;
|
||||||
|
int num;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
||||||
|
@ -412,9 +472,12 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
}
|
}
|
||||||
|
|
||||||
data->last_chan = 0; /* force the first selection */
|
data->last_chan = 0; /* force the first selection */
|
||||||
|
data->idle_state = MUX_IDLE_AS_IS;
|
||||||
|
|
||||||
idle_disconnect_dt = np &&
|
idle_disconnect_dt = np &&
|
||||||
of_property_read_bool(np, "i2c-mux-idle-disconnect");
|
of_property_read_bool(np, "i2c-mux-idle-disconnect");
|
||||||
|
if (idle_disconnect_dt)
|
||||||
|
data->idle_state = MUX_IDLE_DISCONNECT;
|
||||||
|
|
||||||
ret = pca954x_irq_setup(muxc);
|
ret = pca954x_irq_setup(muxc);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -422,24 +485,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
|
|
||||||
/* Now create an adapter for each channel */
|
/* Now create an adapter for each channel */
|
||||||
for (num = 0; num < data->chip->nchans; num++) {
|
for (num = 0; num < data->chip->nchans; num++) {
|
||||||
bool idle_disconnect_pd = false;
|
ret = i2c_mux_add_adapter(muxc, 0, num, 0);
|
||||||
|
|
||||||
force = 0; /* dynamic adap number */
|
|
||||||
class = 0; /* no class by default */
|
|
||||||
if (pdata) {
|
|
||||||
if (num < pdata->num_modes) {
|
|
||||||
/* force static number */
|
|
||||||
force = pdata->modes[num].adap_id;
|
|
||||||
class = pdata->modes[num].class;
|
|
||||||
} else
|
|
||||||
/* discard unconfigured channels */
|
|
||||||
break;
|
|
||||||
idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
|
|
||||||
}
|
|
||||||
data->deselect |= (idle_disconnect_pd ||
|
|
||||||
idle_disconnect_dt) << num;
|
|
||||||
|
|
||||||
ret = i2c_mux_add_adapter(muxc, force, num, class);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto fail_cleanup;
|
goto fail_cleanup;
|
||||||
}
|
}
|
||||||
|
@ -453,6 +499,12 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
goto fail_cleanup;
|
goto fail_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The attr probably isn't going to be needed in most cases,
|
||||||
|
* so don't fail completely on error.
|
||||||
|
*/
|
||||||
|
device_create_file(dev, &dev_attr_idle_state);
|
||||||
|
|
||||||
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
|
dev_info(dev, "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);
|
||||||
|
|
|
@ -33,6 +33,7 @@ struct i2c_algo_bit_data {
|
||||||
minimum 5 us for standard-mode I2C and SMBus,
|
minimum 5 us for standard-mode I2C and SMBus,
|
||||||
maximum 50 us for SMBus */
|
maximum 50 us for SMBus */
|
||||||
int timeout; /* in jiffies */
|
int timeout; /* in jiffies */
|
||||||
|
bool can_do_atomic; /* callbacks don't sleep, we can be atomic */
|
||||||
};
|
};
|
||||||
|
|
||||||
int i2c_bit_add_bus(struct i2c_adapter *);
|
int i2c_bit_add_bus(struct i2c_adapter *);
|
||||||
|
|
|
@ -499,9 +499,13 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
|
||||||
* @master_xfer: Issue a set of i2c transactions to the given I2C adapter
|
* @master_xfer: Issue a set of i2c transactions to the given I2C adapter
|
||||||
* defined by the msgs array, with num messages available to transfer via
|
* defined by the msgs array, with num messages available to transfer via
|
||||||
* the adapter specified by adap.
|
* the adapter specified by adap.
|
||||||
|
* @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context
|
||||||
|
* so e.g. PMICs can be accessed very late before shutdown. Optional.
|
||||||
* @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
|
* @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
|
||||||
* is not present, then the bus layer will try and convert the SMBus calls
|
* is not present, then the bus layer will try and convert the SMBus calls
|
||||||
* into I2C transfers instead.
|
* into I2C transfers instead.
|
||||||
|
* @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context
|
||||||
|
* so e.g. PMICs can be accessed very late before shutdown. Optional.
|
||||||
* @functionality: Return the flags that this algorithm/adapter pair supports
|
* @functionality: Return the flags that this algorithm/adapter pair supports
|
||||||
* from the I2C_FUNC_* flags.
|
* from the I2C_FUNC_* flags.
|
||||||
* @reg_slave: Register given client to I2C slave mode of this adapter
|
* @reg_slave: Register given client to I2C slave mode of this adapter
|
||||||
|
@ -512,25 +516,33 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
|
||||||
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
|
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
|
||||||
* to name two of the most common.
|
* to name two of the most common.
|
||||||
*
|
*
|
||||||
* The return codes from the @master_xfer field should indicate the type of
|
* The return codes from the @master_xfer{_atomic} fields should indicate the
|
||||||
* error code that occurred during the transfer, as documented in the kernel
|
* type of error code that occurred during the transfer, as documented in the
|
||||||
* Documentation file Documentation/i2c/fault-codes.
|
* Kernel Documentation file Documentation/i2c/fault-codes.
|
||||||
*/
|
*/
|
||||||
struct i2c_algorithm {
|
struct i2c_algorithm {
|
||||||
/* If an adapter algorithm can't do I2C-level access, set master_xfer
|
/*
|
||||||
to NULL. If an adapter algorithm can do SMBus access, set
|
* If an adapter algorithm can't do I2C-level access, set master_xfer
|
||||||
smbus_xfer. If set to NULL, the SMBus protocol is simulated
|
* to NULL. If an adapter algorithm can do SMBus access, set
|
||||||
using common I2C messages */
|
* smbus_xfer. If set to NULL, the SMBus protocol is simulated
|
||||||
/* master_xfer should return the number of messages successfully
|
* using common I2C messages.
|
||||||
processed, or a negative value on error */
|
*
|
||||||
|
* master_xfer should return the number of messages successfully
|
||||||
|
* processed, or a negative value on error
|
||||||
|
*/
|
||||||
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
int num);
|
int num);
|
||||||
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
|
int (*master_xfer_atomic)(struct i2c_adapter *adap,
|
||||||
unsigned short flags, char read_write,
|
struct i2c_msg *msgs, int num);
|
||||||
u8 command, int size, union i2c_smbus_data *data);
|
int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
|
||||||
|
unsigned short flags, char read_write,
|
||||||
|
u8 command, int size, union i2c_smbus_data *data);
|
||||||
|
int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,
|
||||||
|
unsigned short flags, char read_write,
|
||||||
|
u8 command, int size, union i2c_smbus_data *data);
|
||||||
|
|
||||||
/* To determine what the adapter supports */
|
/* To determine what the adapter supports */
|
||||||
u32 (*functionality) (struct i2c_adapter *);
|
u32 (*functionality)(struct i2c_adapter *adap);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||||
int (*reg_slave)(struct i2c_client *client);
|
int (*reg_slave)(struct i2c_client *client);
|
||||||
|
@ -682,7 +694,8 @@ struct i2c_adapter {
|
||||||
int retries;
|
int retries;
|
||||||
struct device dev; /* the adapter device */
|
struct device dev; /* the adapter device */
|
||||||
unsigned long locked_flags; /* owned by the I2C core */
|
unsigned long locked_flags; /* owned by the I2C core */
|
||||||
#define I2C_ALF_IS_SUSPENDED 0
|
#define I2C_ALF_IS_SUSPENDED 0
|
||||||
|
#define I2C_ALF_SUSPEND_REPORTED 1
|
||||||
|
|
||||||
int nr;
|
int nr;
|
||||||
char name[48];
|
char name[48];
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* pca954x.h - I2C multiplexer/switch support
|
|
||||||
*
|
|
||||||
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
|
|
||||||
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
|
|
||||||
* Michael Lawnick <michael.lawnick.ext@nsn.com>
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _LINUX_I2C_PCA954X_H
|
|
||||||
#define _LINUX_I2C_PCA954X_H
|
|
||||||
|
|
||||||
/* Platform data for the PCA954x I2C multiplexers */
|
|
||||||
|
|
||||||
/* Per channel initialisation data:
|
|
||||||
* @adap_id: bus number for the adapter. 0 = don't care
|
|
||||||
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
|
|
||||||
* of this channel after transaction.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct pca954x_platform_mode {
|
|
||||||
int adap_id;
|
|
||||||
unsigned int deselect_on_exit:1;
|
|
||||||
unsigned int class;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Per mux/switch data, used with i2c_register_board_info */
|
|
||||||
struct pca954x_platform_data {
|
|
||||||
struct pca954x_platform_mode *modes;
|
|
||||||
int num_modes;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _LINUX_I2C_PCA954X_H */
|
|
Loading…
Reference in New Issue