diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x b/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x new file mode 100644 index 000000000000..0b0de8cd0d13 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-pca954x @@ -0,0 +1,20 @@ +What: /sys/bus/i2c/.../idle_state +Date: January 2019 +KernelVersion: 5.2 +Contact: Robert Shearman +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.. - 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 diff --git a/Documentation/devicetree/bindings/eeprom/at24.txt b/Documentation/devicetree/bindings/eeprom/at24.txt index 0e456bbc1213..22aead844d0f 100644 --- a/Documentation/devicetree/bindings/eeprom/at24.txt +++ b/Documentation/devicetree/bindings/eeprom/at24.txt @@ -50,6 +50,7 @@ Required properties: "nxp,se97b" - 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" "rohm,br24t01" - the fallback is "atmel,24c01" diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt index 81f982ccca31..d12cc33cca6c 100644 --- a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -3,15 +3,12 @@ Broadcom iProc I2C controller Required properties: - compatible: - Must be "brcm,iproc-i2c" + Must be "brcm,iproc-i2c" or "brcm,iproc-nic-i2c" - reg: Define the base and range of the I/O address space that contain the iProc I2C controller registers -- interrupts: - Should contain the I2C interrupt - - clock-frequency: This is the I2C bus clock. Need to be either 100000 or 400000 @@ -21,6 +18,18 @@ Required properties: - #size-cells: 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: i2c0: i2c@18008000 { compatible = "brcm,iproc-i2c"; diff --git a/Documentation/devicetree/bindings/i2c/i2c-designware.txt b/Documentation/devicetree/bindings/i2c/i2c-designware.txt index 3e4bcc2fb6f7..08be4d3846e5 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-designware.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-designware.txt @@ -6,12 +6,21 @@ Required properties : or "mscc,ocelot-i2c" with "snps,designware-i2c" for fallback - reg : Offset and length of the register set for the device - interrupts : 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 : - clock-frequency : desired I2C bus clock frequency in Hz. 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 time, named ICPU_CFG:TWI_DELAY in the datasheet. diff --git a/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt b/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt index ee4c32454198..68f6d73a8b73 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-mt65xx.txt @@ -12,13 +12,16 @@ Required properties: "mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623 "mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629 "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 mapped region. - interrupts: interrupt number to the cpu. - clock-div: the fixed value for frequency divider of clock source in i2c module. Each IC may be different. - 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. Optional properties: diff --git a/Documentation/devicetree/bindings/i2c/i2c-riic.txt b/Documentation/devicetree/bindings/i2c/i2c-riic.txt index 0bcc4716c319..e26fe3ad86a9 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-riic.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-riic.txt @@ -1,7 +1,10 @@ Device tree configuration for Renesas RIIC driver Required properties: -- compatible : "renesas,riic-". "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 - interrupts : 8 interrupts (TEI, RI, TI, SPI, STI, NAKI, ALI, TMOI) - clock-frequency : frequency of bus clock in Hz diff --git a/Documentation/devicetree/bindings/i2c/i2c-stm32.txt b/Documentation/devicetree/bindings/i2c/i2c-stm32.txt index 69240e189b01..f334738f7a35 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-stm32.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-stm32.txt @@ -1,11 +1,11 @@ * I2C controller embedded in STMicroelectronics STM32 I2C platform -Required properties : -- compatible : Must be one of the following +Required properties: +- compatible: Must be one of the following - "st,stm32f4-i2c" - "st,stm32f7-i2c" -- reg : Offset and length of the register set for the device -- interrupts : Must contain the interrupt id for I2C event and then the +- reg: Offset and length of the register set for the device +- interrupts: Must contain the interrupt id for I2C event and then the interrupt id for I2C error. - resets: Must contain the phandle to the reset controller. - clocks: Must contain the input clock of the I2C instance. @@ -14,25 +14,26 @@ Required properties : - #address-cells = <1>; - #size-cells = <0>; -Optional properties : -- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified, +Optional properties: +- clock-frequency: Desired I2C bus clock frequency in Hz. If not specified, the default 100 kHz frequency will be used. For STM32F4 SoC Standard-mode and Fast-mode are supported, possible values are 100000 and 400000. - For STM32F7 SoC, Standard-mode, Fast-mode and Fast-mode 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 - (default: 25) -- i2c-scl-falling-time-ns : Only for STM32F7, I2C SCL Falling time for the board - (default: 10) + For STM32F7, STM32H7 and STM32MP1 SoCs, Standard-mode, Fast-mode and Fast-mode + Plus are supported, possible values are 100000, 400000 and 1000000. +- i2c-scl-rising-time-ns: I2C SCL Rising time for the board (default: 25) + For STM32F7, STM32H7 and STM32MP1 only. +- i2c-scl-falling-time-ns: I2C SCL Falling time for the board (default: 10) + For STM32F7, STM32H7 and STM32MP1 only. I2C Timings are derived from these 2 values -- st,syscfg-fmp: Only for STM32F7, use to set Fast Mode Plus bit within SYSCFG - whether Fast Mode Plus speed is selected by slave. - 1st cell : phandle to syscfg - 2nd cell : register offset within SYSCFG - 3rd cell : register bitmask for FMP bit +- st,syscfg-fmp: Use to set Fast Mode Plus bit within SYSCFG when Fast Mode + Plus speed is selected by slave. + 1st cell: phandle to syscfg + 2nd cell: register offset within SYSCFG + 3rd cell: register bitmask for FMP bit + For STM32F7, STM32H7 and STM32MP1 only. -Example : +Example: i2c@40005400 { compatible = "st,stm32f4-i2c"; diff --git a/Documentation/i2c/busses/i2c-amd-mp2 b/Documentation/i2c/busses/i2c-amd-mp2 new file mode 100644 index 000000000000..6571487171f4 --- /dev/null +++ b/Documentation/i2c/busses/i2c-amd-mp2 @@ -0,0 +1,23 @@ +Kernel driver i2c-amd-mp2 + +Supported adapters: + * AMD MP2 PCIe interface + +Datasheet: not publicly available. + +Authors: + Shyam Sundar S K + Nehal Shah + Elie Morisse + +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. diff --git a/Documentation/i2c/busses/i2c-piix4 b/Documentation/i2c/busses/i2c-piix4 index aa959fd22450..2703bc3acad0 100644 --- a/Documentation/i2c/busses/i2c-piix4 +++ b/Documentation/i2c/busses/i2c-piix4 @@ -15,6 +15,8 @@ Supported adapters: http://support.amd.com/us/Embedded_TechDocs/44413.pdf * AMD Hudson-2, ML, CZ Datasheet: Not publicly available + * Hygon CZ + Datasheet: Not publicly available * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge Datasheet: Publicly available at the SMSC website http://www.smsc.com diff --git a/MAINTAINERS b/MAINTAINERS index ae4063ef5533..a3143515e134 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -826,6 +826,14 @@ F: drivers/gpu/drm/amd/include/vi_structs.h F: drivers/gpu/drm/amd/include/v9_structs.h F: include/uapi/linux/kfd_ioctl.h +AMD MP2 I2C DRIVER +M: Elie Morisse +M: Nehal Shah +M: Shyam Sundar S K +L: linux-i2c@vger.kernel.org +S: Maintained +F: drivers/i2c/busses/i2c-amd-mp2* + AMD POWERPLAY M: Rex Zhu M: Evan Quan @@ -2582,7 +2590,7 @@ F: include/linux/dmaengine.h F: include/linux/async_tx.h AT24 EEPROM DRIVER -M: Bartosz Golaszewski +M: Bartosz Golaszewski L: linux-i2c@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git S: Maintained @@ -10207,7 +10215,8 @@ MICROCHIP I2C DRIVER M: Ludovic Desroches L: linux-i2c@vger.kernel.org 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 M: Eugen Hristev diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 5e5990a83da5..913db013fe90 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -603,6 +603,23 @@ bailout: 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) { 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: ------------------------------------- */ const struct i2c_algorithm i2c_bit_algo = { - .master_xfer = bit_xfer, - .functionality = bit_func, + .master_xfer = bit_xfer, + .master_xfer_atomic = bit_xfer_atomic, + .functionality = bit_func, }; EXPORT_SYMBOL(i2c_bit_algo); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index f8979abb9a19..26186439db6b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -77,6 +77,16 @@ config I2C_AMD8111 This driver can also be built as a module. If so, the module 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 tristate "Hix5hd2 high-speed I2C driver" depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST @@ -176,6 +186,7 @@ config I2C_PIIX4 AMD Hudson-2 AMD ML AMD CZ + Hygon CZ Serverworks OSB4 Serverworks CSB5 Serverworks CSB6 @@ -388,6 +399,19 @@ config I2C_AT91 the latency to fill the transmission register is too long. If you 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 tristate "Au1550/Au1200/Au1300 SMBus interface" depends on MIPS_ALCHEMY @@ -425,6 +449,7 @@ config I2C_BCM_IPROC tristate "Broadcom iProc I2C controller" depends on ARCH_BCM_IPROC || COMPILE_TEST default ARCH_BCM_IPROC + select I2C_SLAVE help If you say yes to this option, support will be included for the Broadcom iProc I2C controller. diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 5f0cb6915969..a3245231b0b7 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,8 +33,13 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o # Embedded system I2C/SMBus host controller drivers obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o +obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o +i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o +ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y) + i2c-at91-objs += i2c-at91-slave.o +endif obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o diff --git a/drivers/i2c/busses/i2c-amd-mp2-pci.c b/drivers/i2c/busses/i2c-amd-mp2-pci.c new file mode 100644 index 000000000000..455e1f36a2a3 --- /dev/null +++ b/drivers/i2c/busses/i2c-amd-mp2-pci.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * AMD MP2 PCIe communication driver + * + * Authors: Shyam Sundar S K + * Elie Morisse + */ + +#include +#include +#include +#include +#include + +#include "i2c-amd-mp2.h" + +#include + +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 "); +MODULE_AUTHOR("Elie Morisse "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c new file mode 100644 index 000000000000..f5b3f00c6559 --- /dev/null +++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c @@ -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 + * Elie Morisse + */ + +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Elie Morisse "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/i2c/busses/i2c-amd-mp2.h b/drivers/i2c/busses/i2c-amd-mp2.h new file mode 100644 index 000000000000..058362edebaa --- /dev/null +++ b/drivers/i2c/busses/i2c-amd-mp2.h @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * AMD MP2 I2C adapter driver + * + * Authors: Shyam Sundar S K + * Elie Morisse + */ + +#ifndef I2C_AMD_PCI_MP2_H +#define I2C_AMD_PCI_MP2_H + +#include +#include +#include + +#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 diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c new file mode 100644 index 000000000000..8d55cdd69ff4 --- /dev/null +++ b/drivers/i2c/busses/i2c-at91-core.c @@ -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 + * + * Evolved from original work by: + * Copyright (C) 2004 Rick Bronson + * Converted to 2.6 by Andrew Victor + * + * Borrowed heavily from original work by: + * Copyright (C) 2000 Philip Edelbrock + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:at91_i2c"); diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91-master.c similarity index 65% rename from drivers/i2c/busses/i2c-at91.c rename to drivers/i2c/busses/i2c-at91-master.c index 3f3e8b3bf5ff..e87232f2e708 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91-master.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * i2c Support for Atmel's AT91 Two-Wire Interface (TWI) * @@ -10,11 +11,6 @@ * * Borrowed heavily from original work by: * Copyright (C) 2000 Philip Edelbrock - * - * 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 @@ -25,159 +21,16 @@ #include #include #include -#include #include #include #include -#include #include #include -#include -#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */ -#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 +#include "i2c-at91.h" -/* 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_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) +void at91_init_twi_bus_master(struct at91_twi_dev *dev) { - 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 */ if (dev->fifo_size) 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: * 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; struct at91_twi_pdata *pdata = dev->pdata; int offset = pdata->clk_offset; 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), - 2 * twi_clk) - offset); + 2 * t->bus_freq_hz) - offset); ckdiv = fls(div >> 8); 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) { - 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 * Use clk rate in kHz to prevent overflows when computing * hold. */ - hold = DIV_ROUND_UP(twd_hold_time_ns + hold = DIV_ROUND_UP(t->sda_hold_ns * (clk_get_rate(dev->clk) / 1000), 1000000); hold -= 3; 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); 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) @@ -833,124 +685,6 @@ static const struct i2c_algorithm at91_twi_algorithm = { .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) { int ret = 0; @@ -1033,74 +767,24 @@ error: return ret; } -static struct at91_twi_pdata *at91_twi_get_driver_data( - struct platform_device *pdev) +int at91_twi_probe_master(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; - 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); - 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, - dev_name(dev->dev), dev); + dev_name(dev->dev), dev); if (rc) { dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, 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) { rc = at91_twi_configure_dma(dev, phy_addr); - if (rc == -EPROBE_DEFER) { - clk_disable_unprepare(dev->clk); + if (rc == -EPROBE_DEFER) return rc; - } } 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); } - rc = of_property_read_u32(dev->dev->of_node, "clock-frequency", - &bus_clk_rate); - if (rc) - bus_clk_rate = DEFAULT_TWI_CLK_HZ; + at91_calc_twi_clock(dev); - 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.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; } - -#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 "); -MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:at91_i2c"); diff --git a/drivers/i2c/busses/i2c-at91-slave.c b/drivers/i2c/busses/i2c-at91-slave.c new file mode 100644 index 000000000000..d6eeea5166c0 --- /dev/null +++ b/drivers/i2c/busses/i2c-at91-slave.c @@ -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 + */ + +#include +#include +#include +#include + +#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); + } +} diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h new file mode 100644 index 000000000000..499b506f6128 --- /dev/null +++ b/drivers/i2c/busses/i2c-at91.h @@ -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 + * + * Evolved from original work by: + * Copyright (C) 2004 Rick Bronson + * Converted to 2.6 by Andrew Victor + * + * Borrowed heavily from original work by: + * Copyright (C) 2000 Philip Edelbrock + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index bf564391091f..1c7b41f45c83 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -99,6 +99,7 @@ * @adapter: core i2c abstraction * @i2c_clk: clock reference for i2c input clock * @bus_clk_rate: current i2c bus clock rate + * @last: a flag indicating is this is last message in transfer */ struct axxia_i2c_dev { void __iomem *base; @@ -112,6 +113,7 @@ struct axxia_i2c_dev { struct i2c_adapter adapter; struct clk *i2c_clk; u32 bus_clk_rate; + bool last; }; 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 */ i2c_int_disable(idev, ~MST_STATUS_TSS); complete(&idev->msg_complete); - } else if (status & MST_STATUS_SNS) { + } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) { /* 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) axxia_i2c_empty_rx_fifo(idev); complete(&idev->msg_complete); - } else if (status & MST_STATUS_SS) { - /* Auto/Sequence transfer done */ - complete(&idev->msg_complete); } else if (status & MST_STATUS_TSS) { /* Transfer timeout */ 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_xfrd = 0; idev->msg_xfrd_r = 0; + idev->last = true; axxia_i2c_fill_tx_fifo(idev); 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, I2C_XFER_TIMEOUT); - i2c_int_disable(idev, int_mask); - - axxia_i2c_empty_rx_fifo(idev); - if (idev->msg_err == -ENXIO) { if (axxia_i2c_handle_seq_nak(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; } -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; unsigned long time_left; 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_xfrd = 0; idev->msg_xfrd_r = 0; + idev->last = last; reinit_completion(&idev->msg_complete); 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) goto out; - /* Start manual mode */ - writel(CMD_MANUAL, idev->base + MST_COMMAND); + if (!last) { + 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); @@ -507,28 +512,6 @@ out: 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 * 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, @@ -558,9 +541,7 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_int_enable(idev, MST_STATUS_TSS); for (i = 0; ret == 0 && i < num; ++i) - ret = axxia_i2c_xfer_msg(idev, &msgs[i]); - - axxia_i2c_stop(idev); + ret = axxia_i2c_xfer_msg(idev, &msgs[i], i == (num - 1)); return ret ? : i; } diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 4c8c3bc4669c..a845b8decac8 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -17,17 +17,38 @@ #include #include #include +#include #include #include +#define IDM_CTRL_DIRECT_OFFSET 0x00 #define CFG_OFFSET 0x00 #define CFG_RESET_SHIFT 31 #define CFG_EN_SHIFT 30 +#define CFG_SLAVE_ADDR_0_SHIFT 28 #define CFG_M_RETRY_CNT_SHIFT 16 #define CFG_M_RETRY_CNT_MASK 0x0f #define TIM_CFG_OFFSET 0x04 #define TIM_CFG_MODE_400_SHIFT 31 +#define TIM_RAND_SLAVE_STRETCH_SHIFT 24 +#define TIM_RAND_SLAVE_STRETCH_MASK 0x7f +#define TIM_PERIODIC_SLAVE_STRETCH_SHIFT 16 +#define TIM_PERIODIC_SLAVE_STRETCH_MASK 0x7f + +#define S_CFG_SMBUS_ADDR_OFFSET 0x08 +#define S_CFG_EN_NIC_SMB_ADDR3_SHIFT 31 +#define S_CFG_NIC_SMB_ADDR3_SHIFT 24 +#define S_CFG_NIC_SMB_ADDR3_MASK 0x7f +#define S_CFG_EN_NIC_SMB_ADDR2_SHIFT 23 +#define S_CFG_NIC_SMB_ADDR2_SHIFT 16 +#define S_CFG_NIC_SMB_ADDR2_MASK 0x7f +#define S_CFG_EN_NIC_SMB_ADDR1_SHIFT 15 +#define S_CFG_NIC_SMB_ADDR1_SHIFT 8 +#define S_CFG_NIC_SMB_ADDR1_MASK 0x7f +#define S_CFG_EN_NIC_SMB_ADDR0_SHIFT 7 +#define S_CFG_NIC_SMB_ADDR0_SHIFT 0 +#define S_CFG_NIC_SMB_ADDR0_MASK 0x7f #define M_FIFO_CTRL_OFFSET 0x0c #define M_FIFO_RX_FLUSH_SHIFT 31 @@ -37,6 +58,14 @@ #define M_FIFO_RX_THLD_SHIFT 8 #define M_FIFO_RX_THLD_MASK 0x3f +#define S_FIFO_CTRL_OFFSET 0x10 +#define S_FIFO_RX_FLUSH_SHIFT 31 +#define S_FIFO_TX_FLUSH_SHIFT 30 +#define S_FIFO_RX_CNT_SHIFT 16 +#define S_FIFO_RX_CNT_MASK 0x7f +#define S_FIFO_RX_THLD_SHIFT 8 +#define S_FIFO_RX_THLD_MASK 0x3f + #define M_CMD_OFFSET 0x30 #define M_CMD_START_BUSY_SHIFT 31 #define M_CMD_STATUS_SHIFT 25 @@ -46,6 +75,8 @@ #define M_CMD_STATUS_NACK_ADDR 0x2 #define M_CMD_STATUS_NACK_DATA 0x3 #define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_STATUS_FIFO_UNDERRUN 0x5 +#define M_CMD_STATUS_RX_FIFO_FULL 0x6 #define M_CMD_PROTOCOL_SHIFT 9 #define M_CMD_PROTOCOL_MASK 0xf #define M_CMD_PROTOCOL_BLK_WR 0x7 @@ -54,17 +85,36 @@ #define M_CMD_RD_CNT_SHIFT 0 #define M_CMD_RD_CNT_MASK 0xff +#define S_CMD_OFFSET 0x34 +#define S_CMD_START_BUSY_SHIFT 31 +#define S_CMD_STATUS_SHIFT 23 +#define S_CMD_STATUS_MASK 0x07 +#define S_CMD_STATUS_SUCCESS 0x0 +#define S_CMD_STATUS_TIMEOUT 0x5 + #define IE_OFFSET 0x38 #define IE_M_RX_FIFO_FULL_SHIFT 31 #define IE_M_RX_THLD_SHIFT 30 #define IE_M_START_BUSY_SHIFT 28 #define IE_M_TX_UNDERRUN_SHIFT 27 +#define IE_S_RX_FIFO_FULL_SHIFT 26 +#define IE_S_RX_THLD_SHIFT 25 +#define IE_S_RX_EVENT_SHIFT 24 +#define IE_S_START_BUSY_SHIFT 23 +#define IE_S_TX_UNDERRUN_SHIFT 22 +#define IE_S_RD_EVENT_SHIFT 21 #define IS_OFFSET 0x3c #define IS_M_RX_FIFO_FULL_SHIFT 31 #define IS_M_RX_THLD_SHIFT 30 #define IS_M_START_BUSY_SHIFT 28 #define IS_M_TX_UNDERRUN_SHIFT 27 +#define IS_S_RX_FIFO_FULL_SHIFT 26 +#define IS_S_RX_THLD_SHIFT 25 +#define IS_S_RX_EVENT_SHIFT 24 +#define IS_S_START_BUSY_SHIFT 23 +#define IS_S_TX_UNDERRUN_SHIFT 22 +#define IS_S_RD_EVENT_SHIFT 21 #define M_TX_OFFSET 0x40 #define M_TX_WR_STATUS_SHIFT 31 @@ -78,19 +128,71 @@ #define M_RX_DATA_SHIFT 0 #define M_RX_DATA_MASK 0xff +#define S_TX_OFFSET 0x48 +#define S_TX_WR_STATUS_SHIFT 31 +#define S_TX_DATA_SHIFT 0 +#define S_TX_DATA_MASK 0xff + +#define S_RX_OFFSET 0x4c +#define S_RX_STATUS_SHIFT 30 +#define S_RX_STATUS_MASK 0x03 +#define S_RX_PEC_ERR_SHIFT 29 +#define S_RX_DATA_SHIFT 0 +#define S_RX_DATA_MASK 0xff + #define I2C_TIMEOUT_MSEC 50000 #define M_TX_RX_FIFO_SIZE 64 +#define M_RX_FIFO_MAX_THLD_VALUE (M_TX_RX_FIFO_SIZE - 1) + +#define M_RX_MAX_READ_LEN 255 +#define M_RX_FIFO_THLD_VALUE 50 + +#define IE_M_ALL_INTERRUPT_SHIFT 27 +#define IE_M_ALL_INTERRUPT_MASK 0x1e + +#define SLAVE_READ_WRITE_BIT_MASK 0x1 +#define SLAVE_READ_WRITE_BIT_SHIFT 0x1 +#define SLAVE_MAX_SIZE_TRANSACTION 64 +#define SLAVE_CLOCK_STRETCH_TIME 25 + +#define IE_S_ALL_INTERRUPT_SHIFT 21 +#define IE_S_ALL_INTERRUPT_MASK 0x3f + +enum i2c_slave_read_status { + I2C_SLAVE_RX_FIFO_EMPTY = 0, + I2C_SLAVE_RX_START, + I2C_SLAVE_RX_DATA, + I2C_SLAVE_RX_END, +}; + +enum i2c_slave_xfer_dir { + I2C_SLAVE_DIR_READ = 0, + I2C_SLAVE_DIR_WRITE, + I2C_SLAVE_DIR_NONE, +}; enum bus_speed_index { I2C_SPD_100K = 0, I2C_SPD_400K, }; +enum bcm_iproc_i2c_type { + IPROC_I2C, + IPROC_I2C_NIC +}; + struct bcm_iproc_i2c_dev { struct device *device; + enum bcm_iproc_i2c_type type; int irq; void __iomem *base; + void __iomem *idm_base; + + u32 ape_addr_mask; + + /* lock for indirect access through IDM */ + spinlock_t idm_lock; struct i2c_adapter adapter; unsigned int bus_speed; @@ -100,68 +202,332 @@ struct bcm_iproc_i2c_dev { struct i2c_msg *msg; + struct i2c_client *slave; + enum i2c_slave_xfer_dir xfer_dir; + /* bytes that have been transferred */ unsigned int tx_bytes; + /* bytes that have been read */ + unsigned int rx_bytes; + unsigned int thld_bytes; }; /* * Can be expanded in the future if more interrupt status bits are utilized */ -#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT)) +#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT)\ + | BIT(IS_M_RX_THLD_SHIFT)) + +#define ISR_MASK_SLAVE (BIT(IS_S_START_BUSY_SHIFT)\ + | BIT(IS_S_RX_EVENT_SHIFT) | BIT(IS_S_RD_EVENT_SHIFT)) + +static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave); +static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave); +static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, + bool enable); + +static inline u32 iproc_i2c_rd_reg(struct bcm_iproc_i2c_dev *iproc_i2c, + u32 offset) +{ + u32 val; + + if (iproc_i2c->idm_base) { + spin_lock(&iproc_i2c->idm_lock); + writel(iproc_i2c->ape_addr_mask, + iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET); + val = readl(iproc_i2c->base + offset); + spin_unlock(&iproc_i2c->idm_lock); + } else { + val = readl(iproc_i2c->base + offset); + } + + return val; +} + +static inline void iproc_i2c_wr_reg(struct bcm_iproc_i2c_dev *iproc_i2c, + u32 offset, u32 val) +{ + if (iproc_i2c->idm_base) { + spin_lock(&iproc_i2c->idm_lock); + writel(iproc_i2c->ape_addr_mask, + iproc_i2c->idm_base + IDM_CTRL_DIRECT_OFFSET); + writel(val, iproc_i2c->base + offset); + spin_unlock(&iproc_i2c->idm_lock); + } else { + writel(val, iproc_i2c->base + offset); + } +} + +static void bcm_iproc_i2c_slave_init( + struct bcm_iproc_i2c_dev *iproc_i2c, bool need_reset) +{ + u32 val; + + if (need_reset) { + /* put controller in reset */ + val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); + val |= BIT(CFG_RESET_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val &= ~(BIT(CFG_RESET_SHIFT)); + iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); + } + + /* flush TX/RX FIFOs */ + val = (BIT(S_FIFO_RX_FLUSH_SHIFT) | BIT(S_FIFO_TX_FLUSH_SHIFT)); + iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val); + + /* Maximum slave stretch time */ + val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); + val &= ~(TIM_RAND_SLAVE_STRETCH_MASK << TIM_RAND_SLAVE_STRETCH_SHIFT); + val |= (SLAVE_CLOCK_STRETCH_TIME << TIM_RAND_SLAVE_STRETCH_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); + + /* Configure the slave address */ + val = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET); + val |= BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT); + val &= ~(S_CFG_NIC_SMB_ADDR3_MASK << S_CFG_NIC_SMB_ADDR3_SHIFT); + val |= (iproc_i2c->slave->addr << S_CFG_NIC_SMB_ADDR3_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, val); + + /* clear all pending slave interrupts */ + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, ISR_MASK_SLAVE); + + /* Enable interrupt register for any READ event */ + val = BIT(IE_S_RD_EVENT_SHIFT); + /* Enable interrupt register to indicate a valid byte in receive fifo */ + val |= BIT(IE_S_RX_EVENT_SHIFT); + /* Enable interrupt register for the Slave BUSY command */ + val |= BIT(IE_S_START_BUSY_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); + + iproc_i2c->xfer_dir = I2C_SLAVE_DIR_NONE; +} + +static void bcm_iproc_i2c_check_slave_status( + struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET); + val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK; + + if (val == S_CMD_STATUS_TIMEOUT) { + dev_err(iproc_i2c->device, "slave random stretch time timeout\n"); + + /* re-initialize i2c for recovery */ + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + bcm_iproc_i2c_slave_init(iproc_i2c, true); + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + } +} + +static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c, + u32 status) +{ + u8 value; + u32 val; + u32 rd_status; + u32 tmp; + + /* Start of transaction. check address and populate the direction */ + if (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_NONE) { + tmp = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET); + rd_status = (tmp >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK; + /* This condition checks whether the request is a new request */ + if (((rd_status == I2C_SLAVE_RX_START) && + (status & BIT(IS_S_RX_EVENT_SHIFT))) || + ((rd_status == I2C_SLAVE_RX_END) && + (status & BIT(IS_S_RD_EVENT_SHIFT)))) { + + /* Last bit is W/R bit. + * If 1 then its a read request(by master). + */ + iproc_i2c->xfer_dir = tmp & SLAVE_READ_WRITE_BIT_MASK; + if (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_WRITE) + i2c_slave_event(iproc_i2c->slave, + I2C_SLAVE_READ_REQUESTED, &value); + else + i2c_slave_event(iproc_i2c->slave, + I2C_SLAVE_WRITE_REQUESTED, &value); + } + } + + /* read request from master */ + if ((status & BIT(IS_S_RD_EVENT_SHIFT)) && + (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_WRITE)) { + i2c_slave_event(iproc_i2c->slave, + I2C_SLAVE_READ_PROCESSED, &value); + iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value); + + val = BIT(S_CMD_START_BUSY_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); + } + + /* write request from master */ + if ((status & BIT(IS_S_RX_EVENT_SHIFT)) && + (iproc_i2c->xfer_dir == I2C_SLAVE_DIR_READ)) { + val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET); + /* Its a write request by Master to Slave. + * We read data present in receive FIFO + */ + value = (u8)((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK); + i2c_slave_event(iproc_i2c->slave, + I2C_SLAVE_WRITE_RECEIVED, &value); + + /* check the status for the last byte of the transaction */ + rd_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK; + if (rd_status == I2C_SLAVE_RX_END) + iproc_i2c->xfer_dir = I2C_SLAVE_DIR_NONE; + + dev_dbg(iproc_i2c->device, "\nread value = 0x%x\n", value); + } + + /* Stop */ + if (status & BIT(IS_S_START_BUSY_SHIFT)) { + i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value); + iproc_i2c->xfer_dir = I2C_SLAVE_DIR_NONE; + } + + /* clear interrupt status */ + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status); + + bcm_iproc_i2c_check_slave_status(iproc_i2c); + return true; +} + +static void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + struct i2c_msg *msg = iproc_i2c->msg; + + /* Read valid data from RX FIFO */ + while (iproc_i2c->rx_bytes < msg->len) { + if (!((iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET) >> M_FIFO_RX_CNT_SHIFT) + & M_FIFO_RX_CNT_MASK)) + break; + + msg->buf[iproc_i2c->rx_bytes] = + (iproc_i2c_rd_reg(iproc_i2c, M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + iproc_i2c->rx_bytes++; + } +} + +static void bcm_iproc_i2c_send(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + struct i2c_msg *msg = iproc_i2c->msg; + unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes; + unsigned int i; + u32 val; + + /* can only fill up to the FIFO size */ + tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE); + for (i = 0; i < tx_bytes; i++) { + /* start from where we left over */ + unsigned int idx = iproc_i2c->tx_bytes + i; + + val = msg->buf[idx]; + + /* mark the last byte */ + if (idx == msg->len - 1) { + val |= BIT(M_TX_WR_STATUS_SHIFT); + + if (iproc_i2c->irq) { + u32 tmp; + + /* + * Since this is the last byte, we should now + * disable TX FIFO underrun interrupt + */ + tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, + tmp); + } + } + + /* load data into TX FIFO */ + iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); + } + + /* update number of transferred bytes */ + iproc_i2c->tx_bytes += tx_bytes; +} + +static void bcm_iproc_i2c_read(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + struct i2c_msg *msg = iproc_i2c->msg; + u32 bytes_left, val; + + bcm_iproc_i2c_read_valid_bytes(iproc_i2c); + bytes_left = msg->len - iproc_i2c->rx_bytes; + if (bytes_left == 0) { + if (iproc_i2c->irq) { + /* finished reading all data, disable rx thld event */ + val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + val &= ~BIT(IS_M_RX_THLD_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); + } + } else if (bytes_left < iproc_i2c->thld_bytes) { + /* set bytes left as threshold */ + val = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET); + val &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT); + val |= (bytes_left << M_FIFO_RX_THLD_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); + iproc_i2c->thld_bytes = bytes_left; + } + /* + * bytes_left >= iproc_i2c->thld_bytes, + * hence no need to change the THRESHOLD SET. + * It will remain as iproc_i2c->thld_bytes itself + */ +} + +static void bcm_iproc_i2c_process_m_event(struct bcm_iproc_i2c_dev *iproc_i2c, + u32 status) +{ + /* TX FIFO is empty and we have more data to send */ + if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) + bcm_iproc_i2c_send(iproc_i2c); + + /* RX FIFO threshold is reached and data needs to be read out */ + if (status & BIT(IS_M_RX_THLD_SHIFT)) + bcm_iproc_i2c_read(iproc_i2c); + + /* transfer is done */ + if (status & BIT(IS_M_START_BUSY_SHIFT)) { + iproc_i2c->xfer_is_done = 1; + if (iproc_i2c->irq) + complete(&iproc_i2c->done); + } +} static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) { struct bcm_iproc_i2c_dev *iproc_i2c = data; - u32 status = readl(iproc_i2c->base + IS_OFFSET); + u32 status = iproc_i2c_rd_reg(iproc_i2c, IS_OFFSET); + bool ret; + u32 sl_status = status & ISR_MASK_SLAVE; + + if (sl_status) { + ret = bcm_iproc_i2c_slave_isr(iproc_i2c, sl_status); + if (ret) + return IRQ_HANDLED; + else + return IRQ_NONE; + } status &= ISR_MASK; - if (!status) return IRQ_NONE; - /* TX FIFO is empty and we have more data to send */ - if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) { - struct i2c_msg *msg = iproc_i2c->msg; - unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes; - unsigned int i; - u32 val; - - /* can only fill up to the FIFO size */ - tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE); - for (i = 0; i < tx_bytes; i++) { - /* start from where we left over */ - unsigned int idx = iproc_i2c->tx_bytes + i; - - val = msg->buf[idx]; - - /* mark the last byte */ - if (idx == msg->len - 1) { - u32 tmp; - - val |= BIT(M_TX_WR_STATUS_SHIFT); - - /* - * Since this is the last byte, we should - * now disable TX FIFO underrun interrupt - */ - tmp = readl(iproc_i2c->base + IE_OFFSET); - tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT); - writel(tmp, iproc_i2c->base + IE_OFFSET); - } - - /* load data into TX FIFO */ - writel(val, iproc_i2c->base + M_TX_OFFSET); - } - /* update number of transferred bytes */ - iproc_i2c->tx_bytes += tx_bytes; - } - - if (status & BIT(IS_M_START_BUSY_SHIFT)) { - iproc_i2c->xfer_is_done = 1; - complete(&iproc_i2c->done); - } - - writel(status, iproc_i2c->base + IS_OFFSET); + /* process all master based events */ + bcm_iproc_i2c_process_m_event(iproc_i2c, status); + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status); return IRQ_HANDLED; } @@ -171,26 +537,29 @@ static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) u32 val; /* put controller in reset */ - val = readl(iproc_i2c->base + CFG_OFFSET); - val |= 1 << CFG_RESET_SHIFT; - val &= ~(1 << CFG_EN_SHIFT); - writel(val, iproc_i2c->base + CFG_OFFSET); + val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); + val |= BIT(CFG_RESET_SHIFT); + val &= ~(BIT(CFG_EN_SHIFT)); + iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); /* wait 100 usec per spec */ udelay(100); /* bring controller out of reset */ - val &= ~(1 << CFG_RESET_SHIFT); - writel(val, iproc_i2c->base + CFG_OFFSET); + val &= ~(BIT(CFG_RESET_SHIFT)); + iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ - val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); - writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + val = (BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT)); + iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); /* disable all interrupts */ - writel(0, iproc_i2c->base + IE_OFFSET); + val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + val &= ~(IE_M_ALL_INTERRUPT_MASK << + IE_M_ALL_INTERRUPT_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); /* clear all pending interrupts */ - writel(0xffffffff, iproc_i2c->base + IS_OFFSET); + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff); return 0; } @@ -200,12 +569,12 @@ static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, { u32 val; - val = readl(iproc_i2c->base + CFG_OFFSET); + val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET); if (enable) val |= BIT(CFG_EN_SHIFT); else val &= ~BIT(CFG_EN_SHIFT); - writel(val, iproc_i2c->base + CFG_OFFSET); + iproc_i2c_wr_reg(iproc_i2c, CFG_OFFSET, val); } static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, @@ -213,7 +582,7 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, { u32 val; - val = readl(iproc_i2c->base + M_CMD_OFFSET); + val = iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET); val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; switch (val) { @@ -236,6 +605,14 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, dev_dbg(iproc_i2c->device, "bus timeout\n"); return -ETIMEDOUT; + case M_CMD_STATUS_FIFO_UNDERRUN: + dev_dbg(iproc_i2c->device, "FIFO under-run\n"); + return -ENXIO; + + case M_CMD_STATUS_RX_FIFO_FULL: + dev_dbg(iproc_i2c->device, "RX FIFO full\n"); + return -ETIMEDOUT; + default: dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); @@ -248,18 +625,76 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, } } +static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg, + u32 cmd) +{ + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC); + u32 val, status; + int ret; + + iproc_i2c_wr_reg(iproc_i2c, M_CMD_OFFSET, cmd); + + if (iproc_i2c->irq) { + time_left = wait_for_completion_timeout(&iproc_i2c->done, + time_left); + /* disable all interrupts */ + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); + /* read it back to flush the write */ + iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + /* make sure the interrupt handler isn't running */ + synchronize_irq(iproc_i2c->irq); + + } else { /* polling mode */ + unsigned long timeout = jiffies + time_left; + + do { + status = iproc_i2c_rd_reg(iproc_i2c, + IS_OFFSET) & ISR_MASK; + bcm_iproc_i2c_process_m_event(iproc_i2c, status); + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status); + + if (time_after(jiffies, timeout)) { + time_left = 0; + break; + } + + cpu_relax(); + cond_resched(); + } while (!iproc_i2c->xfer_is_done); + } + + if (!time_left && !iproc_i2c->xfer_is_done) { + dev_err(iproc_i2c->device, "transaction timed out\n"); + + /* flush both TX/RX FIFOs */ + val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); + return -ETIMEDOUT; + } + + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); + if (ret) { + /* flush both TX/RX FIFOs */ + val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); + return ret; + } + + return 0; +} + static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, struct i2c_msg *msg) { - int ret, i; + int i; u8 addr; - u32 val; + u32 val, tmp, val_intr_en; unsigned int tx_bytes; - unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC); /* check if bus is busy */ - if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) & - BIT(M_CMD_START_BUSY_SHIFT))) { + if (!!(iproc_i2c_rd_reg(iproc_i2c, + M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT))) { dev_warn(iproc_i2c->device, "bus is busy\n"); return -EBUSY; } @@ -268,7 +703,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, /* format and load slave address into the TX FIFO */ addr = i2c_8bit_addr_from_msg(msg); - writel(addr, iproc_i2c->base + M_TX_OFFSET); + iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, addr); /* * For a write transaction, load data into the TX FIFO. Only allow @@ -282,15 +717,17 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, /* mark the last byte */ if (i == msg->len - 1) - val |= 1 << M_TX_WR_STATUS_SHIFT; + val |= BIT(M_TX_WR_STATUS_SHIFT); - writel(val, iproc_i2c->base + M_TX_OFFSET); + iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); } iproc_i2c->tx_bytes = tx_bytes; } /* mark as incomplete before starting the transaction */ - reinit_completion(&iproc_i2c->done); + if (iproc_i2c->irq) + reinit_completion(&iproc_i2c->done); + iproc_i2c->xfer_is_done = 0; /* @@ -298,7 +735,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, * transaction is done, i.e., the internal start_busy bit, transitions * from 1 to 0. */ - val = BIT(IE_M_START_BUSY_SHIFT); + val_intr_en = BIT(IE_M_START_BUSY_SHIFT); /* * If TX data size is larger than the TX FIFO, need to enable TX @@ -307,9 +744,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, */ if (!(msg->flags & I2C_M_RD) && msg->len > iproc_i2c->tx_bytes) - val |= BIT(IE_M_TX_UNDERRUN_SHIFT); - - writel(val, iproc_i2c->base + IE_OFFSET); + val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT); /* * Now we can activate the transfer. For a read operation, specify the @@ -317,54 +752,31 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, */ val = BIT(M_CMD_START_BUSY_SHIFT); if (msg->flags & I2C_M_RD) { + iproc_i2c->rx_bytes = 0; + if (msg->len > M_RX_FIFO_MAX_THLD_VALUE) + iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE; + else + iproc_i2c->thld_bytes = msg->len; + + /* set threshold value */ + tmp = iproc_i2c_rd_reg(iproc_i2c, M_FIFO_CTRL_OFFSET); + tmp &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT); + tmp |= iproc_i2c->thld_bytes << M_FIFO_RX_THLD_SHIFT; + iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, tmp); + + /* enable the RX threshold interrupt */ + val_intr_en |= BIT(IE_M_RX_THLD_SHIFT); + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | (msg->len << M_CMD_RD_CNT_SHIFT); } else { val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); } - writel(val, iproc_i2c->base + M_CMD_OFFSET); - time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); + if (iproc_i2c->irq) + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val_intr_en); - /* disable all interrupts */ - writel(0, iproc_i2c->base + IE_OFFSET); - /* read it back to flush the write */ - readl(iproc_i2c->base + IE_OFFSET); - - /* make sure the interrupt handler isn't running */ - synchronize_irq(iproc_i2c->irq); - - if (!time_left && !iproc_i2c->xfer_is_done) { - dev_err(iproc_i2c->device, "transaction timed out\n"); - - /* flush FIFOs */ - val = (1 << M_FIFO_RX_FLUSH_SHIFT) | - (1 << M_FIFO_TX_FLUSH_SHIFT); - writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); - return -ETIMEDOUT; - } - - ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); - if (ret) { - /* flush both TX/RX FIFOs */ - val = (1 << M_FIFO_RX_FLUSH_SHIFT) | - (1 << M_FIFO_TX_FLUSH_SHIFT); - writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); - return ret; - } - - /* - * For a read operation, we now need to load the data from FIFO - * into the memory buffer - */ - if (msg->flags & I2C_M_RD) { - for (i = 0; i < msg->len; i++) { - msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> - M_RX_DATA_SHIFT) & M_RX_DATA_MASK; - } - } - - return 0; + return bcm_iproc_i2c_xfer_wait(iproc_i2c, msg, val); } static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, @@ -387,17 +799,23 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + u32 val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + + if (adap->algo->reg_slave) + val |= I2C_FUNC_SLAVE; + + return val; } -static const struct i2c_algorithm bcm_iproc_algo = { +static struct i2c_algorithm bcm_iproc_algo = { .master_xfer = bcm_iproc_i2c_xfer, .functionality = bcm_iproc_i2c_functionality, + .reg_slave = bcm_iproc_i2c_reg_slave, + .unreg_slave = bcm_iproc_i2c_unreg_slave, }; -static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = { - /* need to reserve one byte in the FIFO for the slave address */ - .max_read_len = M_TX_RX_FIFO_SIZE - 1, +static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = { + .max_read_len = M_RX_MAX_READ_LEN, }; static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) @@ -425,10 +843,10 @@ static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) } iproc_i2c->bus_speed = bus_speed; - val = readl(iproc_i2c->base + TIM_CFG_OFFSET); - val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); + val &= ~BIT(TIM_CFG_MODE_400_SHIFT); val |= (bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT; - writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); @@ -449,6 +867,8 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, iproc_i2c); iproc_i2c->device = &pdev->dev; + iproc_i2c->type = + (enum bcm_iproc_i2c_type)of_device_get_match_data(&pdev->dev); init_completion(&iproc_i2c->done); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -456,6 +876,29 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) if (IS_ERR(iproc_i2c->base)) return PTR_ERR(iproc_i2c->base); + if (iproc_i2c->type == IPROC_I2C_NIC) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + iproc_i2c->idm_base = devm_ioremap_resource(iproc_i2c->device, + res); + if (IS_ERR(iproc_i2c->idm_base)) + return PTR_ERR(iproc_i2c->idm_base); + + ret = of_property_read_u32(iproc_i2c->device->of_node, + "brcm,ape-hsls-addr-mask", + &iproc_i2c->ape_addr_mask); + if (ret < 0) { + dev_err(iproc_i2c->device, + "'brcm,ape-hsls-addr-mask' missing\n"); + return -EINVAL; + } + + spin_lock_init(&iproc_i2c->idm_lock); + + /* no slave support */ + bcm_iproc_algo.reg_slave = NULL; + bcm_iproc_algo.unreg_slave = NULL; + } + ret = bcm_iproc_i2c_init(iproc_i2c); if (ret) return ret; @@ -465,17 +908,20 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) return ret; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(iproc_i2c->device, "no irq resource\n"); - return irq; - } - iproc_i2c->irq = irq; + if (irq > 0) { + ret = devm_request_irq(iproc_i2c->device, irq, + bcm_iproc_i2c_isr, 0, pdev->name, + iproc_i2c); + if (ret < 0) { + dev_err(iproc_i2c->device, + "unable to request irq %i\n", irq); + return ret; + } - ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, - pdev->name, iproc_i2c); - if (ret < 0) { - dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); - return ret; + iproc_i2c->irq = irq; + } else { + dev_warn(iproc_i2c->device, + "no irq resource, falling back to poll mode\n"); } bcm_iproc_i2c_enable_disable(iproc_i2c, true); @@ -495,10 +941,15 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev) { struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); - /* make sure there's no pending interrupt when we remove the adapter */ - writel(0, iproc_i2c->base + IE_OFFSET); - readl(iproc_i2c->base + IE_OFFSET); - synchronize_irq(iproc_i2c->irq); + if (iproc_i2c->irq) { + /* + * Make sure there's no pending interrupt when we remove the + * adapter + */ + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); + iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + synchronize_irq(iproc_i2c->irq); + } i2c_del_adapter(&iproc_i2c->adapter); bcm_iproc_i2c_enable_disable(iproc_i2c, false); @@ -512,10 +963,15 @@ static int bcm_iproc_i2c_suspend(struct device *dev) { struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); - /* make sure there's no pending interrupt when we go into suspend */ - writel(0, iproc_i2c->base + IE_OFFSET); - readl(iproc_i2c->base + IE_OFFSET); - synchronize_irq(iproc_i2c->irq); + if (iproc_i2c->irq) { + /* + * Make sure there's no pending interrupt when we go into + * suspend + */ + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, 0); + iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + synchronize_irq(iproc_i2c->irq); + } /* now disable the controller */ bcm_iproc_i2c_enable_disable(iproc_i2c, false); @@ -538,10 +994,10 @@ static int bcm_iproc_i2c_resume(struct device *dev) return ret; /* configure to the desired bus speed */ - val = readl(iproc_i2c->base + TIM_CFG_OFFSET); - val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); + val &= ~BIT(TIM_CFG_MODE_400_SHIFT); val |= (iproc_i2c->bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT; - writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + iproc_i2c_wr_reg(iproc_i2c, TIM_CFG_OFFSET, val); bcm_iproc_i2c_enable_disable(iproc_i2c, true); @@ -558,8 +1014,54 @@ static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = { #define BCM_IPROC_I2C_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ + +static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter); + + if (iproc_i2c->slave) + return -EBUSY; + + if (slave->flags & I2C_CLIENT_TEN) + return -EAFNOSUPPORT; + + iproc_i2c->slave = slave; + bcm_iproc_i2c_slave_init(iproc_i2c, false); + return 0; +} + +static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave) +{ + u32 tmp; + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter); + + if (!iproc_i2c->slave) + return -EINVAL; + + iproc_i2c->slave = NULL; + + /* disable all slave interrupts */ + tmp = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + tmp &= ~(IE_S_ALL_INTERRUPT_MASK << + IE_S_ALL_INTERRUPT_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp); + + /* Erase the slave address programmed */ + tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET); + tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET, tmp); + + return 0; +} + static const struct of_device_id bcm_iproc_i2c_of_match[] = { - { .compatible = "brcm,iproc-i2c" }, + { + .compatible = "brcm,iproc-i2c", + .data = (int *)IPROC_I2C, + }, { + .compatible = "brcm,iproc-nic-i2c", + .data = (int *)IPROC_I2C_NIC, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index f4d862234980..506991596b68 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -165,7 +165,6 @@ static const struct bsc_clk_param bsc_clk[] = { struct brcmstb_i2c_dev { struct device *device; void __iomem *base; - void __iomem *irq_base; int irq; struct bsc_regs *bsc_regmap; struct i2c_adapter adapter; diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index a4730111d290..2de7452fcd6d 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -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 ret; + if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); - if (prepare) - return clk_prepare_enable(dev->clk); + if (prepare) { + /* 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->pclk); + return 0; } EXPORT_SYMBOL_GPL(i2c_dw_prepare_clk); diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 6b4ef1d38fb2..67edbbde1070 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -177,6 +177,7 @@ * @base: IO registers pointer * @cmd_complete: tx completion indicator * @clk: input reference clock + * @pclk: clock required to access the registers * @slave: represent an I2C slave device * @cmd_err: run time hadware error code * @msgs: points to an array of messages currently being transferred @@ -227,6 +228,7 @@ struct dw_i2c_dev { void __iomem *ext; struct completion cmd_complete; struct clk *clk; + struct clk *pclk; struct reset_control *rst; struct i2c_client *slave; u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 416f89b8f881..ddfb81872906 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -344,6 +344,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) else 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); if (!i2c_dw_prepare_clk(dev, true)) { u64 clk_khz; diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index bba5c4627de3..9684a0ac2a6d 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -413,6 +413,8 @@ static int i2c_gpio_probe(struct platform_device *pdev) if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl)) 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->setscl = i2c_gpio_setscl_val; diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 06c4c767af32..dc00fabc919a 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -639,8 +639,7 @@ static int lpi2c_imx_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int lpi2c_runtime_suspend(struct device *dev) +static int __maybe_unused lpi2c_runtime_suspend(struct device *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; } -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); int ret; @@ -671,10 +670,6 @@ static const struct dev_pm_ops lpi2c_pm_ops = { SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend, 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 = { .probe = lpi2c_imx_probe, @@ -682,7 +677,7 @@ static struct platform_driver lpi2c_imx_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = lpi2c_imx_of_match, - .pm = IMX_LPI2C_PM, + .pm = &lpi2c_pm_ops, }, }; diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c index 5c754bf659e2..f64c1d72f73f 100644 --- a/drivers/i2c/busses/i2c-isch.c +++ b/drivers/i2c/busses/i2c-isch.c @@ -30,7 +30,6 @@ #include #include #include -#include /* SCH SMBus address offsets */ #define SMBHSTCNT (0 + sch_smba) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 684d651612b3..745b0d03e883 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -35,6 +35,7 @@ #include #define I2C_RS_TRANSFER (1 << 4) +#define I2C_ARB_LOST (1 << 3) #define I2C_HS_NACKERR (1 << 2) #define I2C_ACKERR (1 << 1) #define I2C_TRANSAC_COMP (1 << 0) @@ -76,6 +77,8 @@ #define I2C_CONTROL_DIR_CHANGE (0x1 << 4) #define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5) #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_DRV_NAME "i2c-mt65xx" @@ -106,40 +109,97 @@ enum mtk_trans_op { }; enum I2C_REGS_OFFSET { - OFFSET_DATA_PORT = 0x0, - OFFSET_SLAVE_ADDR = 0x04, - OFFSET_INTR_MASK = 0x08, - OFFSET_INTR_STAT = 0x0c, - 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, + OFFSET_DATA_PORT, + OFFSET_SLAVE_ADDR, + OFFSET_INTR_MASK, + OFFSET_INTR_STAT, + OFFSET_CONTROL, + OFFSET_TRANSFER_LEN, + OFFSET_TRANSAC_LEN, + OFFSET_DELAY_LEN, + OFFSET_TIMING, + OFFSET_START, + OFFSET_EXT_CONF, + OFFSET_FIFO_STAT, + OFFSET_FIFO_THRESH, + OFFSET_FIFO_ADDR_CLR, + OFFSET_IO_CONFIG, + OFFSET_RSV_DEBUG, + OFFSET_HS, + OFFSET_SOFTRESET, + OFFSET_DCM_EN, + OFFSET_PATH_DIR, + OFFSET_DEBUGSTAT, + OFFSET_DEBUGCTRL, + OFFSET_TRANSFER_LEN_AUX, + 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 { const struct i2c_adapter_quirks *quirks; + const u16 *regs; unsigned char pmic_i2c: 1; unsigned char dcm: 1; unsigned char auto_restart: 1; unsigned char aux_len_reg: 1; unsigned char support_33bits: 1; unsigned char timing_adjust: 1; + unsigned char dma_sync: 1; + unsigned char ltiming_adjust: 1; }; struct mtk_i2c { @@ -153,6 +213,7 @@ struct mtk_i2c { struct clk *clk_main; /* main clock for i2c bus */ struct clk *clk_dma; /* DMA clock for i2c via DMA */ struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */ + struct clk *clk_arb; /* Arbitrator clock for i2c */ bool have_pmic; /* can use i2c pins from PMIC */ bool use_push_pull; /* IO config push-pull mode */ @@ -162,6 +223,7 @@ struct mtk_i2c { enum mtk_trans_op op; u16 timing_reg; u16 high_speed_reg; + u16 ltiming_reg; unsigned char auto_restart; bool ignore_restart_irq; 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 = { + .regs = mt_i2c_regs_v1, .pmic_i2c = 0, .dcm = 1, .auto_restart = 1, .aux_len_reg = 1, .support_33bits = 1, .timing_adjust = 1, + .dma_sync = 0, + .ltiming_adjust = 0, }; static const struct mtk_i2c_compatible mt6577_compat = { .quirks = &mt6577_i2c_quirks, + .regs = mt_i2c_regs_v1, .pmic_i2c = 0, .dcm = 1, .auto_restart = 0, .aux_len_reg = 0, .support_33bits = 0, .timing_adjust = 0, + .dma_sync = 0, + .ltiming_adjust = 0, }; static const struct mtk_i2c_compatible mt6589_compat = { .quirks = &mt6577_i2c_quirks, + .regs = mt_i2c_regs_v1, .pmic_i2c = 1, .dcm = 0, .auto_restart = 0, .aux_len_reg = 0, .support_33bits = 0, .timing_adjust = 0, + .dma_sync = 0, + .ltiming_adjust = 0, }; static const struct mtk_i2c_compatible mt7622_compat = { .quirks = &mt7622_i2c_quirks, + .regs = mt_i2c_regs_v1, .pmic_i2c = 0, .dcm = 1, .auto_restart = 1, .aux_len_reg = 1, .support_33bits = 0, .timing_adjust = 0, + .dma_sync = 0, + .ltiming_adjust = 0, }; static const struct mtk_i2c_compatible mt8173_compat = { + .regs = mt_i2c_regs_v1, .pmic_i2c = 0, .dcm = 1, .auto_restart = 1, .aux_len_reg = 1, .support_33bits = 1, .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[] = { @@ -234,10 +323,22 @@ static const struct of_device_id mtk_i2c_of_match[] = { { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, {} }; 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) { int ret; @@ -255,8 +356,18 @@ static int mtk_i2c_clock_enable(struct mtk_i2c *i2c) if (ret) goto err_pmic; } + + if (i2c->clk_arb) { + ret = clk_prepare_enable(i2c->clk_arb); + if (ret) + goto err_arb; + } + return 0; +err_arb: + if (i2c->have_pmic) + clk_disable_unprepare(i2c->clk_pmic); err_pmic: clk_disable_unprepare(i2c->clk_main); err_main: @@ -267,6 +378,9 @@ err_main: static void mtk_i2c_clock_disable(struct mtk_i2c *i2c) { + if (i2c->clk_arb) + clk_disable_unprepare(i2c->clk_arb); + if (i2c->have_pmic) clk_disable_unprepare(i2c->clk_pmic); @@ -278,31 +392,36 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c) { u16 control_reg; - writew(I2C_SOFT_RST, i2c->base + OFFSET_SOFTRESET); + mtk_i2c_writew(i2c, I2C_SOFT_RST, OFFSET_SOFTRESET); /* Set ioconfig */ 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 - 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) - 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) - 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); - writew(i2c->high_speed_reg, i2c->base + OFFSET_HS); + mtk_i2c_writew(i2c, i2c->timing_reg, OFFSET_TIMING); + 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 (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 | I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN; - writew(control_reg, i2c->base + OFFSET_CONTROL); - writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN); + if (i2c->dev_comp->dma_sync) + 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); 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 step_cnt; unsigned int sample_cnt; + unsigned int l_step_cnt; + unsigned int l_sample_cnt; unsigned int target_speed; 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) { /* Set master code speed register */ 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) 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 */ 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 | (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 { ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed, &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 */ i2c->high_speed_reg = I2C_TIME_CLR_VALUE; + + if (i2c->dev_comp->ltiming_adjust) + i2c->ltiming_reg = (sample_cnt << 6) | step_cnt; } 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); - control_reg = readw(i2c->base + OFFSET_CONTROL) & + control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) & ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1)) 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) 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 */ 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 - 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); - writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR); + mtk_i2c_writew(i2c, addr_reg, OFFSET_SLAVE_ADDR); /* Clear interrupt status */ - writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | - I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT); - writew(I2C_FIFO_ADDR_CLR, i2c->base + OFFSET_FIFO_ADDR_CLR); + mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR | + I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_STAT); + + mtk_i2c_writew(i2c, I2C_FIFO_ADDR_CLR, OFFSET_FIFO_ADDR_CLR); /* Enable interrupt */ - writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | - I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK); + mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR | + I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_MASK); /* Set transfer and transaction len */ if (i2c->op == I2C_MASTER_WRRD) { if (i2c->dev_comp->aux_len_reg) { - writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN); - writew((msgs + 1)->len, i2c->base + - OFFSET_TRANSFER_LEN_AUX); + mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN); + mtk_i2c_writew(i2c, (msgs + 1)->len, + OFFSET_TRANSFER_LEN_AUX); } else { - writew(msgs->len | ((msgs + 1)->len) << 8, - i2c->base + OFFSET_TRANSFER_LEN); + mtk_i2c_writew(i2c, msgs->len | ((msgs + 1)->len) << 8, + 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 { - writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN); - writew(num, i2c->base + OFFSET_TRANSAC_LEN); + mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN); + mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN); } /* 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) 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, i2c->adap.timeout); /* Clear interrupt mask */ - writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | - I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK); + mtk_i2c_writew(i2c, ~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | + I2C_ARB_LOST | I2C_TRANSAC_COMP), OFFSET_INTR_MASK); if (i2c->op == I2C_MASTER_WR) { 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) restart_flag = I2C_RS_TRANSFER; - intr_stat = readw(i2c->base + OFFSET_INTR_STAT); - writew(intr_stat, i2c->base + OFFSET_INTR_STAT); + intr_stat = mtk_i2c_readw(i2c, OFFSET_INTR_STAT); + mtk_i2c_writew(i2c, intr_stat, OFFSET_INTR_STAT); /* * 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)) { i2c->ignore_restart_irq = false; i2c->irq_stat = 0; - writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START, - i2c->base + OFFSET_START); + mtk_i2c_writew(i2c, I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | + I2C_TRANSAC_START, OFFSET_START); } else { if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag)) complete(&i2c->msg_complete); @@ -839,6 +968,10 @@ static int mtk_i2c_probe(struct platform_device *pdev) 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; if (i2c->have_pmic) { i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic"); diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 0ed5a41804dc..4f30a43b63da 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -1070,8 +1070,7 @@ static int nmk_i2c_remove(struct amba_device *adev) /* disable the controller */ i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); 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; } diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 4e1a077fb688..c3dabee0aa35 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -26,8 +26,6 @@ #include #include -#define OCORES_FLAG_POLL BIT(0) - /* * 'process_lock' exists because ocores_process() and ocores_process_timeout() * can't run in parallel. @@ -37,7 +35,6 @@ struct ocores_i2c { int iobase; u32 reg_shift; u32 reg_io_width; - unsigned long flags; wait_queue_head_t wait; struct i2c_adapter adap; 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, struct i2c_msg *msgs, int num) { - struct ocores_i2c *i2c = i2c_get_adapdata(adap); - - if (i2c->flags & OCORES_FLAG_POLL) - return ocores_xfer_polling(adap, msgs, num); - return ocores_xfer_core(i2c, msgs, num, false); + return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, false); } 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; } -static const struct i2c_algorithm ocores_algorithm = { +static struct i2c_algorithm ocores_algorithm = { .master_xfer = ocores_xfer, + .master_xfer_atomic = ocores_xfer_polling, .functionality = ocores_func, }; @@ -673,13 +667,13 @@ static int ocores_i2c_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq == -ENXIO) { - i2c->flags |= OCORES_FLAG_POLL; + ocores_algorithm.master_xfer = ocores_xfer_polling; } else { if (irq < 0) 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, pdev->name, i2c); if (ret) { diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index cd9c65f3d404..faa0394048a0 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -269,6 +269,8 @@ static const u8 reg_map_ip_v2[] = { [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, 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); } +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. */ 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); unsigned long timeout; u16 w; + int ret; dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", 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; 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; 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 * into arbitration and we're currently unable to recover from it. */ - timeout = wait_for_completion_timeout(&omap->cmd_complete, - OMAP_I2C_TIMEOUT); + if (!polling) { + 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) { dev_err(omap->dev, "controller timed out\n"); 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. */ 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); 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); 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) break; } @@ -813,6 +841,18 @@ out: 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 omap_i2c_func(struct i2c_adapter *adap) { @@ -1035,10 +1075,8 @@ omap_i2c_isr(int irq, void *dev_id) return ret; } -static irqreturn_t -omap_i2c_isr_thread(int this_irq, void *dev_id) +static int omap_i2c_xfer_data(struct omap_i2c_dev *omap) { - struct omap_i2c_dev *omap = dev_id; u16 bits; u16 stat; int err = 0, count = 0; @@ -1056,7 +1094,8 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) if (!stat) { /* my work here is done */ - goto out; + err = -EAGAIN; + break; } 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); - 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; } 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, }; diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 90946a8b9a75..e9a0514ae166 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -19,6 +19,7 @@ Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800 AMD Hudson-2, ML, CZ + Hygon CZ SMSC Victory66 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->vendor == PCI_VENDOR_ID_AMD && 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; else smb_en = (aux) ? 0x28 : 0x2c; @@ -361,7 +364,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, piix4_smba, i2ccfg >> 4); /* 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) { case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS: 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_AMD, PCI_DEVICE_ID_AMD_HUDSON2_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_ID_SERVERWORKS_OSB4) }, { 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 && dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && 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; 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) { u8 imc; diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index a7578f6da979..d39a4606f72d 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -85,6 +85,7 @@ /* ICFBSCR */ #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_DATA (MDBS | MIE) @@ -398,7 +399,7 @@ static void rcar_i2c_dma_callback(void *data) 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 i2c_msg *msg = priv->msg; @@ -412,9 +413,9 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv) int len; /* Do various checks to see if DMA is feasible at all */ - if (IS_ERR(chan) || msg->len < 8 || !(msg->flags & I2C_M_DMA_SAFE) || - (read && priv->flags & ID_P_NO_RXDMA)) - return; + if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN || + !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA)) + return false; 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); if (dma_mapping_error(chan->device->dev, dma_addr)) { dev_dbg(dev, "dma map failed, using PIO\n"); - return; + return false; } sg_dma_len(&priv->sg) = len; @@ -448,7 +449,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv) if (!txdesc) { dev_dbg(dev, "dma prep slave sg failed, using PIO\n"); rcar_i2c_cleanup_dma(priv); - return; + return false; } 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)) { dev_dbg(dev, "submitting dma failed, using PIO\n"); rcar_i2c_cleanup_dma(priv); - return; + return false; } /* 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); dma_async_issue_pending(chan); + return true; } 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)) return; + /* Check if DMA can be enabled and take over */ + if (priv->pos == 1 && rcar_i2c_dma(priv)) + return; + if (priv->pos < msg->len) { /* * 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]); 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 { /* * 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; 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); if (!priv) return -ENOMEM; diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index b75ff144b570..f31413fd9521 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -43,6 +43,7 @@ #include #include #include +#include #define RIIC_ICCR1 0x00 #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); unsigned long time_left; - int i, ret; + int i; u8 start_bit; - ret = clk_prepare_enable(riic->clk); - if (ret) - return ret; + pm_runtime_get_sync(adap->dev.parent); if (readb(riic->base + RIIC_ICCR2) & ICCR2_BBSY) { riic->err = -EBUSY; @@ -150,7 +149,7 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) } out: - clk_disable_unprepare(riic->clk); + pm_runtime_put(adap->dev.parent); 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) { - int ret; + int ret = 0; unsigned long rate; int total_ticks, cks, brl, brh; - ret = clk_prepare_enable(riic->clk); - if (ret) - return ret; + pm_runtime_get_sync(riic->adapter.dev.parent); if (t->bus_freq_hz > 400000) { dev_err(&riic->adapter.dev, "unsupported bus speed (%dHz). 400000 max\n", t->bus_freq_hz); - clk_disable_unprepare(riic->clk); - return -EINVAL; + ret = -EINVAL; + goto out; } 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)) { dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n", (unsigned long)t->bus_freq_hz); - clk_disable_unprepare(riic->clk); - return -EINVAL; + ret = -EINVAL; + goto out; } 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); - clk_disable_unprepare(riic->clk); - - return 0; +out: + pm_runtime_put(riic->adapter.dev.parent); + return ret; } 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); + pm_runtime_enable(&pdev->dev); + ret = riic_init_hw(riic, &i2c_t); if (ret) - return ret; - + goto out; ret = i2c_add_adapter(adap); if (ret) - return ret; + goto out; platform_set_drvdata(pdev, riic); dev_info(&pdev->dev, "registered with %dHz bus speed\n", i2c_t.bus_freq_hz); return 0; + +out: + pm_runtime_disable(&pdev->dev); + return ret; } static int riic_i2c_remove(struct platform_device *pdev) { struct riic_dev *riic = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); writeb(0, riic->base + RIIC_ICIER); + pm_runtime_put(&pdev->dev); i2c_del_adapter(&riic->adapter); + pm_runtime_disable(&pdev->dev); return 0; } diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 4284fc991cfd..48337bef5b87 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -476,8 +476,12 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, list_add_tail(&v->node, &solutions); + break; } } + + if (p_prev == p) + break; } } diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c index 5503fa171df0..743c161b22c5 100644 --- a/drivers/i2c/busses/i2c-stu300.c +++ b/drivers/i2c/busses/i2c-stu300.c @@ -328,12 +328,6 @@ static int stu300_start_and_await_event(struct stu300_dev *dev, { 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 */ spin_lock_irq(&dev->cmd_issue_lock); init_completion(&dev->cmd_complete); @@ -380,13 +374,6 @@ static int stu300_await_event(struct stu300_dev *dev, { 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? */ spin_lock_irq(&dev->cmd_issue_lock); dev->cmd_err = STU300_ERROR_NONE; @@ -846,6 +833,13 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 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) { /* 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 = { - .master_xfer = stu300_xfer, - .functionality = stu300_func, + .master_xfer = stu300_xfer, + .master_xfer_atomic = stu300_xfer_todo, + .functionality = stu300_func, }; static const struct i2c_adapter_quirks stu300_quirks = { diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c index f6cd35d0a2ac..9bb085793a0c 100644 --- a/drivers/i2c/busses/i2c-tegra-bpmp.c +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -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, struct mrq_i2c_request *request, - struct mrq_i2c_response *response) + struct mrq_i2c_response *response, + bool atomic) { struct tegra_bpmp_message msg; int err; @@ -222,7 +223,7 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c, msg.rx.data = response; msg.rx.size = sizeof(*response); - if (irqs_disabled()) + if (atomic) err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg); else 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; } -static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter, - struct i2c_msg *msgs, int num) +static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num, + bool atomic) { struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter); struct mrq_i2c_response response; @@ -253,7 +255,7 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter, return err; } - err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response); + err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic); if (err < 0) { dev_err(i2c->dev, "failed to transfer message: %d\n", err); return err; @@ -268,6 +270,18 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter, 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) { 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 = { .master_xfer = tegra_bpmp_i2c_xfer, + .master_xfer_atomic = tegra_bpmp_i2c_xfer_atomic, .functionality = tegra_bpmp_i2c_func, }; diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 688aa3b5f3ac..9732a81bb7dd 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1871,8 +1871,10 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (WARN_ON(!msgs || num < 1)) 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)) return -EOPNOTSUPP; @@ -1894,7 +1896,11 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; 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) break; 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 * (discarding status on the first one). */ - if (in_atomic() || irqs_disabled()) { - ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); - if (!ret) - /* I2C activity is ongoing. */ - return -EAGAIN; - } else { - i2c_lock_bus(adap, I2C_LOCK_SEGMENT); - } + ret = __i2c_lock_bus_helper(adap); + if (ret) + return ret; ret = __i2c_transfer(adap, msgs, num); i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 132119112596..788d42f2aad9 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -20,6 +20,8 @@ #include #include +#include "i2c-core.h" + #define CREATE_TRACE_POINTS #include @@ -530,7 +532,10 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, { 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, command, protocol, data); 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, 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; int try; s32 res; + res = __i2c_check_suspended(adapter); + if (res) + return res; + /* If enabled, the following two tracepoints are conditional on * 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; - 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 */ orig_jiffies = jiffies; for (res = 0, try = 0; try <= adapter->retries; try++) { - res = adapter->algo->smbus_xfer(adapter, addr, flags, - read_write, command, - protocol, data); + res = xfer_func(adapter, addr, flags, read_write, + command, protocol, data); if (res != -EAGAIN) break; if (time_after(jiffies, diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index 37576f50fe20..c88cfef81343 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -29,6 +29,42 @@ extern int __i2c_first_dynamic_bus_num; 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 const struct acpi_device_id * i2c_acpi_match_device(const struct acpi_device_id *matches, diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index f330690b4125..603252fa1284 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -310,12 +310,18 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, else 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 (muxc->mux_locked) priv->algo.smbus_xfer = i2c_mux_smbus_xfer; else 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; /* Now fill out new adapter structure */ diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c index 035032e20327..4eecffc26527 100644 --- a/drivers/i2c/muxes/i2c-demux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -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 */ 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; 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; } - priv = devm_kzalloc(&pdev->dev, sizeof(*priv) - + num_chan * sizeof(struct i2c_demux_pinctrl_chan), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, struct_size(priv, chan, num_chan), + GFP_KERNEL); props = devm_kcalloc(&pdev->dev, num_chan, sizeof(*props), GFP_KERNEL); diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 9e75d6b9140b..50e1fb4aedf5 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -22,7 +22,6 @@ #include #include #include -#include #include /* @@ -287,10 +286,8 @@ static int pca9541_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adap = client->adapter; - struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); struct i2c_mux_core *muxc; struct pca9541 *data; - int force; int ret; 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 */ - force = 0; - if (pdata) - force = pdata->modes[0].adap_id; muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), I2C_MUX_ARBITRATOR, pca9541_select_chan, pca9541_release_chan); @@ -320,7 +314,7 @@ static int pca9541_probe(struct i2c_client *client, 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) return ret; diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index bfabf985e830..923aa3a5a3dc 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -46,10 +46,10 @@ #include #include #include -#include #include #include #include +#include #define PCA954X_MAX_NCHANS 8 @@ -85,7 +85,9 @@ struct pca954x { const struct chip_desc *chip; 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 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 i2c_client *client = data->client; + s8 idle_state; - if (!(data->deselect & (1 << chan))) - return 0; + idle_state = READ_ONCE(data->idle_state); + if (idle_state >= 0) + /* Set the mux back to a predetermined channel */ + return pca954x_select_chan(muxc, idle_state); - /* Deselect active channel */ - data->last_chan = 0; - return pca954x_reg_write(muxc->parent, client, data->last_chan); + if (idle_state == MUX_IDLE_DISCONNECT) { + /* Deselect active channel */ + 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) { 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) { struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; int c, irq; + device_remove_file(&client->dev, &dev_attr_idle_state); + if (data->irq) { for (c = 0; c < data->chip->nchans; 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) { struct i2c_adapter *adap = client->adapter; - struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); struct device *dev = &client->dev; struct device_node *np = dev->of_node; bool idle_disconnect_dt; struct gpio_desc *gpio; - int num, force, class; struct i2c_mux_core *muxc; struct pca954x *data; + int num; int ret; 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->idle_state = MUX_IDLE_AS_IS; idle_disconnect_dt = np && of_property_read_bool(np, "i2c-mux-idle-disconnect"); + if (idle_disconnect_dt) + data->idle_state = MUX_IDLE_DISCONNECT; ret = pca954x_irq_setup(muxc); if (ret) @@ -422,24 +485,7 @@ static int pca954x_probe(struct i2c_client *client, /* Now create an adapter for each channel */ for (num = 0; num < data->chip->nchans; num++) { - bool idle_disconnect_pd = false; - - 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); + ret = i2c_mux_add_adapter(muxc, 0, num, 0); if (ret) goto fail_cleanup; } @@ -453,6 +499,12 @@ static int pca954x_probe(struct i2c_client *client, 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", num, data->chip->muxtype == pca954x_ismux ? "mux" : "switch", client->name); diff --git a/include/linux/i2c-algo-bit.h b/include/linux/i2c-algo-bit.h index 69045df78e2d..7fd5575a368f 100644 --- a/include/linux/i2c-algo-bit.h +++ b/include/linux/i2c-algo-bit.h @@ -33,6 +33,7 @@ struct i2c_algo_bit_data { minimum 5 us for standard-mode I2C and SMBus, maximum 50 us for SMBus */ int timeout; /* in jiffies */ + bool can_do_atomic; /* callbacks don't sleep, we can be atomic */ }; int i2c_bit_add_bus(struct i2c_adapter *); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 383510b4f083..be27062f8ed1 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -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 * defined by the msgs array, with num messages available to transfer via * 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 * is not present, then the bus layer will try and convert the SMBus calls * 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 * from the I2C_FUNC_* flags. * @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 * to name two of the most common. * - * The return codes from the @master_xfer field should indicate the type of - * error code that occurred during the transfer, as documented in the kernel - * Documentation file Documentation/i2c/fault-codes. + * The return codes from the @master_xfer{_atomic} fields should indicate the + * type of error code that occurred during the transfer, as documented in the + * Kernel Documentation file Documentation/i2c/fault-codes. */ 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 - smbus_xfer. If set to NULL, the SMBus protocol is simulated - using common I2C messages */ - /* master_xfer should return the number of messages successfully - processed, or a negative value on error */ + /* + * If an adapter algorithm can't do I2C-level access, set master_xfer + * to NULL. If an adapter algorithm can do SMBus access, set + * smbus_xfer. If set to NULL, the SMBus protocol is simulated + * using common I2C messages. + * + * 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 num); - 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 (*master_xfer_atomic)(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num); + 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 */ - u32 (*functionality) (struct i2c_adapter *); + u32 (*functionality)(struct i2c_adapter *adap); #if IS_ENABLED(CONFIG_I2C_SLAVE) int (*reg_slave)(struct i2c_client *client); @@ -682,7 +694,8 @@ struct i2c_adapter { int retries; struct device dev; /* the adapter device */ 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; char name[48]; diff --git a/include/linux/platform_data/pca954x.h b/include/linux/platform_data/pca954x.h deleted file mode 100644 index 1712677d5904..000000000000 --- a/include/linux/platform_data/pca954x.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * pca954x.h - I2C multiplexer/switch support - * - * Copyright (c) 2008-2009 Rodolfo Giometti - * Copyright (c) 2008-2009 Eurotech S.p.A. - * Michael Lawnick - * - * 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 */