Merge branch 'i2c/for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux

Pull i2c updates from Wolfram Sang:

 - API for late atomic transfers (e.g. to shut down via PMIC). We have a
   seperate callback now which is called under clearly defined
   conditions. In-kernel users are converted, too.

 - new driver for the AMD PCIe MP2 I2C controller

 - large refactoring for at91 and bcm-iproc (both gain slave support due
   to this)

 - and a good share of various driver improvements anf fixes

* 'i2c/for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (57 commits)
  dt-bindings: i2c: riic: document r7s9210 support
  i2c: imx-lpi2c: Use __maybe_unused instead of #if CONFIG_PM_SLEEP
  i2c-piix4: Add Hygon Dhyana SMBus support
  i2c: core: apply 'is_suspended' check for SMBus, too
  i2c: core: ratelimit 'transfer when suspended' errors
  i2c: iproc: Change driver to use 'BIT' macro
  i2c: riic: Add Runtime PM support
  i2c: mux: demux-pinctrl: use struct_size() in devm_kzalloc()
  i2c: mux: pca954x: allow management of device idle state via sysfs
  i2c: mux: pca9541: remove support for unused platform data
  i2c: mux: pca954x: remove support for unused platform data
  dt-bindings: i2c: i2c-mtk: add support for MT8516
  i2c: axxia: use auto cmd for last message
  i2c: gpio: flag atomic capability if possible
  i2c: algo: bit: add flag to whitelist atomic transfers
  i2c: stu300: use xfer_atomic callback to bail out early
  i2c: ocores: enable atomic xfers
  i2c: ocores: refactor setup for polling
  i2c: tegra-bpmp: convert to use new atomic callbacks
  i2c: omap: Add the master_xfer_atomic hook
  ...
This commit is contained in:
Linus Torvalds 2019-05-09 14:41:55 -07:00
commit 45182e4e1f
49 changed files with 3163 additions and 944 deletions

View File

@ -0,0 +1,20 @@
What: /sys/bus/i2c/.../idle_state
Date: January 2019
KernelVersion: 5.2
Contact: Robert Shearman <robert.shearman@att.com>
Description:
Value that exists only for mux devices that can be
written to control the behaviour of the multiplexer on
idle. Possible values:
-2 - disconnect on idle, i.e. deselect the last used
channel, which is useful when there is a device
with an address that conflicts with another
device on another mux on the same parent bus.
-1 - leave the mux as-is, which is the most optimal
setting in terms of I2C operations and is the
default mode.
0..<nchans> - set the mux to a predetermined channel,
which is useful if there is one channel that is
used almost always, and you want to reduce the
latency for normal operations after rare
transactions on other channels

View File

@ -50,6 +50,7 @@ Required properties:
"nxp,se97b" - the fallback is "atmel,24c02", "nxp,se97b" - the fallback is "atmel,24c02",
"renesas,r1ex24002" - the fallback is "atmel,24c02" "renesas,r1ex24002" - the fallback is "atmel,24c02"
"renesas,r1ex24016" - the fallback is "atmel,24c16"
"renesas,r1ex24128" - the fallback is "atmel,24c128" "renesas,r1ex24128" - the fallback is "atmel,24c128"
"rohm,br24t01" - the fallback is "atmel,24c01" "rohm,br24t01" - the fallback is "atmel,24c01"

View File

@ -3,15 +3,12 @@ Broadcom iProc I2C controller
Required properties: Required properties:
- compatible: - compatible:
Must be "brcm,iproc-i2c" Must be "brcm,iproc-i2c" or "brcm,iproc-nic-i2c"
- reg: - reg:
Define the base and range of the I/O address space that contain the iProc Define the base and range of the I/O address space that contain the iProc
I2C controller registers I2C controller registers
- interrupts:
Should contain the I2C interrupt
- clock-frequency: - clock-frequency:
This is the I2C bus clock. Need to be either 100000 or 400000 This is the I2C bus clock. Need to be either 100000 or 400000
@ -21,6 +18,18 @@ Required properties:
- #size-cells: - #size-cells:
Always 0 Always 0
Optional properties:
- interrupts:
Should contain the I2C interrupt. For certain revisions of the I2C
controller, I2C interrupt is unwired to the interrupt controller. In such
case, this property should be left unspecified, and driver will fall back
to polling mode
- brcm,ape-hsls-addr-mask:
Required for "brcm,iproc-nic-i2c". Host view of address mask into the
'APE' co-processor. Value must be unsigned, 32-bit
Example: Example:
i2c0: i2c@18008000 { i2c0: i2c@18008000 {
compatible = "brcm,iproc-i2c"; compatible = "brcm,iproc-i2c";

View File

@ -6,12 +6,21 @@ Required properties :
or "mscc,ocelot-i2c" with "snps,designware-i2c" for fallback or "mscc,ocelot-i2c" with "snps,designware-i2c" for fallback
- reg : Offset and length of the register set for the device - reg : Offset and length of the register set for the device
- interrupts : <IRQ> where IRQ is the interrupt number. - interrupts : <IRQ> where IRQ is the interrupt number.
- clocks : phandles for the clocks, see the description of clock-names below.
The phandle for the "ic_clk" clock is required. The phandle for the "pclk"
clock is optional. If a single clock is specified but no clock-name, it is
the "ic_clk" clock. If both clocks are listed, the "ic_clk" must be first.
Recommended properties : Recommended properties :
- clock-frequency : desired I2C bus clock frequency in Hz. - clock-frequency : desired I2C bus clock frequency in Hz.
Optional properties : Optional properties :
- clock-names : Contains the names of the clocks:
"ic_clk", for the core clock used to generate the external I2C clock.
"pclk", the interface clock, required for register access.
- reg : for "mscc,ocelot-i2c", a second register set to configure the SDA hold - reg : for "mscc,ocelot-i2c", a second register set to configure the SDA hold
time, named ICPU_CFG:TWI_DELAY in the datasheet. time, named ICPU_CFG:TWI_DELAY in the datasheet.

View File

@ -12,13 +12,16 @@ Required properties:
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623 "mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629 "mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
"mediatek,mt8173-i2c": for MediaTek MT8173 "mediatek,mt8173-i2c": for MediaTek MT8173
"mediatek,mt8183-i2c": for MediaTek MT8183
"mediatek,mt8516-i2c", "mediatek,mt2712-i2c": for MediaTek MT8516
- reg: physical base address of the controller and dma base, length of memory - reg: physical base address of the controller and dma base, length of memory
mapped region. mapped region.
- interrupts: interrupt number to the cpu. - interrupts: interrupt number to the cpu.
- clock-div: the fixed value for frequency divider of clock source in i2c - clock-div: the fixed value for frequency divider of clock source in i2c
module. Each IC may be different. module. Each IC may be different.
- clocks: clock name from clock manager - clocks: clock name from clock manager
- clock-names: Must include "main" and "dma", if enable have-pmic need include - clock-names: Must include "main" and "dma", "arb" is for multi-master that
one bus has more than two i2c controllers, if enable have-pmic need include
"pmic" extra. "pmic" extra.
Optional properties: Optional properties:

View File

@ -1,7 +1,10 @@
Device tree configuration for Renesas RIIC driver Device tree configuration for Renesas RIIC driver
Required properties: Required properties:
- compatible : "renesas,riic-<soctype>". "renesas,riic-rz" as fallback - compatible :
"renesas,riic-r7s72100" if the device is a part of a R7S72100 SoC.
"renesas,riic-r7s9210" if the device is a part of a R7S9210 SoC.
"renesas,riic-rz" for a generic RZ/A compatible device.
- reg : address start and address range size of device - reg : address start and address range size of device
- interrupts : 8 interrupts (TEI, RI, TI, SPI, STI, NAKI, ALI, TMOI) - interrupts : 8 interrupts (TEI, RI, TI, SPI, STI, NAKI, ALI, TMOI)
- clock-frequency : frequency of bus clock in Hz - clock-frequency : frequency of bus clock in Hz

View File

@ -1,11 +1,11 @@
* I2C controller embedded in STMicroelectronics STM32 I2C platform * I2C controller embedded in STMicroelectronics STM32 I2C platform
Required properties : Required properties:
- compatible : Must be one of the following - compatible: Must be one of the following
- "st,stm32f4-i2c" - "st,stm32f4-i2c"
- "st,stm32f7-i2c" - "st,stm32f7-i2c"
- reg : Offset and length of the register set for the device - reg: Offset and length of the register set for the device
- interrupts : Must contain the interrupt id for I2C event and then the - interrupts: Must contain the interrupt id for I2C event and then the
interrupt id for I2C error. interrupt id for I2C error.
- resets: Must contain the phandle to the reset controller. - resets: Must contain the phandle to the reset controller.
- clocks: Must contain the input clock of the I2C instance. - clocks: Must contain the input clock of the I2C instance.
@ -14,25 +14,26 @@ Required properties :
- #address-cells = <1>; - #address-cells = <1>;
- #size-cells = <0>; - #size-cells = <0>;
Optional properties : Optional properties:
- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified, - clock-frequency: Desired I2C bus clock frequency in Hz. If not specified,
the default 100 kHz frequency will be used. the default 100 kHz frequency will be used.
For STM32F4 SoC Standard-mode and Fast-mode are supported, possible values are For STM32F4 SoC Standard-mode and Fast-mode are supported, possible values are
100000 and 400000. 100000 and 400000.
For STM32F7 SoC, Standard-mode, Fast-mode and Fast-mode Plus are supported, For STM32F7, STM32H7 and STM32MP1 SoCs, Standard-mode, Fast-mode and Fast-mode
possible values are 100000, 400000 and 1000000. Plus are supported, possible values are 100000, 400000 and 1000000.
- i2c-scl-rising-time-ns : Only for STM32F7, I2C SCL Rising time for the board - i2c-scl-rising-time-ns: I2C SCL Rising time for the board (default: 25)
(default: 25) For STM32F7, STM32H7 and STM32MP1 only.
- i2c-scl-falling-time-ns : Only for STM32F7, I2C SCL Falling time for the board - i2c-scl-falling-time-ns: I2C SCL Falling time for the board (default: 10)
(default: 10) For STM32F7, STM32H7 and STM32MP1 only.
I2C Timings are derived from these 2 values I2C Timings are derived from these 2 values
- st,syscfg-fmp: Only for STM32F7, use to set Fast Mode Plus bit within SYSCFG - st,syscfg-fmp: Use to set Fast Mode Plus bit within SYSCFG when Fast Mode
whether Fast Mode Plus speed is selected by slave. Plus speed is selected by slave.
1st cell : phandle to syscfg 1st cell: phandle to syscfg
2nd cell : register offset within SYSCFG 2nd cell: register offset within SYSCFG
3rd cell : register bitmask for FMP bit 3rd cell: register bitmask for FMP bit
For STM32F7, STM32H7 and STM32MP1 only.
Example : Example:
i2c@40005400 { i2c@40005400 {
compatible = "st,stm32f4-i2c"; compatible = "st,stm32f4-i2c";

View File

@ -0,0 +1,23 @@
Kernel driver i2c-amd-mp2
Supported adapters:
* AMD MP2 PCIe interface
Datasheet: not publicly available.
Authors:
Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
Nehal Shah <nehal-bakulchandra.shah@amd.com>
Elie Morisse <syniurge@gmail.com>
Description
-----------
The MP2 is an ARM processor programmed as an I2C controller and communicating
with the x86 host through PCI.
If you see something like this:
03:00.7 MP2 I2C controller: Advanced Micro Devices, Inc. [AMD] Device 15e6
in your 'lspci -v', then this driver is for your device.

View File

@ -15,6 +15,8 @@ Supported adapters:
http://support.amd.com/us/Embedded_TechDocs/44413.pdf http://support.amd.com/us/Embedded_TechDocs/44413.pdf
* AMD Hudson-2, ML, CZ * AMD Hudson-2, ML, CZ
Datasheet: Not publicly available Datasheet: Not publicly available
* Hygon CZ
Datasheet: Not publicly available
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
Datasheet: Publicly available at the SMSC website http://www.smsc.com Datasheet: Publicly available at the SMSC website http://www.smsc.com

View File

@ -826,6 +826,14 @@ F: drivers/gpu/drm/amd/include/vi_structs.h
F: drivers/gpu/drm/amd/include/v9_structs.h F: drivers/gpu/drm/amd/include/v9_structs.h
F: include/uapi/linux/kfd_ioctl.h F: include/uapi/linux/kfd_ioctl.h
AMD MP2 I2C DRIVER
M: Elie Morisse <syniurge@gmail.com>
M: Nehal Shah <nehal-bakulchandra.shah@amd.com>
M: Shyam Sundar S K <shyam-sundar.s-k@amd.com>
L: linux-i2c@vger.kernel.org
S: Maintained
F: drivers/i2c/busses/i2c-amd-mp2*
AMD POWERPLAY AMD POWERPLAY
M: Rex Zhu <rex.zhu@amd.com> M: Rex Zhu <rex.zhu@amd.com>
M: Evan Quan <evan.quan@amd.com> M: Evan Quan <evan.quan@amd.com>
@ -2582,7 +2590,7 @@ F: include/linux/dmaengine.h
F: include/linux/async_tx.h F: include/linux/async_tx.h
AT24 EEPROM DRIVER AT24 EEPROM DRIVER
M: Bartosz Golaszewski <brgl@bgdev.pl> M: Bartosz Golaszewski <bgolaszewski@baylibre.com>
L: linux-i2c@vger.kernel.org L: linux-i2c@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
S: Maintained S: Maintained
@ -10207,7 +10215,8 @@ MICROCHIP I2C DRIVER
M: Ludovic Desroches <ludovic.desroches@microchip.com> M: Ludovic Desroches <ludovic.desroches@microchip.com>
L: linux-i2c@vger.kernel.org L: linux-i2c@vger.kernel.org
S: Supported S: Supported
F: drivers/i2c/busses/i2c-at91.c F: drivers/i2c/busses/i2c-at91.h
F: drivers/i2c/busses/i2c-at91-*.c
MICROCHIP ISC DRIVER MICROCHIP ISC DRIVER
M: Eugen Hristev <eugen.hristev@microchip.com> M: Eugen Hristev <eugen.hristev@microchip.com>

View File

@ -603,6 +603,23 @@ bailout:
return ret; return ret;
} }
/*
* We print a warning when we are not flagged to support atomic transfers but
* will try anyhow. That's what the I2C core would do as well. Sadly, we can't
* modify the algorithm struct at probe time because this struct is exported
* 'const'.
*/
static int bit_xfer_atomic(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
int num)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
if (!adap->can_do_atomic)
dev_warn(&i2c_adap->dev, "not flagged for atomic transfers\n");
return bit_xfer(i2c_adap, msgs, num);
}
static u32 bit_func(struct i2c_adapter *adap) static u32 bit_func(struct i2c_adapter *adap)
{ {
return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL | return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
@ -615,8 +632,9 @@ static u32 bit_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */ /* -----exported algorithm data: ------------------------------------- */
const struct i2c_algorithm i2c_bit_algo = { const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer, .master_xfer = bit_xfer,
.functionality = bit_func, .master_xfer_atomic = bit_xfer_atomic,
.functionality = bit_func,
}; };
EXPORT_SYMBOL(i2c_bit_algo); EXPORT_SYMBOL(i2c_bit_algo);

View File

@ -77,6 +77,16 @@ config I2C_AMD8111
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-amd8111. will be called i2c-amd8111.
config I2C_AMD_MP2
tristate "AMD MP2 PCIe"
depends on PCI && ACPI
help
If you say yes to this option, support will be included for the AMD
MP2 PCIe I2C adapter.
This driver can also be built as modules. If so, the modules will
be called i2c-amd-mp2-pci and i2c-amd-mp2-plat.
config I2C_HIX5HD2 config I2C_HIX5HD2
tristate "Hix5hd2 high-speed I2C driver" tristate "Hix5hd2 high-speed I2C driver"
depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST depends on ARCH_HISI || ARCH_HIX5HD2 || COMPILE_TEST
@ -176,6 +186,7 @@ config I2C_PIIX4
AMD Hudson-2 AMD Hudson-2
AMD ML AMD ML
AMD CZ AMD CZ
Hygon CZ
Serverworks OSB4 Serverworks OSB4
Serverworks CSB5 Serverworks CSB5
Serverworks CSB6 Serverworks CSB6
@ -388,6 +399,19 @@ config I2C_AT91
the latency to fill the transmission register is too long. If you the latency to fill the transmission register is too long. If you
are facing this situation, use the i2c-gpio driver. are facing this situation, use the i2c-gpio driver.
config I2C_AT91_SLAVE_EXPERIMENTAL
tristate "Microchip AT91 I2C experimental slave mode"
depends on I2C_AT91
select I2C_SLAVE
help
If you say yes to this option, support for the slave mode will be
added. Caution: do not use it for production. This feature has not
been tested in a heavy way, help wanted.
There are known bugs:
- It can hang, on a SAMA5D4, after several transfers.
- There are some mismtaches with a SAMA5D4 as slave and a SAMA5D2 as
master.
config I2C_AU1550 config I2C_AU1550
tristate "Au1550/Au1200/Au1300 SMBus interface" tristate "Au1550/Au1200/Au1300 SMBus interface"
depends on MIPS_ALCHEMY depends on MIPS_ALCHEMY
@ -425,6 +449,7 @@ config I2C_BCM_IPROC
tristate "Broadcom iProc I2C controller" tristate "Broadcom iProc I2C controller"
depends on ARCH_BCM_IPROC || COMPILE_TEST depends on ARCH_BCM_IPROC || COMPILE_TEST
default ARCH_BCM_IPROC default ARCH_BCM_IPROC
select I2C_SLAVE
help help
If you say yes to this option, support will be included for the If you say yes to this option, support will be included for the
Broadcom iProc I2C controller. Broadcom iProc I2C controller.

View File

@ -33,8 +33,13 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
# Embedded system I2C/SMBus host controller drivers # Embedded system I2C/SMBus host controller drivers
obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o
obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o
i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o
ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y)
i2c-at91-objs += i2c-at91-slave.o
endif
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o
obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o

View File

@ -0,0 +1,483 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* AMD MP2 PCIe communication driver
*
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
* Elie Morisse <syniurge@gmail.com>
*/
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "i2c-amd-mp2.h"
#include <linux/io-64-nonatomic-lo-hi.h>
static void amd_mp2_c2p_mutex_lock(struct amd_i2c_common *i2c_common)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
/* there is only one data mailbox for two i2c adapters */
mutex_lock(&privdata->c2p_lock);
privdata->c2p_lock_busid = i2c_common->bus_id;
}
static void amd_mp2_c2p_mutex_unlock(struct amd_i2c_common *i2c_common)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
if (unlikely(privdata->c2p_lock_busid != i2c_common->bus_id)) {
dev_warn(ndev_dev(privdata),
"bus %d attempting to unlock C2P locked by bus %d\n",
i2c_common->bus_id, privdata->c2p_lock_busid);
return;
}
mutex_unlock(&privdata->c2p_lock);
}
static int amd_mp2_cmd(struct amd_i2c_common *i2c_common,
union i2c_cmd_base i2c_cmd_base)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
void __iomem *reg;
i2c_common->reqcmd = i2c_cmd_base.s.i2c_cmd;
reg = privdata->mmio + ((i2c_cmd_base.s.bus_id == 1) ?
AMD_C2P_MSG1 : AMD_C2P_MSG0);
writel(i2c_cmd_base.ul, reg);
return 0;
}
int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
union i2c_cmd_base i2c_cmd_base;
dev_dbg(ndev_dev(privdata), "%s id: %d\n", __func__,
i2c_common->bus_id);
i2c_cmd_base.ul = 0;
i2c_cmd_base.s.i2c_cmd = enable ? i2c_enable : i2c_disable;
i2c_cmd_base.s.bus_id = i2c_common->bus_id;
i2c_cmd_base.s.i2c_speed = i2c_common->i2c_speed;
amd_mp2_c2p_mutex_lock(i2c_common);
return amd_mp2_cmd(i2c_common, i2c_cmd_base);
}
EXPORT_SYMBOL_GPL(amd_mp2_bus_enable_set);
static void amd_mp2_cmd_rw_fill(struct amd_i2c_common *i2c_common,
union i2c_cmd_base *i2c_cmd_base,
enum i2c_cmd reqcmd)
{
i2c_cmd_base->s.i2c_cmd = reqcmd;
i2c_cmd_base->s.bus_id = i2c_common->bus_id;
i2c_cmd_base->s.i2c_speed = i2c_common->i2c_speed;
i2c_cmd_base->s.slave_addr = i2c_common->msg->addr;
i2c_cmd_base->s.length = i2c_common->msg->len;
}
int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
union i2c_cmd_base i2c_cmd_base;
amd_mp2_cmd_rw_fill(i2c_common, &i2c_cmd_base, reqcmd);
amd_mp2_c2p_mutex_lock(i2c_common);
if (i2c_common->msg->len <= 32) {
i2c_cmd_base.s.mem_type = use_c2pmsg;
if (reqcmd == i2c_write)
memcpy_toio(privdata->mmio + AMD_C2P_MSG2,
i2c_common->msg->buf,
i2c_common->msg->len);
} else {
i2c_cmd_base.s.mem_type = use_dram;
writeq((u64)i2c_common->dma_addr,
privdata->mmio + AMD_C2P_MSG2);
}
return amd_mp2_cmd(i2c_common, i2c_cmd_base);
}
EXPORT_SYMBOL_GPL(amd_mp2_rw);
static void amd_mp2_pci_check_rw_event(struct amd_i2c_common *i2c_common)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
int len = i2c_common->eventval.r.length;
u32 slave_addr = i2c_common->eventval.r.slave_addr;
bool err = false;
if (unlikely(len != i2c_common->msg->len)) {
dev_err(ndev_dev(privdata),
"length %d in event doesn't match buffer length %d!\n",
len, i2c_common->msg->len);
err = true;
}
if (unlikely(slave_addr != i2c_common->msg->addr)) {
dev_err(ndev_dev(privdata),
"unexpected slave address %x (expected: %x)!\n",
slave_addr, i2c_common->msg->addr);
err = true;
}
if (!err)
i2c_common->cmd_success = true;
}
static void __amd_mp2_process_event(struct amd_i2c_common *i2c_common)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
enum status_type sts = i2c_common->eventval.r.status;
enum response_type res = i2c_common->eventval.r.response;
int len = i2c_common->eventval.r.length;
if (res != command_success) {
if (res != command_failed)
dev_err(ndev_dev(privdata), "invalid response to i2c command!\n");
return;
}
switch (i2c_common->reqcmd) {
case i2c_read:
if (sts == i2c_readcomplete_event) {
amd_mp2_pci_check_rw_event(i2c_common);
if (len <= 32)
memcpy_fromio(i2c_common->msg->buf,
privdata->mmio + AMD_C2P_MSG2,
len);
} else if (sts != i2c_readfail_event) {
dev_err(ndev_dev(privdata),
"invalid i2c status after read (%d)!\n", sts);
}
break;
case i2c_write:
if (sts == i2c_writecomplete_event)
amd_mp2_pci_check_rw_event(i2c_common);
else if (sts != i2c_writefail_event)
dev_err(ndev_dev(privdata),
"invalid i2c status after write (%d)!\n", sts);
break;
case i2c_enable:
if (sts == i2c_busenable_complete)
i2c_common->cmd_success = true;
else if (sts != i2c_busenable_failed)
dev_err(ndev_dev(privdata),
"invalid i2c status after bus enable (%d)!\n",
sts);
break;
case i2c_disable:
if (sts == i2c_busdisable_complete)
i2c_common->cmd_success = true;
else if (sts != i2c_busdisable_failed)
dev_err(ndev_dev(privdata),
"invalid i2c status after bus disable (%d)!\n",
sts);
break;
default:
break;
}
}
void amd_mp2_process_event(struct amd_i2c_common *i2c_common)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
if (unlikely(i2c_common->reqcmd == i2c_none)) {
dev_warn(ndev_dev(privdata),
"received msg but no cmd was sent (bus = %d)!\n",
i2c_common->bus_id);
return;
}
__amd_mp2_process_event(i2c_common);
i2c_common->reqcmd = i2c_none;
amd_mp2_c2p_mutex_unlock(i2c_common);
}
EXPORT_SYMBOL_GPL(amd_mp2_process_event);
static irqreturn_t amd_mp2_irq_isr(int irq, void *dev)
{
struct amd_mp2_dev *privdata = dev;
struct amd_i2c_common *i2c_common;
u32 val;
unsigned int bus_id;
void __iomem *reg;
enum irqreturn ret = IRQ_NONE;
for (bus_id = 0; bus_id < 2; bus_id++) {
i2c_common = privdata->busses[bus_id];
if (!i2c_common)
continue;
reg = privdata->mmio + ((bus_id == 0) ?
AMD_P2C_MSG1 : AMD_P2C_MSG2);
val = readl(reg);
if (val != 0) {
writel(0, reg);
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
i2c_common->eventval.ul = val;
i2c_common->cmd_completion(i2c_common);
ret = IRQ_HANDLED;
}
}
if (ret != IRQ_HANDLED) {
val = readl(privdata->mmio + AMD_P2C_MSG_INTEN);
if (val != 0) {
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
dev_warn(ndev_dev(privdata),
"received irq without message\n");
ret = IRQ_HANDLED;
}
}
return ret;
}
void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common)
{
i2c_common->reqcmd = i2c_none;
amd_mp2_c2p_mutex_unlock(i2c_common);
}
EXPORT_SYMBOL_GPL(amd_mp2_rw_timeout);
int amd_mp2_register_cb(struct amd_i2c_common *i2c_common)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
if (i2c_common->bus_id > 1)
return -EINVAL;
if (privdata->busses[i2c_common->bus_id]) {
dev_err(ndev_dev(privdata),
"Bus %d already taken!\n", i2c_common->bus_id);
return -EINVAL;
}
privdata->busses[i2c_common->bus_id] = i2c_common;
return 0;
}
EXPORT_SYMBOL_GPL(amd_mp2_register_cb);
int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common)
{
struct amd_mp2_dev *privdata = i2c_common->mp2_dev;
privdata->busses[i2c_common->bus_id] = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(amd_mp2_unregister_cb);
static void amd_mp2_clear_reg(struct amd_mp2_dev *privdata)
{
int reg;
for (reg = AMD_C2P_MSG0; reg <= AMD_C2P_MSG9; reg += 4)
writel(0, privdata->mmio + reg);
for (reg = AMD_P2C_MSG1; reg <= AMD_P2C_MSG2; reg += 4)
writel(0, privdata->mmio + reg);
}
static int amd_mp2_pci_init(struct amd_mp2_dev *privdata,
struct pci_dev *pci_dev)
{
int rc;
pci_set_drvdata(pci_dev, privdata);
rc = pcim_enable_device(pci_dev);
if (rc) {
dev_err(ndev_dev(privdata), "Failed to enable MP2 PCI device\n");
goto err_pci_enable;
}
rc = pcim_iomap_regions(pci_dev, 1 << 2, pci_name(pci_dev));
if (rc) {
dev_err(ndev_dev(privdata), "I/O memory remapping failed\n");
goto err_pci_enable;
}
privdata->mmio = pcim_iomap_table(pci_dev)[2];
pci_set_master(pci_dev);
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));
if (rc) {
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
if (rc)
goto err_dma_mask;
}
/* Set up intx irq */
writel(0, privdata->mmio + AMD_P2C_MSG_INTEN);
pci_intx(pci_dev, 1);
rc = devm_request_irq(&pci_dev->dev, pci_dev->irq, amd_mp2_irq_isr,
IRQF_SHARED, dev_name(&pci_dev->dev), privdata);
if (rc)
dev_err(&pci_dev->dev, "Failure requesting irq %i: %d\n",
pci_dev->irq, rc);
return rc;
err_dma_mask:
pci_clear_master(pci_dev);
err_pci_enable:
pci_set_drvdata(pci_dev, NULL);
return rc;
}
static int amd_mp2_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
{
struct amd_mp2_dev *privdata;
int rc;
privdata = devm_kzalloc(&pci_dev->dev, sizeof(*privdata), GFP_KERNEL);
if (!privdata)
return -ENOMEM;
rc = amd_mp2_pci_init(privdata, pci_dev);
if (rc)
return rc;
mutex_init(&privdata->c2p_lock);
privdata->pci_dev = pci_dev;
pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
pm_runtime_use_autosuspend(&pci_dev->dev);
pm_runtime_put_autosuspend(&pci_dev->dev);
pm_runtime_allow(&pci_dev->dev);
privdata->probed = true;
dev_info(&pci_dev->dev, "MP2 device registered.\n");
return 0;
}
static void amd_mp2_pci_remove(struct pci_dev *pci_dev)
{
struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
pm_runtime_forbid(&pci_dev->dev);
pm_runtime_get_noresume(&pci_dev->dev);
pci_intx(pci_dev, 0);
pci_clear_master(pci_dev);
amd_mp2_clear_reg(privdata);
}
#ifdef CONFIG_PM
static int amd_mp2_pci_suspend(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
struct amd_i2c_common *i2c_common;
unsigned int bus_id;
int ret = 0;
for (bus_id = 0; bus_id < 2; bus_id++) {
i2c_common = privdata->busses[bus_id];
if (i2c_common)
i2c_common->suspend(i2c_common);
}
ret = pci_save_state(pci_dev);
if (ret) {
dev_err(ndev_dev(privdata),
"pci_save_state failed = %d\n", ret);
return ret;
}
pci_disable_device(pci_dev);
return ret;
}
static int amd_mp2_pci_resume(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct amd_mp2_dev *privdata = pci_get_drvdata(pci_dev);
struct amd_i2c_common *i2c_common;
unsigned int bus_id;
int ret = 0;
pci_restore_state(pci_dev);
ret = pci_enable_device(pci_dev);
if (ret < 0) {
dev_err(ndev_dev(privdata),
"pci_enable_device failed = %d\n", ret);
return ret;
}
for (bus_id = 0; bus_id < 2; bus_id++) {
i2c_common = privdata->busses[bus_id];
if (i2c_common) {
ret = i2c_common->resume(i2c_common);
if (ret < 0)
return ret;
}
}
return ret;
}
static UNIVERSAL_DEV_PM_OPS(amd_mp2_pci_pm_ops, amd_mp2_pci_suspend,
amd_mp2_pci_resume, NULL);
#endif /* CONFIG_PM */
static const struct pci_device_id amd_mp2_pci_tbl[] = {
{PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2)},
{0}
};
MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
static struct pci_driver amd_mp2_pci_driver = {
.name = "i2c_amd_mp2",
.id_table = amd_mp2_pci_tbl,
.probe = amd_mp2_pci_probe,
.remove = amd_mp2_pci_remove,
#ifdef CONFIG_PM
.driver = {
.pm = &amd_mp2_pci_pm_ops,
},
#endif
};
module_pci_driver(amd_mp2_pci_driver);
static int amd_mp2_device_match(struct device *dev, void *data)
{
return 1;
}
struct amd_mp2_dev *amd_mp2_find_device(void)
{
struct device *dev;
struct pci_dev *pci_dev;
dev = driver_find_device(&amd_mp2_pci_driver.driver, NULL, NULL,
amd_mp2_device_match);
if (!dev)
return NULL;
pci_dev = to_pci_dev(dev);
return (struct amd_mp2_dev *)pci_get_drvdata(pci_dev);
}
EXPORT_SYMBOL_GPL(amd_mp2_find_device);
MODULE_DESCRIPTION("AMD(R) PCI-E MP2 I2C Controller Driver");
MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
MODULE_LICENSE("Dual BSD/GPL");

View File

@ -0,0 +1,367 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* AMD MP2 platform driver
*
* Setup the I2C adapters enumerated in the ACPI namespace.
* MP2 controllers have 2 separate busses, up to 2 I2C adapters may be listed.
*
* Authors: Nehal Bakulchandra Shah <Nehal-bakulchandra.shah@amd.com>
* Elie Morisse <syniurge@gmail.com>
*/
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "i2c-amd-mp2.h"
#define AMD_MP2_I2C_MAX_RW_LENGTH ((1 << 12) - 1)
#define AMD_I2C_TIMEOUT (msecs_to_jiffies(250))
/**
* struct amd_i2c_dev - MP2 bus/i2c adapter context
* @common: shared context with the MP2 PCI driver
* @pdev: platform driver node
* @adap: i2c adapter
* @cmd_complete: xfer completion object
*/
struct amd_i2c_dev {
struct amd_i2c_common common;
struct platform_device *pdev;
struct i2c_adapter adap;
struct completion cmd_complete;
};
#define amd_i2c_dev_common(__common) \
container_of(__common, struct amd_i2c_dev, common)
static int i2c_amd_dma_map(struct amd_i2c_common *i2c_common)
{
struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
enum dma_data_direction dma_direction =
i2c_common->msg->flags & I2C_M_RD ?
DMA_FROM_DEVICE : DMA_TO_DEVICE;
i2c_common->dma_buf = i2c_get_dma_safe_msg_buf(i2c_common->msg, 0);
i2c_common->dma_addr = dma_map_single(dev_pci, i2c_common->dma_buf,
i2c_common->msg->len,
dma_direction);
if (unlikely(dma_mapping_error(dev_pci, i2c_common->dma_addr))) {
dev_err(&i2c_dev->pdev->dev,
"Error while mapping dma buffer %p\n",
i2c_common->dma_buf);
return -EIO;
}
return 0;
}
static void i2c_amd_dma_unmap(struct amd_i2c_common *i2c_common)
{
struct device *dev_pci = &i2c_common->mp2_dev->pci_dev->dev;
enum dma_data_direction dma_direction =
i2c_common->msg->flags & I2C_M_RD ?
DMA_FROM_DEVICE : DMA_TO_DEVICE;
dma_unmap_single(dev_pci, i2c_common->dma_addr,
i2c_common->msg->len, dma_direction);
i2c_put_dma_safe_msg_buf(i2c_common->dma_buf, i2c_common->msg, true);
}
static void i2c_amd_start_cmd(struct amd_i2c_dev *i2c_dev)
{
struct amd_i2c_common *i2c_common = &i2c_dev->common;
reinit_completion(&i2c_dev->cmd_complete);
i2c_common->cmd_success = false;
}
static void i2c_amd_cmd_completion(struct amd_i2c_common *i2c_common)
{
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
union i2c_event *event = &i2c_common->eventval;
if (event->r.status == i2c_readcomplete_event)
dev_dbg(&i2c_dev->pdev->dev, "%s readdata:%*ph\n",
__func__, event->r.length,
i2c_common->msg->buf);
complete(&i2c_dev->cmd_complete);
}
static int i2c_amd_check_cmd_completion(struct amd_i2c_dev *i2c_dev)
{
struct amd_i2c_common *i2c_common = &i2c_dev->common;
unsigned long timeout;
timeout = wait_for_completion_timeout(&i2c_dev->cmd_complete,
i2c_dev->adap.timeout);
if ((i2c_common->reqcmd == i2c_read ||
i2c_common->reqcmd == i2c_write) &&
i2c_common->msg->len > 32)
i2c_amd_dma_unmap(i2c_common);
if (timeout == 0) {
amd_mp2_rw_timeout(i2c_common);
return -ETIMEDOUT;
}
amd_mp2_process_event(i2c_common);
if (!i2c_common->cmd_success)
return -EIO;
return 0;
}
static int i2c_amd_enable_set(struct amd_i2c_dev *i2c_dev, bool enable)
{
struct amd_i2c_common *i2c_common = &i2c_dev->common;
i2c_amd_start_cmd(i2c_dev);
amd_mp2_bus_enable_set(i2c_common, enable);
return i2c_amd_check_cmd_completion(i2c_dev);
}
static int i2c_amd_xfer_msg(struct amd_i2c_dev *i2c_dev, struct i2c_msg *pmsg)
{
struct amd_i2c_common *i2c_common = &i2c_dev->common;
i2c_amd_start_cmd(i2c_dev);
i2c_common->msg = pmsg;
if (pmsg->len > 32)
if (i2c_amd_dma_map(i2c_common))
return -EIO;
if (pmsg->flags & I2C_M_RD)
amd_mp2_rw(i2c_common, i2c_read);
else
amd_mp2_rw(i2c_common, i2c_write);
return i2c_amd_check_cmd_completion(i2c_dev);
}
static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct amd_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
int i;
struct i2c_msg *pmsg;
int err;
/* the adapter might have been deleted while waiting for the bus lock */
if (unlikely(!i2c_dev->common.mp2_dev))
return -EINVAL;
amd_mp2_pm_runtime_get(i2c_dev->common.mp2_dev);
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
err = i2c_amd_xfer_msg(i2c_dev, pmsg);
if (err)
break;
}
amd_mp2_pm_runtime_put(i2c_dev->common.mp2_dev);
return err ? err : num;
}
static u32 i2c_amd_func(struct i2c_adapter *a)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm i2c_amd_algorithm = {
.master_xfer = i2c_amd_xfer,
.functionality = i2c_amd_func,
};
#ifdef CONFIG_PM
static int i2c_amd_suspend(struct amd_i2c_common *i2c_common)
{
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
i2c_amd_enable_set(i2c_dev, false);
return 0;
}
static int i2c_amd_resume(struct amd_i2c_common *i2c_common)
{
struct amd_i2c_dev *i2c_dev = amd_i2c_dev_common(i2c_common);
return i2c_amd_enable_set(i2c_dev, true);
}
#endif
static enum speed_enum i2c_amd_get_bus_speed(struct platform_device *pdev)
{
u32 acpi_speed;
int i;
static const u32 supported_speeds[] = {
0, 100000, 400000, 1000000, 1400000, 3400000
};
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
/* round down to the lowest standard speed */
for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) {
if (acpi_speed < supported_speeds[i])
break;
}
acpi_speed = supported_speeds[i - 1];
switch (acpi_speed) {
case 100000:
return speed100k;
case 400000:
return speed400k;
case 1000000:
return speed1000k;
case 1400000:
return speed1400k;
case 3400000:
return speed3400k;
default:
return speed400k;
}
}
static const struct i2c_adapter_quirks amd_i2c_dev_quirks = {
.max_read_len = AMD_MP2_I2C_MAX_RW_LENGTH,
.max_write_len = AMD_MP2_I2C_MAX_RW_LENGTH,
};
static int i2c_amd_probe(struct platform_device *pdev)
{
int ret;
struct amd_i2c_dev *i2c_dev;
acpi_handle handle = ACPI_HANDLE(&pdev->dev);
struct acpi_device *adev;
struct amd_mp2_dev *mp2_dev;
const char *uid;
if (acpi_bus_get_device(handle, &adev))
return -ENODEV;
/* The ACPI namespace doesn't contain information about which MP2 PCI
* device an AMDI0011 ACPI device is related to, so assume that there's
* only one MP2 PCI device per system.
*/
mp2_dev = amd_mp2_find_device();
if (!mp2_dev || !mp2_dev->probed)
/* The MP2 PCI device should get probed later */
return -EPROBE_DEFER;
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return -ENOMEM;
i2c_dev->common.mp2_dev = mp2_dev;
i2c_dev->pdev = pdev;
platform_set_drvdata(pdev, i2c_dev);
i2c_dev->common.cmd_completion = &i2c_amd_cmd_completion;
#ifdef CONFIG_PM
i2c_dev->common.suspend = &i2c_amd_suspend;
i2c_dev->common.resume = &i2c_amd_resume;
#endif
uid = adev->pnp.unique_id;
if (!uid) {
dev_err(&pdev->dev, "missing UID/bus id!\n");
return -EINVAL;
} else if (strcmp(uid, "0") == 0) {
i2c_dev->common.bus_id = 0;
} else if (strcmp(uid, "1") == 0) {
i2c_dev->common.bus_id = 1;
} else {
dev_err(&pdev->dev, "incorrect UID/bus id \"%s\"!\n", uid);
return -EINVAL;
}
dev_dbg(&pdev->dev, "bus id is %u\n", i2c_dev->common.bus_id);
/* Register the adapter */
amd_mp2_pm_runtime_get(mp2_dev);
i2c_dev->common.reqcmd = i2c_none;
if (amd_mp2_register_cb(&i2c_dev->common))
return -EINVAL;
device_link_add(&i2c_dev->pdev->dev, &mp2_dev->pci_dev->dev,
DL_FLAG_AUTOREMOVE_CONSUMER);
i2c_dev->common.i2c_speed = i2c_amd_get_bus_speed(pdev);
/* Setup i2c adapter description */
i2c_dev->adap.owner = THIS_MODULE;
i2c_dev->adap.algo = &i2c_amd_algorithm;
i2c_dev->adap.quirks = &amd_i2c_dev_quirks;
i2c_dev->adap.dev.parent = &pdev->dev;
i2c_dev->adap.algo_data = i2c_dev;
i2c_dev->adap.timeout = AMD_I2C_TIMEOUT;
ACPI_COMPANION_SET(&i2c_dev->adap.dev, ACPI_COMPANION(&pdev->dev));
i2c_dev->adap.dev.of_node = pdev->dev.of_node;
snprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),
"AMD MP2 i2c bus %u", i2c_dev->common.bus_id);
i2c_set_adapdata(&i2c_dev->adap, i2c_dev);
init_completion(&i2c_dev->cmd_complete);
/* Enable the bus */
if (i2c_amd_enable_set(i2c_dev, true))
dev_err(&pdev->dev, "initial bus enable failed\n");
/* Attach to the i2c layer */
ret = i2c_add_adapter(&i2c_dev->adap);
amd_mp2_pm_runtime_put(mp2_dev);
if (ret < 0)
dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
return ret;
}
static int i2c_amd_remove(struct platform_device *pdev)
{
struct amd_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
struct amd_i2c_common *i2c_common = &i2c_dev->common;
i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
i2c_amd_enable_set(i2c_dev, false);
amd_mp2_unregister_cb(i2c_common);
i2c_common->mp2_dev = NULL;
i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
i2c_del_adapter(&i2c_dev->adap);
return 0;
}
static const struct acpi_device_id i2c_amd_acpi_match[] = {
{ "AMDI0011" },
{ },
};
MODULE_DEVICE_TABLE(acpi, i2c_amd_acpi_match);
static struct platform_driver i2c_amd_plat_driver = {
.probe = i2c_amd_probe,
.remove = i2c_amd_remove,
.driver = {
.name = "i2c_amd_mp2",
.acpi_match_table = ACPI_PTR(i2c_amd_acpi_match),
},
};
module_platform_driver(i2c_amd_plat_driver);
MODULE_DESCRIPTION("AMD(R) MP2 I2C Platform Driver");
MODULE_AUTHOR("Nehal Shah <nehal-bakulchandra.shah@amd.com>");
MODULE_AUTHOR("Elie Morisse <syniurge@gmail.com>");
MODULE_LICENSE("Dual BSD/GPL");

View File

@ -0,0 +1,219 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* AMD MP2 I2C adapter driver
*
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
* Elie Morisse <syniurge@gmail.com>
*/
#ifndef I2C_AMD_PCI_MP2_H
#define I2C_AMD_PCI_MP2_H
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#define PCI_DEVICE_ID_AMD_MP2 0x15E6
struct amd_i2c_common;
struct amd_mp2_dev;
enum {
/* MP2 C2P Message Registers */
AMD_C2P_MSG0 = 0x10500, /* MP2 Message for I2C0 */
AMD_C2P_MSG1 = 0x10504, /* MP2 Message for I2C1 */
AMD_C2P_MSG2 = 0x10508, /* DRAM Address Lo / Data 0 */
AMD_C2P_MSG3 = 0x1050c, /* DRAM Address HI / Data 1 */
AMD_C2P_MSG4 = 0x10510, /* Data 2 */
AMD_C2P_MSG5 = 0x10514, /* Data 3 */
AMD_C2P_MSG6 = 0x10518, /* Data 4 */
AMD_C2P_MSG7 = 0x1051c, /* Data 5 */
AMD_C2P_MSG8 = 0x10520, /* Data 6 */
AMD_C2P_MSG9 = 0x10524, /* Data 7 */
/* MP2 P2C Message Registers */
AMD_P2C_MSG0 = 0x10680, /* Do not use */
AMD_P2C_MSG1 = 0x10684, /* I2C0 interrupt register */
AMD_P2C_MSG2 = 0x10688, /* I2C1 interrupt register */
AMD_P2C_MSG3 = 0x1068C, /* MP2 debug info */
AMD_P2C_MSG_INTEN = 0x10690, /* MP2 interrupt gen register */
AMD_P2C_MSG_INTSTS = 0x10694, /* Interrupt status */
};
/* Command register data structures */
#define i2c_none (-1)
enum i2c_cmd {
i2c_read = 0,
i2c_write,
i2c_enable,
i2c_disable,
number_of_sensor_discovered,
is_mp2_active,
invalid_cmd = 0xF,
};
enum speed_enum {
speed100k = 0,
speed400k = 1,
speed1000k = 2,
speed1400k = 3,
speed3400k = 4
};
enum mem_type {
use_dram = 0,
use_c2pmsg = 1,
};
/**
* union i2c_cmd_base : bit access of C2P commands
* @i2c_cmd: bit 0..3 i2c R/W command
* @bus_id: bit 4..7 i2c bus index
* @slave_addr: bit 8..15 slave address
* @length: bit 16..27 read/write length
* @i2c_speed: bit 28..30 bus speed
* @mem_type: bit 31 0-DRAM; 1-C2P msg o/p
*/
union i2c_cmd_base {
u32 ul;
struct {
enum i2c_cmd i2c_cmd : 4;
u8 bus_id : 4;
u32 slave_addr : 8;
u32 length : 12;
enum speed_enum i2c_speed : 3;
enum mem_type mem_type : 1;
} s;
};
enum response_type {
invalid_response = 0,
command_success = 1,
command_failed = 2,
};
enum status_type {
i2c_readcomplete_event = 0,
i2c_readfail_event = 1,
i2c_writecomplete_event = 2,
i2c_writefail_event = 3,
i2c_busenable_complete = 4,
i2c_busenable_failed = 5,
i2c_busdisable_complete = 6,
i2c_busdisable_failed = 7,
invalid_data_length = 8,
invalid_slave_address = 9,
invalid_i2cbus_id = 10,
invalid_dram_addr = 11,
invalid_command = 12,
mp2_active = 13,
numberof_sensors_discovered_resp = 14,
i2c_bus_notinitialized
};
/**
* union i2c_event : bit access of P2C events
* @response: bit 0..1 i2c response type
* @status: bit 2..6 status_type
* @mem_type: bit 7 0-DRAM; 1-C2P msg o/p
* @bus_id: bit 8..11 i2c bus id
* @length: bit 12..23 message length
* @slave_addr: bit 24-31 slave address
*/
union i2c_event {
u32 ul;
struct {
enum response_type response : 2;
enum status_type status : 5;
enum mem_type mem_type : 1;
u8 bus_id : 4;
u32 length : 12;
u32 slave_addr : 8;
} r;
};
/**
* struct amd_i2c_common - per bus/i2c adapter context, shared
* between the pci and the platform driver
* @eventval: MP2 event value set by the IRQ handler
* @mp2_dev: MP2 pci device this adapter is part of
* @msg: i2c message
* @cmd_completion: function called by the IRQ handler to signal
* the platform driver
* @reqcmd: requested i2c command type
* @cmd_success: set to true if the MP2 responded to a command with
* the expected status and response type
* @bus_id: bus index
* @i2c_speed: i2c bus speed determined by the slowest slave
* @dma_buf: if msg length > 32, holds the DMA buffer virtual address
* @dma_addr: if msg length > 32, holds the DMA buffer address
*/
struct amd_i2c_common {
union i2c_event eventval;
struct amd_mp2_dev *mp2_dev;
struct i2c_msg *msg;
void (*cmd_completion)(struct amd_i2c_common *i2c_common);
enum i2c_cmd reqcmd;
u8 cmd_success;
u8 bus_id;
enum speed_enum i2c_speed;
u8 *dma_buf;
dma_addr_t dma_addr;
#ifdef CONFIG_PM
int (*suspend)(struct amd_i2c_common *i2c_common);
int (*resume)(struct amd_i2c_common *i2c_common);
#endif /* CONFIG_PM */
};
/**
* struct amd_mp2_dev - per PCI device context
* @pci_dev: PCI driver node
* @busses: MP2 devices may have up to two busses,
* each bus corresponding to an i2c adapter
* @mmio: iommapped registers
* @c2p_lock: controls access to the C2P mailbox shared between
* the two adapters
* @c2p_lock_busid: id of the adapter which locked c2p_lock
*/
struct amd_mp2_dev {
struct pci_dev *pci_dev;
struct amd_i2c_common *busses[2];
void __iomem *mmio;
struct mutex c2p_lock;
u8 c2p_lock_busid;
unsigned int probed;
};
#define ndev_pdev(ndev) ((ndev)->pci_dev)
#define ndev_name(ndev) pci_name(ndev_pdev(ndev))
#define ndev_dev(ndev) (&ndev_pdev(ndev)->dev)
#define work_amd_i2c_common(__work) \
container_of(__work, struct amd_i2c_common, work.work)
/* PCIe communication driver */
int amd_mp2_rw(struct amd_i2c_common *i2c_common, enum i2c_cmd reqcmd);
int amd_mp2_bus_enable_set(struct amd_i2c_common *i2c_common, bool enable);
void amd_mp2_process_event(struct amd_i2c_common *i2c_common);
void amd_mp2_rw_timeout(struct amd_i2c_common *i2c_common);
int amd_mp2_register_cb(struct amd_i2c_common *i2c_common);
int amd_mp2_unregister_cb(struct amd_i2c_common *i2c_common);
struct amd_mp2_dev *amd_mp2_find_device(void);
static inline void amd_mp2_pm_runtime_get(struct amd_mp2_dev *mp2_dev)
{
pm_runtime_get_sync(&mp2_dev->pci_dev->dev);
}
static inline void amd_mp2_pm_runtime_put(struct amd_mp2_dev *mp2_dev)
{
pm_runtime_mark_last_busy(&mp2_dev->pci_dev->dev);
pm_runtime_put_autosuspend(&mp2_dev->pci_dev->dev);
}
#endif

View File

@ -0,0 +1,376 @@
// SPDX-License-Identifier: GPL-2.0
/*
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
*
* Copyright (C) 2011 Weinmann Medical GmbH
* Author: Nikolaus Voss <n.voss@weinmann.de>
*
* Evolved from original work by:
* Copyright (C) 2004 Rick Bronson
* Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
*
* Borrowed heavily from original work by:
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#include "i2c-at91.h"
unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
{
return readl_relaxed(dev->base + reg);
}
void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
{
writel_relaxed(val, dev->base + reg);
}
void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
{
at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
}
void at91_twi_irq_save(struct at91_twi_dev *dev)
{
dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
at91_disable_twi_interrupts(dev);
}
void at91_twi_irq_restore(struct at91_twi_dev *dev)
{
at91_twi_write(dev, AT91_TWI_IER, dev->imr);
}
void at91_init_twi_bus(struct at91_twi_dev *dev)
{
at91_disable_twi_interrupts(dev);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
if (dev->slave_detected)
at91_init_twi_bus_slave(dev);
else
at91_init_twi_bus_master(dev);
}
static struct at91_twi_pdata at91rm9200_config = {
.clk_max_div = 5,
.clk_offset = 3,
.has_unre_flag = true,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9261_config = {
.clk_max_div = 5,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9260_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9g20_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9g10_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static const struct platform_device_id at91_twi_devtypes[] = {
{
.name = "i2c-at91rm9200",
.driver_data = (unsigned long) &at91rm9200_config,
}, {
.name = "i2c-at91sam9261",
.driver_data = (unsigned long) &at91sam9261_config,
}, {
.name = "i2c-at91sam9260",
.driver_data = (unsigned long) &at91sam9260_config,
}, {
.name = "i2c-at91sam9g20",
.driver_data = (unsigned long) &at91sam9g20_config,
}, {
.name = "i2c-at91sam9g10",
.driver_data = (unsigned long) &at91sam9g10_config,
}, {
/* sentinel */
}
};
#if defined(CONFIG_OF)
static struct at91_twi_pdata at91sam9x5_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata sama5d4_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = true,
};
static struct at91_twi_pdata sama5d2_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = true,
.has_alt_cmd = true,
.has_hold_field = true,
};
static const struct of_device_id atmel_twi_dt_ids[] = {
{
.compatible = "atmel,at91rm9200-i2c",
.data = &at91rm9200_config,
}, {
.compatible = "atmel,at91sam9260-i2c",
.data = &at91sam9260_config,
}, {
.compatible = "atmel,at91sam9261-i2c",
.data = &at91sam9261_config,
}, {
.compatible = "atmel,at91sam9g20-i2c",
.data = &at91sam9g20_config,
}, {
.compatible = "atmel,at91sam9g10-i2c",
.data = &at91sam9g10_config,
}, {
.compatible = "atmel,at91sam9x5-i2c",
.data = &at91sam9x5_config,
}, {
.compatible = "atmel,sama5d4-i2c",
.data = &sama5d4_config,
}, {
.compatible = "atmel,sama5d2-i2c",
.data = &sama5d2_config,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
#endif
static struct at91_twi_pdata *at91_twi_get_driver_data(
struct platform_device *pdev)
{
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
if (!match)
return NULL;
return (struct at91_twi_pdata *)match->data;
}
return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
}
static int at91_twi_probe(struct platform_device *pdev)
{
struct at91_twi_dev *dev;
struct resource *mem;
int rc;
u32 phy_addr;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->dev = &pdev->dev;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
return -ENODEV;
phy_addr = mem->start;
dev->pdata = at91_twi_get_driver_data(pdev);
if (!dev->pdata)
return -ENODEV;
dev->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(dev->base))
return PTR_ERR(dev->base);
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq < 0)
return dev->irq;
platform_set_drvdata(pdev, dev);
dev->clk = devm_clk_get(dev->dev, NULL);
if (IS_ERR(dev->clk)) {
dev_err(dev->dev, "no clock defined\n");
return -ENODEV;
}
clk_prepare_enable(dev->clk);
snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
i2c_set_adapdata(&dev->adapter, dev);
dev->adapter.owner = THIS_MODULE;
dev->adapter.class = I2C_CLASS_DEPRECATED;
dev->adapter.dev.parent = dev->dev;
dev->adapter.nr = pdev->id;
dev->adapter.timeout = AT91_I2C_TIMEOUT;
dev->adapter.dev.of_node = pdev->dev.of_node;
dev->slave_detected = i2c_detect_slave_mode(&pdev->dev);
if (dev->slave_detected)
rc = at91_twi_probe_slave(pdev, phy_addr, dev);
else
rc = at91_twi_probe_master(pdev, phy_addr, dev);
if (rc)
return rc;
at91_init_twi_bus(dev);
pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev->dev);
pm_runtime_set_active(dev->dev);
pm_runtime_enable(dev->dev);
rc = i2c_add_numbered_adapter(&dev->adapter);
if (rc) {
clk_disable_unprepare(dev->clk);
pm_runtime_disable(dev->dev);
pm_runtime_set_suspended(dev->dev);
return rc;
}
dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
at91_twi_read(dev, AT91_TWI_VER));
return 0;
}
static int at91_twi_remove(struct platform_device *pdev)
{
struct at91_twi_dev *dev = platform_get_drvdata(pdev);
i2c_del_adapter(&dev->adapter);
clk_disable_unprepare(dev->clk);
pm_runtime_disable(dev->dev);
pm_runtime_set_suspended(dev->dev);
return 0;
}
#ifdef CONFIG_PM
static int at91_twi_runtime_suspend(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
clk_disable_unprepare(twi_dev->clk);
pinctrl_pm_select_sleep_state(dev);
return 0;
}
static int at91_twi_runtime_resume(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
pinctrl_pm_select_default_state(dev);
return clk_prepare_enable(twi_dev->clk);
}
static int at91_twi_suspend_noirq(struct device *dev)
{
if (!pm_runtime_status_suspended(dev))
at91_twi_runtime_suspend(dev);
return 0;
}
static int at91_twi_resume_noirq(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
int ret;
if (!pm_runtime_status_suspended(dev)) {
ret = at91_twi_runtime_resume(dev);
if (ret)
return ret;
}
pm_runtime_mark_last_busy(dev);
pm_request_autosuspend(dev);
at91_init_twi_bus(twi_dev);
return 0;
}
static const struct dev_pm_ops at91_twi_pm = {
.suspend_noirq = at91_twi_suspend_noirq,
.resume_noirq = at91_twi_resume_noirq,
.runtime_suspend = at91_twi_runtime_suspend,
.runtime_resume = at91_twi_runtime_resume,
};
#define at91_twi_pm_ops (&at91_twi_pm)
#else
#define at91_twi_pm_ops NULL
#endif
static struct platform_driver at91_twi_driver = {
.probe = at91_twi_probe,
.remove = at91_twi_remove,
.id_table = at91_twi_devtypes,
.driver = {
.name = "at91_i2c",
.of_match_table = of_match_ptr(atmel_twi_dt_ids),
.pm = at91_twi_pm_ops,
},
};
static int __init at91_twi_init(void)
{
return platform_driver_register(&at91_twi_driver);
}
static void __exit at91_twi_exit(void)
{
platform_driver_unregister(&at91_twi_driver);
}
subsys_initcall(at91_twi_init);
module_exit(at91_twi_exit);
MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:at91_i2c");

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI) * i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
* *
@ -10,11 +11,6 @@
* *
* Borrowed heavily from original work by: * Borrowed heavily from original work by:
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com> * Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/ */
#include <linux/clk.h> #include <linux/clk.h>
@ -25,159 +21,16 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/platform_data/dma-atmel.h> #include <linux/platform_data/dma-atmel.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#define DEFAULT_TWI_CLK_HZ 100000 /* max 400 Kbits/s */ #include "i2c-at91.h"
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
#define AUTOSUSPEND_TIMEOUT 2000
#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
/* AT91 TWI register definitions */ void at91_init_twi_bus_master(struct at91_twi_dev *dev)
#define AT91_TWI_CR 0x0000 /* Control Register */
#define AT91_TWI_START BIT(0) /* Send a Start Condition */
#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */
#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */
#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */
#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */
#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */
#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */
#define AT91_TWI_SWRST BIT(7) /* Software Reset */
#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */
#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */
#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */
#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */
#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */
#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */
#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */
#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
#define AT91_TWI_CWGR_HOLD_MAX 0x1f
#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
#define AT91_TWI_SR 0x0020 /* Status Register */
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
#define AT91_TWI_INT_MASK \
(AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK)
#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */
#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */
#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff)
#define AT91_TWI_ACR_DIR BIT(8)
#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */
#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0)
#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0)
#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4)
#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4)
#define AT91_TWI_ONE_DATA 0x0
#define AT91_TWI_TWO_DATA 0x1
#define AT91_TWI_FOUR_DATA 0x2
#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */
#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */
#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */
#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */
#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */
#define AT91_TWI_VER 0x00fc /* Version Register */
struct at91_twi_pdata {
unsigned clk_max_div;
unsigned clk_offset;
bool has_unre_flag;
bool has_alt_cmd;
bool has_hold_field;
struct at_dma_slave dma_slave;
};
struct at91_twi_dma {
struct dma_chan *chan_rx;
struct dma_chan *chan_tx;
struct scatterlist sg[2];
struct dma_async_tx_descriptor *data_desc;
enum dma_data_direction direction;
bool buf_mapped;
bool xfer_in_progress;
};
struct at91_twi_dev {
struct device *dev;
void __iomem *base;
struct completion cmd_complete;
struct clk *clk;
u8 *buf;
size_t buf_len;
struct i2c_msg *msg;
int irq;
unsigned imr;
unsigned transfer_status;
struct i2c_adapter adapter;
unsigned twi_cwgr_reg;
struct at91_twi_pdata *pdata;
bool use_dma;
bool use_alt_cmd;
bool recv_len_abort;
u32 fifo_size;
struct at91_twi_dma dma;
};
static unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg)
{ {
return readl_relaxed(dev->base + reg);
}
static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val)
{
writel_relaxed(val, dev->base + reg);
}
static void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
{
at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK);
}
static void at91_twi_irq_save(struct at91_twi_dev *dev)
{
dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK;
at91_disable_twi_interrupts(dev);
}
static void at91_twi_irq_restore(struct at91_twi_dev *dev)
{
at91_twi_write(dev, AT91_TWI_IER, dev->imr);
}
static void at91_init_twi_bus(struct at91_twi_dev *dev)
{
at91_disable_twi_interrupts(dev);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
/* FIFO should be enabled immediately after the software reset */ /* FIFO should be enabled immediately after the software reset */
if (dev->fifo_size) if (dev->fifo_size)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN);
@ -190,16 +43,18 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
* Calculate symmetric clock as stated in datasheet: * Calculate symmetric clock as stated in datasheet:
* twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset)) * twi_clk = F_MAIN / (2 * (cdiv * (1 << ckdiv) + offset))
*/ */
static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk) static void at91_calc_twi_clock(struct at91_twi_dev *dev)
{ {
int ckdiv, cdiv, div, hold = 0; int ckdiv, cdiv, div, hold = 0;
struct at91_twi_pdata *pdata = dev->pdata; struct at91_twi_pdata *pdata = dev->pdata;
int offset = pdata->clk_offset; int offset = pdata->clk_offset;
int max_ckdiv = pdata->clk_max_div; int max_ckdiv = pdata->clk_max_div;
u32 twd_hold_time_ns = 0; struct i2c_timings timings, *t = &timings;
i2c_parse_fw_timings(dev->dev, t, true);
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk), div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
2 * twi_clk) - offset); 2 * t->bus_freq_hz) - offset);
ckdiv = fls(div >> 8); ckdiv = fls(div >> 8);
cdiv = div >> ckdiv; cdiv = div >> ckdiv;
@ -211,15 +66,12 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
} }
if (pdata->has_hold_field) { if (pdata->has_hold_field) {
of_property_read_u32(dev->dev->of_node, "i2c-sda-hold-time-ns",
&twd_hold_time_ns);
/* /*
* hold time = HOLD + 3 x T_peripheral_clock * hold time = HOLD + 3 x T_peripheral_clock
* Use clk rate in kHz to prevent overflows when computing * Use clk rate in kHz to prevent overflows when computing
* hold. * hold.
*/ */
hold = DIV_ROUND_UP(twd_hold_time_ns hold = DIV_ROUND_UP(t->sda_hold_ns
* (clk_get_rate(dev->clk) / 1000), 1000000); * (clk_get_rate(dev->clk) / 1000), 1000000);
hold -= 3; hold -= 3;
if (hold < 0) if (hold < 0)
@ -236,7 +88,7 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
| AT91_TWI_CWGR_HOLD(hold); | AT91_TWI_CWGR_HOLD(hold);
dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n", dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
cdiv, ckdiv, hold, twd_hold_time_ns); cdiv, ckdiv, hold, t->sda_hold_ns);
} }
static void at91_twi_dma_cleanup(struct at91_twi_dev *dev) static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
@ -833,124 +685,6 @@ static const struct i2c_algorithm at91_twi_algorithm = {
.functionality = at91_twi_func, .functionality = at91_twi_func,
}; };
static struct at91_twi_pdata at91rm9200_config = {
.clk_max_div = 5,
.clk_offset = 3,
.has_unre_flag = true,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9261_config = {
.clk_max_div = 5,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9260_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9g20_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata at91sam9g10_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static const struct platform_device_id at91_twi_devtypes[] = {
{
.name = "i2c-at91rm9200",
.driver_data = (unsigned long) &at91rm9200_config,
}, {
.name = "i2c-at91sam9261",
.driver_data = (unsigned long) &at91sam9261_config,
}, {
.name = "i2c-at91sam9260",
.driver_data = (unsigned long) &at91sam9260_config,
}, {
.name = "i2c-at91sam9g20",
.driver_data = (unsigned long) &at91sam9g20_config,
}, {
.name = "i2c-at91sam9g10",
.driver_data = (unsigned long) &at91sam9g10_config,
}, {
/* sentinel */
}
};
#if defined(CONFIG_OF)
static struct at91_twi_pdata at91sam9x5_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = false,
};
static struct at91_twi_pdata sama5d4_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
.has_hold_field = true,
};
static struct at91_twi_pdata sama5d2_config = {
.clk_max_div = 7,
.clk_offset = 4,
.has_unre_flag = true,
.has_alt_cmd = true,
.has_hold_field = true,
};
static const struct of_device_id atmel_twi_dt_ids[] = {
{
.compatible = "atmel,at91rm9200-i2c",
.data = &at91rm9200_config,
} , {
.compatible = "atmel,at91sam9260-i2c",
.data = &at91sam9260_config,
} , {
.compatible = "atmel,at91sam9261-i2c",
.data = &at91sam9261_config,
} , {
.compatible = "atmel,at91sam9g20-i2c",
.data = &at91sam9g20_config,
} , {
.compatible = "atmel,at91sam9g10-i2c",
.data = &at91sam9g10_config,
}, {
.compatible = "atmel,at91sam9x5-i2c",
.data = &at91sam9x5_config,
}, {
.compatible = "atmel,sama5d4-i2c",
.data = &sama5d4_config,
}, {
.compatible = "atmel,sama5d2-i2c",
.data = &sama5d2_config,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, atmel_twi_dt_ids);
#endif
static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr)
{ {
int ret = 0; int ret = 0;
@ -1033,74 +767,24 @@ error:
return ret; return ret;
} }
static struct at91_twi_pdata *at91_twi_get_driver_data( int at91_twi_probe_master(struct platform_device *pdev,
struct platform_device *pdev) u32 phy_addr, struct at91_twi_dev *dev)
{ {
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(atmel_twi_dt_ids, pdev->dev.of_node);
if (!match)
return NULL;
return (struct at91_twi_pdata *)match->data;
}
return (struct at91_twi_pdata *) platform_get_device_id(pdev)->driver_data;
}
static int at91_twi_probe(struct platform_device *pdev)
{
struct at91_twi_dev *dev;
struct resource *mem;
int rc; int rc;
u32 phy_addr;
u32 bus_clk_rate;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
init_completion(&dev->cmd_complete); init_completion(&dev->cmd_complete);
dev->dev = &pdev->dev;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
return -ENODEV;
phy_addr = mem->start;
dev->pdata = at91_twi_get_driver_data(pdev);
if (!dev->pdata)
return -ENODEV;
dev->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(dev->base))
return PTR_ERR(dev->base);
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq < 0)
return dev->irq;
rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0, rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt, 0,
dev_name(dev->dev), dev); dev_name(dev->dev), dev);
if (rc) { if (rc) {
dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc); dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
return rc; return rc;
} }
platform_set_drvdata(pdev, dev);
dev->clk = devm_clk_get(dev->dev, NULL);
if (IS_ERR(dev->clk)) {
dev_err(dev->dev, "no clock defined\n");
return -ENODEV;
}
rc = clk_prepare_enable(dev->clk);
if (rc)
return rc;
if (dev->dev->of_node) { if (dev->dev->of_node) {
rc = at91_twi_configure_dma(dev, phy_addr); rc = at91_twi_configure_dma(dev, phy_addr);
if (rc == -EPROBE_DEFER) { if (rc == -EPROBE_DEFER)
clk_disable_unprepare(dev->clk);
return rc; return rc;
}
} }
if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
@ -1108,144 +792,10 @@ static int at91_twi_probe(struct platform_device *pdev)
dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size); dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size);
} }
rc = of_property_read_u32(dev->dev->of_node, "clock-frequency", at91_calc_twi_clock(dev);
&bus_clk_rate);
if (rc)
bus_clk_rate = DEFAULT_TWI_CLK_HZ;
at91_calc_twi_clock(dev, bus_clk_rate);
at91_init_twi_bus(dev);
snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91");
i2c_set_adapdata(&dev->adapter, dev);
dev->adapter.owner = THIS_MODULE;
dev->adapter.class = I2C_CLASS_DEPRECATED;
dev->adapter.algo = &at91_twi_algorithm; dev->adapter.algo = &at91_twi_algorithm;
dev->adapter.quirks = &at91_twi_quirks; dev->adapter.quirks = &at91_twi_quirks;
dev->adapter.dev.parent = dev->dev;
dev->adapter.nr = pdev->id;
dev->adapter.timeout = AT91_I2C_TIMEOUT;
dev->adapter.dev.of_node = pdev->dev.of_node;
pm_runtime_set_autosuspend_delay(dev->dev, AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev->dev);
pm_runtime_set_active(dev->dev);
pm_runtime_enable(dev->dev);
rc = i2c_add_numbered_adapter(&dev->adapter);
if (rc) {
clk_disable_unprepare(dev->clk);
pm_runtime_disable(dev->dev);
pm_runtime_set_suspended(dev->dev);
return rc;
}
dev_info(dev->dev, "AT91 i2c bus driver (hw version: %#x).\n",
at91_twi_read(dev, AT91_TWI_VER));
return 0;
}
static int at91_twi_remove(struct platform_device *pdev)
{
struct at91_twi_dev *dev = platform_get_drvdata(pdev);
i2c_del_adapter(&dev->adapter);
clk_disable_unprepare(dev->clk);
pm_runtime_disable(dev->dev);
pm_runtime_set_suspended(dev->dev);
return 0; return 0;
} }
#ifdef CONFIG_PM
static int at91_twi_runtime_suspend(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
clk_disable_unprepare(twi_dev->clk);
pinctrl_pm_select_sleep_state(dev);
return 0;
}
static int at91_twi_runtime_resume(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
pinctrl_pm_select_default_state(dev);
return clk_prepare_enable(twi_dev->clk);
}
static int at91_twi_suspend_noirq(struct device *dev)
{
if (!pm_runtime_status_suspended(dev))
at91_twi_runtime_suspend(dev);
return 0;
}
static int at91_twi_resume_noirq(struct device *dev)
{
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
int ret;
if (!pm_runtime_status_suspended(dev)) {
ret = at91_twi_runtime_resume(dev);
if (ret)
return ret;
}
pm_runtime_mark_last_busy(dev);
pm_request_autosuspend(dev);
at91_init_twi_bus(twi_dev);
return 0;
}
static const struct dev_pm_ops at91_twi_pm = {
.suspend_noirq = at91_twi_suspend_noirq,
.resume_noirq = at91_twi_resume_noirq,
.runtime_suspend = at91_twi_runtime_suspend,
.runtime_resume = at91_twi_runtime_resume,
};
#define at91_twi_pm_ops (&at91_twi_pm)
#else
#define at91_twi_pm_ops NULL
#endif
static struct platform_driver at91_twi_driver = {
.probe = at91_twi_probe,
.remove = at91_twi_remove,
.id_table = at91_twi_devtypes,
.driver = {
.name = "at91_i2c",
.of_match_table = of_match_ptr(atmel_twi_dt_ids),
.pm = at91_twi_pm_ops,
},
};
static int __init at91_twi_init(void)
{
return platform_driver_register(&at91_twi_driver);
}
static void __exit at91_twi_exit(void)
{
platform_driver_unregister(&at91_twi_driver);
}
subsys_initcall(at91_twi_init);
module_exit(at91_twi_exit);
MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
MODULE_DESCRIPTION("I2C (TWI) driver for Atmel AT91");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:at91_i2c");

View File

@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
/*
* i2c slave support for Atmel's AT91 Two-Wire Interface (TWI)
*
* Copyright (C) 2017 Juergen Fitschen <me@jue.yt>
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include "i2c-at91.h"
static irqreturn_t atmel_twi_interrupt_slave(int irq, void *dev_id)
{
struct at91_twi_dev *dev = dev_id;
const unsigned status = at91_twi_read(dev, AT91_TWI_SR);
const unsigned irqstatus = status & at91_twi_read(dev, AT91_TWI_IMR);
u8 value;
if (!irqstatus)
return IRQ_NONE;
/* slave address has been detected on I2C bus */
if (irqstatus & AT91_TWI_SVACC) {
if (status & AT91_TWI_SVREAD) {
i2c_slave_event(dev->slave,
I2C_SLAVE_READ_REQUESTED, &value);
writeb_relaxed(value, dev->base + AT91_TWI_THR);
at91_twi_write(dev, AT91_TWI_IER,
AT91_TWI_TXRDY | AT91_TWI_EOSACC);
} else {
i2c_slave_event(dev->slave,
I2C_SLAVE_WRITE_REQUESTED, &value);
at91_twi_write(dev, AT91_TWI_IER,
AT91_TWI_RXRDY | AT91_TWI_EOSACC);
}
at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_SVACC);
}
/* byte transmitted to remote master */
if (irqstatus & AT91_TWI_TXRDY) {
i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &value);
writeb_relaxed(value, dev->base + AT91_TWI_THR);
}
/* byte received from remote master */
if (irqstatus & AT91_TWI_RXRDY) {
value = readb_relaxed(dev->base + AT91_TWI_RHR);
i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
}
/* master sent stop */
if (irqstatus & AT91_TWI_EOSACC) {
at91_twi_write(dev, AT91_TWI_IDR,
AT91_TWI_TXRDY | AT91_TWI_RXRDY | AT91_TWI_EOSACC);
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &value);
}
return IRQ_HANDLED;
}
static int at91_reg_slave(struct i2c_client *slave)
{
struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
if (dev->slave)
return -EBUSY;
if (slave->flags & I2C_CLIENT_TEN)
return -EAFNOSUPPORT;
/* Make sure twi_clk doesn't get turned off! */
pm_runtime_get_sync(dev->dev);
dev->slave = slave;
dev->smr = AT91_TWI_SMR_SADR(slave->addr);
at91_init_twi_bus(dev);
at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_SVACC);
dev_info(dev->dev, "entered slave mode (ADR=%d)\n", slave->addr);
return 0;
}
static int at91_unreg_slave(struct i2c_client *slave)
{
struct at91_twi_dev *dev = i2c_get_adapdata(slave->adapter);
WARN_ON(!dev->slave);
dev_info(dev->dev, "leaving slave mode\n");
dev->slave = NULL;
dev->smr = 0;
at91_init_twi_bus(dev);
pm_runtime_put(dev->dev);
return 0;
}
static u32 at91_twi_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SLAVE | I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}
static const struct i2c_algorithm at91_twi_algorithm_slave = {
.reg_slave = at91_reg_slave,
.unreg_slave = at91_unreg_slave,
.functionality = at91_twi_func,
};
int at91_twi_probe_slave(struct platform_device *pdev,
u32 phy_addr, struct at91_twi_dev *dev)
{
int rc;
rc = devm_request_irq(&pdev->dev, dev->irq, atmel_twi_interrupt_slave,
0, dev_name(dev->dev), dev);
if (rc) {
dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, rc);
return rc;
}
dev->adapter.algo = &at91_twi_algorithm_slave;
return 0;
}
void at91_init_twi_bus_slave(struct at91_twi_dev *dev)
{
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS);
if (dev->slave_detected && dev->smr) {
at91_twi_write(dev, AT91_TWI_SMR, dev->smr);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVEN);
}
}

View File

@ -0,0 +1,174 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
*
* Copyright (C) 2011 Weinmann Medical GmbH
* Author: Nikolaus Voss <n.voss@weinmann.de>
*
* Evolved from original work by:
* Copyright (C) 2004 Rick Bronson
* Converted to 2.6 by Andrew Victor <andrew@sanpeople.com>
*
* Borrowed heavily from original work by:
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/i2c.h>
#include <linux/platform_data/dma-atmel.h>
#include <linux/platform_device.h>
#define AT91_I2C_TIMEOUT msecs_to_jiffies(100) /* transfer timeout */
#define AT91_I2C_DMA_THRESHOLD 8 /* enable DMA if transfer size is bigger than this threshold */
#define AUTOSUSPEND_TIMEOUT 2000
#define AT91_I2C_MAX_ALT_CMD_DATA_SIZE 256
/* AT91 TWI register definitions */
#define AT91_TWI_CR 0x0000 /* Control Register */
#define AT91_TWI_START BIT(0) /* Send a Start Condition */
#define AT91_TWI_STOP BIT(1) /* Send a Stop Condition */
#define AT91_TWI_MSEN BIT(2) /* Master Transfer Enable */
#define AT91_TWI_MSDIS BIT(3) /* Master Transfer Disable */
#define AT91_TWI_SVEN BIT(4) /* Slave Transfer Enable */
#define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */
#define AT91_TWI_QUICK BIT(6) /* SMBus quick command */
#define AT91_TWI_SWRST BIT(7) /* Software Reset */
#define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */
#define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */
#define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */
#define AT91_TWI_RHRCLR BIT(25) /* Receive Holding Register Clear */
#define AT91_TWI_LOCKCLR BIT(26) /* Lock Clear */
#define AT91_TWI_FIFOEN BIT(28) /* FIFO Enable */
#define AT91_TWI_FIFODIS BIT(29) /* FIFO Disable */
#define AT91_TWI_MMR 0x0004 /* Master Mode Register */
#define AT91_TWI_IADRSZ_1 0x0100 /* Internal Device Address Size */
#define AT91_TWI_MREAD BIT(12) /* Master Read Direction */
#define AT91_TWI_SMR 0x0008 /* Slave Mode Register */
#define AT91_TWI_SMR_SADR_MAX 0x007f
#define AT91_TWI_SMR_SADR(x) (((x) & AT91_TWI_SMR_SADR_MAX) << 16)
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
#define AT91_TWI_CWGR_HOLD_MAX 0x1f
#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
#define AT91_TWI_SR 0x0020 /* Status Register */
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
#define AT91_TWI_RXRDY BIT(1) /* Receive Holding Register Ready */
#define AT91_TWI_TXRDY BIT(2) /* Transmit Holding Register Ready */
#define AT91_TWI_SVREAD BIT(3) /* Slave Read */
#define AT91_TWI_SVACC BIT(4) /* Slave Access */
#define AT91_TWI_OVRE BIT(6) /* Overrun Error */
#define AT91_TWI_UNRE BIT(7) /* Underrun Error */
#define AT91_TWI_NACK BIT(8) /* Not Acknowledged */
#define AT91_TWI_EOSACC BIT(11) /* End Of Slave Access */
#define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */
#define AT91_TWI_INT_MASK \
(AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \
| AT91_TWI_SVACC | AT91_TWI_EOSACC)
#define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */
#define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */
#define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */
#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
#define AT91_TWI_ACR 0x0040 /* Alternative Command Register */
#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff)
#define AT91_TWI_ACR_DIR BIT(8)
#define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */
#define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0)
#define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0)
#define AT91_TWI_FMR_RXRDYM(mode) (((mode) & 0x3) << 4)
#define AT91_TWI_FMR_RXRDYM_MASK (0x3 << 4)
#define AT91_TWI_ONE_DATA 0x0
#define AT91_TWI_TWO_DATA 0x1
#define AT91_TWI_FOUR_DATA 0x2
#define AT91_TWI_FLR 0x0054 /* FIFO Level Register */
#define AT91_TWI_FSR 0x0060 /* FIFO Status Register */
#define AT91_TWI_FIER 0x0064 /* FIFO Interrupt Enable Register */
#define AT91_TWI_FIDR 0x0068 /* FIFO Interrupt Disable Register */
#define AT91_TWI_FIMR 0x006c /* FIFO Interrupt Mask Register */
#define AT91_TWI_VER 0x00fc /* Version Register */
struct at91_twi_pdata {
unsigned clk_max_div;
unsigned clk_offset;
bool has_unre_flag;
bool has_alt_cmd;
bool has_hold_field;
struct at_dma_slave dma_slave;
};
struct at91_twi_dma {
struct dma_chan *chan_rx;
struct dma_chan *chan_tx;
struct scatterlist sg[2];
struct dma_async_tx_descriptor *data_desc;
enum dma_data_direction direction;
bool buf_mapped;
bool xfer_in_progress;
};
struct at91_twi_dev {
struct device *dev;
void __iomem *base;
struct completion cmd_complete;
struct clk *clk;
u8 *buf;
size_t buf_len;
struct i2c_msg *msg;
int irq;
unsigned imr;
unsigned transfer_status;
struct i2c_adapter adapter;
unsigned twi_cwgr_reg;
struct at91_twi_pdata *pdata;
bool use_dma;
bool use_alt_cmd;
bool recv_len_abort;
u32 fifo_size;
struct at91_twi_dma dma;
bool slave_detected;
#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
unsigned smr;
struct i2c_client *slave;
#endif
};
unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg);
void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val);
void at91_disable_twi_interrupts(struct at91_twi_dev *dev);
void at91_twi_irq_save(struct at91_twi_dev *dev);
void at91_twi_irq_restore(struct at91_twi_dev *dev);
void at91_init_twi_bus(struct at91_twi_dev *dev);
void at91_init_twi_bus_master(struct at91_twi_dev *dev);
int at91_twi_probe_master(struct platform_device *pdev, u32 phy_addr,
struct at91_twi_dev *dev);
#ifdef CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL
void at91_init_twi_bus_slave(struct at91_twi_dev *dev);
int at91_twi_probe_slave(struct platform_device *pdev, u32 phy_addr,
struct at91_twi_dev *dev);
#else
static inline void at91_init_twi_bus_slave(struct at91_twi_dev *dev) {}
static inline int at91_twi_probe_slave(struct platform_device *pdev,
u32 phy_addr, struct at91_twi_dev *dev)
{
return -EINVAL;
}
#endif

View File

@ -99,6 +99,7 @@
* @adapter: core i2c abstraction * @adapter: core i2c abstraction
* @i2c_clk: clock reference for i2c input clock * @i2c_clk: clock reference for i2c input clock
* @bus_clk_rate: current i2c bus clock rate * @bus_clk_rate: current i2c bus clock rate
* @last: a flag indicating is this is last message in transfer
*/ */
struct axxia_i2c_dev { struct axxia_i2c_dev {
void __iomem *base; void __iomem *base;
@ -112,6 +113,7 @@ struct axxia_i2c_dev {
struct i2c_adapter adapter; struct i2c_adapter adapter;
struct clk *i2c_clk; struct clk *i2c_clk;
u32 bus_clk_rate; u32 bus_clk_rate;
bool last;
}; };
static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask) static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask)
@ -324,15 +326,14 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev)
/* Stop completed */ /* Stop completed */
i2c_int_disable(idev, ~MST_STATUS_TSS); i2c_int_disable(idev, ~MST_STATUS_TSS);
complete(&idev->msg_complete); complete(&idev->msg_complete);
} else if (status & MST_STATUS_SNS) { } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) {
/* Transfer done */ /* Transfer done */
i2c_int_disable(idev, ~MST_STATUS_TSS); int mask = idev->last ? ~0 : ~MST_STATUS_TSS;
i2c_int_disable(idev, mask);
if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len) if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len)
axxia_i2c_empty_rx_fifo(idev); axxia_i2c_empty_rx_fifo(idev);
complete(&idev->msg_complete); complete(&idev->msg_complete);
} else if (status & MST_STATUS_SS) {
/* Auto/Sequence transfer done */
complete(&idev->msg_complete);
} else if (status & MST_STATUS_TSS) { } else if (status & MST_STATUS_TSS) {
/* Transfer timeout */ /* Transfer timeout */
idev->msg_err = -ETIMEDOUT; idev->msg_err = -ETIMEDOUT;
@ -405,6 +406,7 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
idev->msg_r = &msgs[1]; idev->msg_r = &msgs[1];
idev->msg_xfrd = 0; idev->msg_xfrd = 0;
idev->msg_xfrd_r = 0; idev->msg_xfrd_r = 0;
idev->last = true;
axxia_i2c_fill_tx_fifo(idev); axxia_i2c_fill_tx_fifo(idev);
writel(CMD_SEQUENCE, idev->base + MST_COMMAND); writel(CMD_SEQUENCE, idev->base + MST_COMMAND);
@ -415,10 +417,6 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
time_left = wait_for_completion_timeout(&idev->msg_complete, time_left = wait_for_completion_timeout(&idev->msg_complete,
I2C_XFER_TIMEOUT); I2C_XFER_TIMEOUT);
i2c_int_disable(idev, int_mask);
axxia_i2c_empty_rx_fifo(idev);
if (idev->msg_err == -ENXIO) { if (idev->msg_err == -ENXIO) {
if (axxia_i2c_handle_seq_nak(idev)) if (axxia_i2c_handle_seq_nak(idev))
axxia_i2c_init(idev); axxia_i2c_init(idev);
@ -438,9 +436,10 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[])
return idev->msg_err; return idev->msg_err;
} }
static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg,
bool last)
{ {
u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS; u32 int_mask = MST_STATUS_ERR;
u32 rx_xfer, tx_xfer; u32 rx_xfer, tx_xfer;
unsigned long time_left; unsigned long time_left;
unsigned int wt_value; unsigned int wt_value;
@ -449,6 +448,7 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
idev->msg_r = msg; idev->msg_r = msg;
idev->msg_xfrd = 0; idev->msg_xfrd = 0;
idev->msg_xfrd_r = 0; idev->msg_xfrd_r = 0;
idev->last = last;
reinit_completion(&idev->msg_complete); reinit_completion(&idev->msg_complete);
axxia_i2c_set_addr(idev, msg); axxia_i2c_set_addr(idev, msg);
@ -478,8 +478,13 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg)
if (idev->msg_err) if (idev->msg_err)
goto out; goto out;
/* Start manual mode */ if (!last) {
writel(CMD_MANUAL, idev->base + MST_COMMAND); writel(CMD_MANUAL, idev->base + MST_COMMAND);
int_mask |= MST_STATUS_SNS;
} else {
writel(CMD_AUTO, idev->base + MST_COMMAND);
int_mask |= MST_STATUS_SS;
}
writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL); writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL);
@ -507,28 +512,6 @@ out:
return idev->msg_err; return idev->msg_err;
} }
static int axxia_i2c_stop(struct axxia_i2c_dev *idev)
{
u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC | MST_STATUS_TSS;
unsigned long time_left;
reinit_completion(&idev->msg_complete);
/* Issue stop */
writel(0xb, idev->base + MST_COMMAND);
i2c_int_enable(idev, int_mask);
time_left = wait_for_completion_timeout(&idev->msg_complete,
I2C_STOP_TIMEOUT);
i2c_int_disable(idev, int_mask);
if (time_left == 0)
return -ETIMEDOUT;
if (readl(idev->base + MST_COMMAND) & CMD_BUSY)
dev_warn(idev->dev, "busy after stop\n");
return 0;
}
/* This function checks if the msgs[] array contains messages compatible with /* This function checks if the msgs[] array contains messages compatible with
* Sequence mode of operation. This mode assumes there will be exactly one * Sequence mode of operation. This mode assumes there will be exactly one
* write of non-zero length followed by exactly one read of non-zero length, * write of non-zero length followed by exactly one read of non-zero length,
@ -558,9 +541,7 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
i2c_int_enable(idev, MST_STATUS_TSS); i2c_int_enable(idev, MST_STATUS_TSS);
for (i = 0; ret == 0 && i < num; ++i) for (i = 0; ret == 0 && i < num; ++i)
ret = axxia_i2c_xfer_msg(idev, &msgs[i]); ret = axxia_i2c_xfer_msg(idev, &msgs[i], i == (num - 1));
axxia_i2c_stop(idev);
return ret ? : i; return ret ? : i;
} }

File diff suppressed because it is too large Load Diff

View File

@ -165,7 +165,6 @@ static const struct bsc_clk_param bsc_clk[] = {
struct brcmstb_i2c_dev { struct brcmstb_i2c_dev {
struct device *device; struct device *device;
void __iomem *base; void __iomem *base;
void __iomem *irq_base;
int irq; int irq;
struct bsc_regs *bsc_regmap; struct bsc_regs *bsc_regmap;
struct i2c_adapter adapter; struct i2c_adapter adapter;

View File

@ -251,13 +251,27 @@ unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare) int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare)
{ {
int ret;
if (IS_ERR(dev->clk)) if (IS_ERR(dev->clk))
return PTR_ERR(dev->clk); return PTR_ERR(dev->clk);
if (prepare) if (prepare) {
return clk_prepare_enable(dev->clk); /* Optional interface clock */
ret = clk_prepare_enable(dev->pclk);
if (ret)
return ret;
ret = clk_prepare_enable(dev->clk);
if (ret)
clk_disable_unprepare(dev->pclk);
return ret;
}
clk_disable_unprepare(dev->clk); clk_disable_unprepare(dev->clk);
clk_disable_unprepare(dev->pclk);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(i2c_dw_prepare_clk); EXPORT_SYMBOL_GPL(i2c_dw_prepare_clk);

View File

@ -177,6 +177,7 @@
* @base: IO registers pointer * @base: IO registers pointer
* @cmd_complete: tx completion indicator * @cmd_complete: tx completion indicator
* @clk: input reference clock * @clk: input reference clock
* @pclk: clock required to access the registers
* @slave: represent an I2C slave device * @slave: represent an I2C slave device
* @cmd_err: run time hadware error code * @cmd_err: run time hadware error code
* @msgs: points to an array of messages currently being transferred * @msgs: points to an array of messages currently being transferred
@ -227,6 +228,7 @@ struct dw_i2c_dev {
void __iomem *ext; void __iomem *ext;
struct completion cmd_complete; struct completion cmd_complete;
struct clk *clk; struct clk *clk;
struct clk *pclk;
struct reset_control *rst; struct reset_control *rst;
struct i2c_client *slave; struct i2c_client *slave;
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);

View File

@ -344,6 +344,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
else else
i2c_dw_configure_master(dev); i2c_dw_configure_master(dev);
/* Optional interface clock */
dev->pclk = devm_clk_get_optional(&pdev->dev, "pclk");
if (IS_ERR(dev->pclk))
return PTR_ERR(dev->pclk);
dev->clk = devm_clk_get(&pdev->dev, NULL); dev->clk = devm_clk_get(&pdev->dev, NULL);
if (!i2c_dw_prepare_clk(dev, true)) { if (!i2c_dw_prepare_clk(dev, true)) {
u64 clk_khz; u64 clk_khz;

View File

@ -413,6 +413,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl)) if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl))
dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing"); dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing");
else
bit_data->can_do_atomic = true;
bit_data->setsda = i2c_gpio_setsda_val; bit_data->setsda = i2c_gpio_setsda_val;
bit_data->setscl = i2c_gpio_setscl_val; bit_data->setscl = i2c_gpio_setscl_val;

View File

@ -639,8 +639,7 @@ static int lpi2c_imx_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
static int lpi2c_runtime_suspend(struct device *dev)
{ {
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
@ -650,7 +649,7 @@ static int lpi2c_runtime_suspend(struct device *dev)
return 0; return 0;
} }
static int lpi2c_runtime_resume(struct device *dev) static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
{ {
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
int ret; int ret;
@ -671,10 +670,6 @@ static const struct dev_pm_ops lpi2c_pm_ops = {
SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend, SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend,
lpi2c_runtime_resume, NULL) lpi2c_runtime_resume, NULL)
}; };
#define IMX_LPI2C_PM (&lpi2c_pm_ops)
#else
#define IMX_LPI2C_PM NULL
#endif
static struct platform_driver lpi2c_imx_driver = { static struct platform_driver lpi2c_imx_driver = {
.probe = lpi2c_imx_probe, .probe = lpi2c_imx_probe,
@ -682,7 +677,7 @@ static struct platform_driver lpi2c_imx_driver = {
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.of_match_table = lpi2c_imx_of_match, .of_match_table = lpi2c_imx_of_match,
.pm = IMX_LPI2C_PM, .pm = &lpi2c_pm_ops,
}, },
}; };

View File

@ -30,7 +30,6 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/acpi.h>
/* SCH SMBus address offsets */ /* SCH SMBus address offsets */
#define SMBHSTCNT (0 + sch_smba) #define SMBHSTCNT (0 + sch_smba)

View File

@ -35,6 +35,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#define I2C_RS_TRANSFER (1 << 4) #define I2C_RS_TRANSFER (1 << 4)
#define I2C_ARB_LOST (1 << 3)
#define I2C_HS_NACKERR (1 << 2) #define I2C_HS_NACKERR (1 << 2)
#define I2C_ACKERR (1 << 1) #define I2C_ACKERR (1 << 1)
#define I2C_TRANSAC_COMP (1 << 0) #define I2C_TRANSAC_COMP (1 << 0)
@ -76,6 +77,8 @@
#define I2C_CONTROL_DIR_CHANGE (0x1 << 4) #define I2C_CONTROL_DIR_CHANGE (0x1 << 4)
#define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5) #define I2C_CONTROL_ACKERR_DET_EN (0x1 << 5)
#define I2C_CONTROL_TRANSFER_LEN_CHANGE (0x1 << 6) #define I2C_CONTROL_TRANSFER_LEN_CHANGE (0x1 << 6)
#define I2C_CONTROL_DMAACK_EN (0x1 << 8)
#define I2C_CONTROL_ASYNC_MODE (0x1 << 9)
#define I2C_CONTROL_WRAPPER (0x1 << 0) #define I2C_CONTROL_WRAPPER (0x1 << 0)
#define I2C_DRV_NAME "i2c-mt65xx" #define I2C_DRV_NAME "i2c-mt65xx"
@ -106,40 +109,97 @@ enum mtk_trans_op {
}; };
enum I2C_REGS_OFFSET { enum I2C_REGS_OFFSET {
OFFSET_DATA_PORT = 0x0, OFFSET_DATA_PORT,
OFFSET_SLAVE_ADDR = 0x04, OFFSET_SLAVE_ADDR,
OFFSET_INTR_MASK = 0x08, OFFSET_INTR_MASK,
OFFSET_INTR_STAT = 0x0c, OFFSET_INTR_STAT,
OFFSET_CONTROL = 0x10, OFFSET_CONTROL,
OFFSET_TRANSFER_LEN = 0x14, OFFSET_TRANSFER_LEN,
OFFSET_TRANSAC_LEN = 0x18, OFFSET_TRANSAC_LEN,
OFFSET_DELAY_LEN = 0x1c, OFFSET_DELAY_LEN,
OFFSET_TIMING = 0x20, OFFSET_TIMING,
OFFSET_START = 0x24, OFFSET_START,
OFFSET_EXT_CONF = 0x28, OFFSET_EXT_CONF,
OFFSET_FIFO_STAT = 0x30, OFFSET_FIFO_STAT,
OFFSET_FIFO_THRESH = 0x34, OFFSET_FIFO_THRESH,
OFFSET_FIFO_ADDR_CLR = 0x38, OFFSET_FIFO_ADDR_CLR,
OFFSET_IO_CONFIG = 0x40, OFFSET_IO_CONFIG,
OFFSET_RSV_DEBUG = 0x44, OFFSET_RSV_DEBUG,
OFFSET_HS = 0x48, OFFSET_HS,
OFFSET_SOFTRESET = 0x50, OFFSET_SOFTRESET,
OFFSET_DCM_EN = 0x54, OFFSET_DCM_EN,
OFFSET_PATH_DIR = 0x60, OFFSET_PATH_DIR,
OFFSET_DEBUGSTAT = 0x64, OFFSET_DEBUGSTAT,
OFFSET_DEBUGCTRL = 0x68, OFFSET_DEBUGCTRL,
OFFSET_TRANSFER_LEN_AUX = 0x6c, OFFSET_TRANSFER_LEN_AUX,
OFFSET_CLOCK_DIV = 0x70, OFFSET_CLOCK_DIV,
OFFSET_LTIMING,
};
static const u16 mt_i2c_regs_v1[] = {
[OFFSET_DATA_PORT] = 0x0,
[OFFSET_SLAVE_ADDR] = 0x4,
[OFFSET_INTR_MASK] = 0x8,
[OFFSET_INTR_STAT] = 0xc,
[OFFSET_CONTROL] = 0x10,
[OFFSET_TRANSFER_LEN] = 0x14,
[OFFSET_TRANSAC_LEN] = 0x18,
[OFFSET_DELAY_LEN] = 0x1c,
[OFFSET_TIMING] = 0x20,
[OFFSET_START] = 0x24,
[OFFSET_EXT_CONF] = 0x28,
[OFFSET_FIFO_STAT] = 0x30,
[OFFSET_FIFO_THRESH] = 0x34,
[OFFSET_FIFO_ADDR_CLR] = 0x38,
[OFFSET_IO_CONFIG] = 0x40,
[OFFSET_RSV_DEBUG] = 0x44,
[OFFSET_HS] = 0x48,
[OFFSET_SOFTRESET] = 0x50,
[OFFSET_DCM_EN] = 0x54,
[OFFSET_PATH_DIR] = 0x60,
[OFFSET_DEBUGSTAT] = 0x64,
[OFFSET_DEBUGCTRL] = 0x68,
[OFFSET_TRANSFER_LEN_AUX] = 0x6c,
[OFFSET_CLOCK_DIV] = 0x70,
};
static const u16 mt_i2c_regs_v2[] = {
[OFFSET_DATA_PORT] = 0x0,
[OFFSET_SLAVE_ADDR] = 0x4,
[OFFSET_INTR_MASK] = 0x8,
[OFFSET_INTR_STAT] = 0xc,
[OFFSET_CONTROL] = 0x10,
[OFFSET_TRANSFER_LEN] = 0x14,
[OFFSET_TRANSAC_LEN] = 0x18,
[OFFSET_DELAY_LEN] = 0x1c,
[OFFSET_TIMING] = 0x20,
[OFFSET_START] = 0x24,
[OFFSET_EXT_CONF] = 0x28,
[OFFSET_LTIMING] = 0x2c,
[OFFSET_HS] = 0x30,
[OFFSET_IO_CONFIG] = 0x34,
[OFFSET_FIFO_ADDR_CLR] = 0x38,
[OFFSET_TRANSFER_LEN_AUX] = 0x44,
[OFFSET_CLOCK_DIV] = 0x48,
[OFFSET_SOFTRESET] = 0x50,
[OFFSET_DEBUGSTAT] = 0xe0,
[OFFSET_DEBUGCTRL] = 0xe8,
[OFFSET_FIFO_STAT] = 0xf4,
[OFFSET_FIFO_THRESH] = 0xf8,
[OFFSET_DCM_EN] = 0xf88,
}; };
struct mtk_i2c_compatible { struct mtk_i2c_compatible {
const struct i2c_adapter_quirks *quirks; const struct i2c_adapter_quirks *quirks;
const u16 *regs;
unsigned char pmic_i2c: 1; unsigned char pmic_i2c: 1;
unsigned char dcm: 1; unsigned char dcm: 1;
unsigned char auto_restart: 1; unsigned char auto_restart: 1;
unsigned char aux_len_reg: 1; unsigned char aux_len_reg: 1;
unsigned char support_33bits: 1; unsigned char support_33bits: 1;
unsigned char timing_adjust: 1; unsigned char timing_adjust: 1;
unsigned char dma_sync: 1;
unsigned char ltiming_adjust: 1;
}; };
struct mtk_i2c { struct mtk_i2c {
@ -153,6 +213,7 @@ struct mtk_i2c {
struct clk *clk_main; /* main clock for i2c bus */ struct clk *clk_main; /* main clock for i2c bus */
struct clk *clk_dma; /* DMA clock for i2c via DMA */ struct clk *clk_dma; /* DMA clock for i2c via DMA */
struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */ struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */
struct clk *clk_arb; /* Arbitrator clock for i2c */
bool have_pmic; /* can use i2c pins from PMIC */ bool have_pmic; /* can use i2c pins from PMIC */
bool use_push_pull; /* IO config push-pull mode */ bool use_push_pull; /* IO config push-pull mode */
@ -162,6 +223,7 @@ struct mtk_i2c {
enum mtk_trans_op op; enum mtk_trans_op op;
u16 timing_reg; u16 timing_reg;
u16 high_speed_reg; u16 high_speed_reg;
u16 ltiming_reg;
unsigned char auto_restart; unsigned char auto_restart;
bool ignore_restart_irq; bool ignore_restart_irq;
const struct mtk_i2c_compatible *dev_comp; const struct mtk_i2c_compatible *dev_comp;
@ -181,51 +243,78 @@ static const struct i2c_adapter_quirks mt7622_i2c_quirks = {
}; };
static const struct mtk_i2c_compatible mt2712_compat = { static const struct mtk_i2c_compatible mt2712_compat = {
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0, .pmic_i2c = 0,
.dcm = 1, .dcm = 1,
.auto_restart = 1, .auto_restart = 1,
.aux_len_reg = 1, .aux_len_reg = 1,
.support_33bits = 1, .support_33bits = 1,
.timing_adjust = 1, .timing_adjust = 1,
.dma_sync = 0,
.ltiming_adjust = 0,
}; };
static const struct mtk_i2c_compatible mt6577_compat = { static const struct mtk_i2c_compatible mt6577_compat = {
.quirks = &mt6577_i2c_quirks, .quirks = &mt6577_i2c_quirks,
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0, .pmic_i2c = 0,
.dcm = 1, .dcm = 1,
.auto_restart = 0, .auto_restart = 0,
.aux_len_reg = 0, .aux_len_reg = 0,
.support_33bits = 0, .support_33bits = 0,
.timing_adjust = 0, .timing_adjust = 0,
.dma_sync = 0,
.ltiming_adjust = 0,
}; };
static const struct mtk_i2c_compatible mt6589_compat = { static const struct mtk_i2c_compatible mt6589_compat = {
.quirks = &mt6577_i2c_quirks, .quirks = &mt6577_i2c_quirks,
.regs = mt_i2c_regs_v1,
.pmic_i2c = 1, .pmic_i2c = 1,
.dcm = 0, .dcm = 0,
.auto_restart = 0, .auto_restart = 0,
.aux_len_reg = 0, .aux_len_reg = 0,
.support_33bits = 0, .support_33bits = 0,
.timing_adjust = 0, .timing_adjust = 0,
.dma_sync = 0,
.ltiming_adjust = 0,
}; };
static const struct mtk_i2c_compatible mt7622_compat = { static const struct mtk_i2c_compatible mt7622_compat = {
.quirks = &mt7622_i2c_quirks, .quirks = &mt7622_i2c_quirks,
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0, .pmic_i2c = 0,
.dcm = 1, .dcm = 1,
.auto_restart = 1, .auto_restart = 1,
.aux_len_reg = 1, .aux_len_reg = 1,
.support_33bits = 0, .support_33bits = 0,
.timing_adjust = 0, .timing_adjust = 0,
.dma_sync = 0,
.ltiming_adjust = 0,
}; };
static const struct mtk_i2c_compatible mt8173_compat = { static const struct mtk_i2c_compatible mt8173_compat = {
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0, .pmic_i2c = 0,
.dcm = 1, .dcm = 1,
.auto_restart = 1, .auto_restart = 1,
.aux_len_reg = 1, .aux_len_reg = 1,
.support_33bits = 1, .support_33bits = 1,
.timing_adjust = 0, .timing_adjust = 0,
.dma_sync = 0,
.ltiming_adjust = 0,
};
static const struct mtk_i2c_compatible mt8183_compat = {
.regs = mt_i2c_regs_v2,
.pmic_i2c = 0,
.dcm = 0,
.auto_restart = 1,
.aux_len_reg = 1,
.support_33bits = 1,
.timing_adjust = 1,
.dma_sync = 1,
.ltiming_adjust = 1,
}; };
static const struct of_device_id mtk_i2c_of_match[] = { static const struct of_device_id mtk_i2c_of_match[] = {
@ -234,10 +323,22 @@ static const struct of_device_id mtk_i2c_of_match[] = {
{ .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat },
{ .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat },
{ .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat },
{ .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat },
{} {}
}; };
MODULE_DEVICE_TABLE(of, mtk_i2c_of_match); MODULE_DEVICE_TABLE(of, mtk_i2c_of_match);
static u16 mtk_i2c_readw(struct mtk_i2c *i2c, enum I2C_REGS_OFFSET reg)
{
return readw(i2c->base + i2c->dev_comp->regs[reg]);
}
static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val,
enum I2C_REGS_OFFSET reg)
{
writew(val, i2c->base + i2c->dev_comp->regs[reg]);
}
static int mtk_i2c_clock_enable(struct mtk_i2c *i2c) static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
{ {
int ret; int ret;
@ -255,8 +356,18 @@ static int mtk_i2c_clock_enable(struct mtk_i2c *i2c)
if (ret) if (ret)
goto err_pmic; goto err_pmic;
} }
if (i2c->clk_arb) {
ret = clk_prepare_enable(i2c->clk_arb);
if (ret)
goto err_arb;
}
return 0; return 0;
err_arb:
if (i2c->have_pmic)
clk_disable_unprepare(i2c->clk_pmic);
err_pmic: err_pmic:
clk_disable_unprepare(i2c->clk_main); clk_disable_unprepare(i2c->clk_main);
err_main: err_main:
@ -267,6 +378,9 @@ err_main:
static void mtk_i2c_clock_disable(struct mtk_i2c *i2c) static void mtk_i2c_clock_disable(struct mtk_i2c *i2c)
{ {
if (i2c->clk_arb)
clk_disable_unprepare(i2c->clk_arb);
if (i2c->have_pmic) if (i2c->have_pmic)
clk_disable_unprepare(i2c->clk_pmic); clk_disable_unprepare(i2c->clk_pmic);
@ -278,31 +392,36 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
{ {
u16 control_reg; u16 control_reg;
writew(I2C_SOFT_RST, i2c->base + OFFSET_SOFTRESET); mtk_i2c_writew(i2c, I2C_SOFT_RST, OFFSET_SOFTRESET);
/* Set ioconfig */ /* Set ioconfig */
if (i2c->use_push_pull) if (i2c->use_push_pull)
writew(I2C_IO_CONFIG_PUSH_PULL, i2c->base + OFFSET_IO_CONFIG); mtk_i2c_writew(i2c, I2C_IO_CONFIG_PUSH_PULL, OFFSET_IO_CONFIG);
else else
writew(I2C_IO_CONFIG_OPEN_DRAIN, i2c->base + OFFSET_IO_CONFIG); mtk_i2c_writew(i2c, I2C_IO_CONFIG_OPEN_DRAIN, OFFSET_IO_CONFIG);
if (i2c->dev_comp->dcm) if (i2c->dev_comp->dcm)
writew(I2C_DCM_DISABLE, i2c->base + OFFSET_DCM_EN); mtk_i2c_writew(i2c, I2C_DCM_DISABLE, OFFSET_DCM_EN);
if (i2c->dev_comp->timing_adjust) if (i2c->dev_comp->timing_adjust)
writew(I2C_DEFAULT_CLK_DIV - 1, i2c->base + OFFSET_CLOCK_DIV); mtk_i2c_writew(i2c, I2C_DEFAULT_CLK_DIV - 1, OFFSET_CLOCK_DIV);
writew(i2c->timing_reg, i2c->base + OFFSET_TIMING); mtk_i2c_writew(i2c, i2c->timing_reg, OFFSET_TIMING);
writew(i2c->high_speed_reg, i2c->base + OFFSET_HS); mtk_i2c_writew(i2c, i2c->high_speed_reg, OFFSET_HS);
if (i2c->dev_comp->ltiming_adjust)
mtk_i2c_writew(i2c, i2c->ltiming_reg, OFFSET_LTIMING);
/* If use i2c pin from PMIC mt6397 side, need set PATH_DIR first */ /* If use i2c pin from PMIC mt6397 side, need set PATH_DIR first */
if (i2c->have_pmic) if (i2c->have_pmic)
writew(I2C_CONTROL_WRAPPER, i2c->base + OFFSET_PATH_DIR); mtk_i2c_writew(i2c, I2C_CONTROL_WRAPPER, OFFSET_PATH_DIR);
control_reg = I2C_CONTROL_ACKERR_DET_EN | control_reg = I2C_CONTROL_ACKERR_DET_EN |
I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN; I2C_CONTROL_CLK_EXT_EN | I2C_CONTROL_DMA_EN;
writew(control_reg, i2c->base + OFFSET_CONTROL); if (i2c->dev_comp->dma_sync)
writew(I2C_DELAY_LEN, i2c->base + OFFSET_DELAY_LEN); control_reg |= I2C_CONTROL_DMAACK_EN | I2C_CONTROL_ASYNC_MODE;
mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
mtk_i2c_writew(i2c, I2C_DELAY_LEN, OFFSET_DELAY_LEN);
writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST); writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
udelay(50); udelay(50);
@ -390,6 +509,8 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
unsigned int clk_src; unsigned int clk_src;
unsigned int step_cnt; unsigned int step_cnt;
unsigned int sample_cnt; unsigned int sample_cnt;
unsigned int l_step_cnt;
unsigned int l_sample_cnt;
unsigned int target_speed; unsigned int target_speed;
int ret; int ret;
@ -399,11 +520,11 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
if (target_speed > MAX_FS_MODE_SPEED) { if (target_speed > MAX_FS_MODE_SPEED) {
/* Set master code speed register */ /* Set master code speed register */
ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED, ret = mtk_i2c_calculate_speed(i2c, clk_src, MAX_FS_MODE_SPEED,
&step_cnt, &sample_cnt); &l_step_cnt, &l_sample_cnt);
if (ret < 0) if (ret < 0)
return ret; return ret;
i2c->timing_reg = (sample_cnt << 8) | step_cnt; i2c->timing_reg = (l_sample_cnt << 8) | l_step_cnt;
/* Set the high speed mode register */ /* Set the high speed mode register */
ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed, ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
@ -413,6 +534,10 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE | i2c->high_speed_reg = I2C_TIME_DEFAULT_VALUE |
(sample_cnt << 12) | (step_cnt << 8); (sample_cnt << 12) | (step_cnt << 8);
if (i2c->dev_comp->ltiming_adjust)
i2c->ltiming_reg = (l_sample_cnt << 6) | l_step_cnt |
(sample_cnt << 12) | (step_cnt << 9);
} else { } else {
ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed, ret = mtk_i2c_calculate_speed(i2c, clk_src, target_speed,
&step_cnt, &sample_cnt); &step_cnt, &sample_cnt);
@ -423,6 +548,9 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk)
/* Disable the high speed transaction */ /* Disable the high speed transaction */
i2c->high_speed_reg = I2C_TIME_CLR_VALUE; i2c->high_speed_reg = I2C_TIME_CLR_VALUE;
if (i2c->dev_comp->ltiming_adjust)
i2c->ltiming_reg = (sample_cnt << 6) | step_cnt;
} }
return 0; return 0;
@ -454,7 +582,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
reinit_completion(&i2c->msg_complete); reinit_completion(&i2c->msg_complete);
control_reg = readw(i2c->base + OFFSET_CONTROL) & control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) &
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1)) if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1))
control_reg |= I2C_CONTROL_RS; control_reg |= I2C_CONTROL_RS;
@ -462,40 +590,41 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
if (i2c->op == I2C_MASTER_WRRD) if (i2c->op == I2C_MASTER_WRRD)
control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS; control_reg |= I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS;
writew(control_reg, i2c->base + OFFSET_CONTROL); mtk_i2c_writew(i2c, control_reg, OFFSET_CONTROL);
/* set start condition */ /* set start condition */
if (i2c->speed_hz <= I2C_DEFAULT_SPEED) if (i2c->speed_hz <= I2C_DEFAULT_SPEED)
writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF); mtk_i2c_writew(i2c, I2C_ST_START_CON, OFFSET_EXT_CONF);
else else
writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF); mtk_i2c_writew(i2c, I2C_FS_START_CON, OFFSET_EXT_CONF);
addr_reg = i2c_8bit_addr_from_msg(msgs); addr_reg = i2c_8bit_addr_from_msg(msgs);
writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR); mtk_i2c_writew(i2c, addr_reg, OFFSET_SLAVE_ADDR);
/* Clear interrupt status */ /* Clear interrupt status */
writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_STAT); I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_STAT);
writew(I2C_FIFO_ADDR_CLR, i2c->base + OFFSET_FIFO_ADDR_CLR);
mtk_i2c_writew(i2c, I2C_FIFO_ADDR_CLR, OFFSET_FIFO_ADDR_CLR);
/* Enable interrupt */ /* Enable interrupt */
writew(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | mtk_i2c_writew(i2c, restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
I2C_TRANSAC_COMP, i2c->base + OFFSET_INTR_MASK); I2C_ARB_LOST | I2C_TRANSAC_COMP, OFFSET_INTR_MASK);
/* Set transfer and transaction len */ /* Set transfer and transaction len */
if (i2c->op == I2C_MASTER_WRRD) { if (i2c->op == I2C_MASTER_WRRD) {
if (i2c->dev_comp->aux_len_reg) { if (i2c->dev_comp->aux_len_reg) {
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN); mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
writew((msgs + 1)->len, i2c->base + mtk_i2c_writew(i2c, (msgs + 1)->len,
OFFSET_TRANSFER_LEN_AUX); OFFSET_TRANSFER_LEN_AUX);
} else { } else {
writew(msgs->len | ((msgs + 1)->len) << 8, mtk_i2c_writew(i2c, msgs->len | ((msgs + 1)->len) << 8,
i2c->base + OFFSET_TRANSFER_LEN); OFFSET_TRANSFER_LEN);
} }
writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN); mtk_i2c_writew(i2c, I2C_WRRD_TRANAC_VALUE, OFFSET_TRANSAC_LEN);
} else { } else {
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN); mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
writew(num, i2c->base + OFFSET_TRANSAC_LEN); mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
} }
/* Prepare buffer data to start transfer */ /* Prepare buffer data to start transfer */
@ -607,14 +736,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
if (left_num >= 1) if (left_num >= 1)
start_reg |= I2C_RS_MUL_CNFG; start_reg |= I2C_RS_MUL_CNFG;
} }
writew(start_reg, i2c->base + OFFSET_START); mtk_i2c_writew(i2c, start_reg, OFFSET_START);
ret = wait_for_completion_timeout(&i2c->msg_complete, ret = wait_for_completion_timeout(&i2c->msg_complete,
i2c->adap.timeout); i2c->adap.timeout);
/* Clear interrupt mask */ /* Clear interrupt mask */
writew(~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR | mtk_i2c_writew(i2c, ~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
I2C_TRANSAC_COMP), i2c->base + OFFSET_INTR_MASK); I2C_ARB_LOST | I2C_TRANSAC_COMP), OFFSET_INTR_MASK);
if (i2c->op == I2C_MASTER_WR) { if (i2c->op == I2C_MASTER_WR) {
dma_unmap_single(i2c->dev, wpaddr, dma_unmap_single(i2c->dev, wpaddr,
@ -724,8 +853,8 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
if (i2c->auto_restart) if (i2c->auto_restart)
restart_flag = I2C_RS_TRANSFER; restart_flag = I2C_RS_TRANSFER;
intr_stat = readw(i2c->base + OFFSET_INTR_STAT); intr_stat = mtk_i2c_readw(i2c, OFFSET_INTR_STAT);
writew(intr_stat, i2c->base + OFFSET_INTR_STAT); mtk_i2c_writew(i2c, intr_stat, OFFSET_INTR_STAT);
/* /*
* when occurs ack error, i2c controller generate two interrupts * when occurs ack error, i2c controller generate two interrupts
@ -737,8 +866,8 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) { if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
i2c->ignore_restart_irq = false; i2c->ignore_restart_irq = false;
i2c->irq_stat = 0; i2c->irq_stat = 0;
writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START, mtk_i2c_writew(i2c, I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG |
i2c->base + OFFSET_START); I2C_TRANSAC_START, OFFSET_START);
} else { } else {
if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag)) if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
complete(&i2c->msg_complete); complete(&i2c->msg_complete);
@ -839,6 +968,10 @@ static int mtk_i2c_probe(struct platform_device *pdev)
return PTR_ERR(i2c->clk_dma); return PTR_ERR(i2c->clk_dma);
} }
i2c->clk_arb = devm_clk_get(&pdev->dev, "arb");
if (IS_ERR(i2c->clk_arb))
i2c->clk_arb = NULL;
clk = i2c->clk_main; clk = i2c->clk_main;
if (i2c->have_pmic) { if (i2c->have_pmic) {
i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic"); i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");

View File

@ -1070,8 +1070,7 @@ static int nmk_i2c_remove(struct amba_device *adev)
/* disable the controller */ /* disable the controller */
i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
clk_disable_unprepare(dev->clk); clk_disable_unprepare(dev->clk);
if (res) release_mem_region(res->start, resource_size(res));
release_mem_region(res->start, resource_size(res));
return 0; return 0;
} }

View File

@ -26,8 +26,6 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#define OCORES_FLAG_POLL BIT(0)
/* /*
* 'process_lock' exists because ocores_process() and ocores_process_timeout() * 'process_lock' exists because ocores_process() and ocores_process_timeout()
* can't run in parallel. * can't run in parallel.
@ -37,7 +35,6 @@ struct ocores_i2c {
int iobase; int iobase;
u32 reg_shift; u32 reg_shift;
u32 reg_io_width; u32 reg_io_width;
unsigned long flags;
wait_queue_head_t wait; wait_queue_head_t wait;
struct i2c_adapter adap; struct i2c_adapter adap;
struct i2c_msg *msg; struct i2c_msg *msg;
@ -403,11 +400,7 @@ static int ocores_xfer_polling(struct i2c_adapter *adap,
static int ocores_xfer(struct i2c_adapter *adap, static int ocores_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num) struct i2c_msg *msgs, int num)
{ {
struct ocores_i2c *i2c = i2c_get_adapdata(adap); return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, false);
if (i2c->flags & OCORES_FLAG_POLL)
return ocores_xfer_polling(adap, msgs, num);
return ocores_xfer_core(i2c, msgs, num, false);
} }
static int ocores_init(struct device *dev, struct ocores_i2c *i2c) static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
@ -447,8 +440,9 @@ static u32 ocores_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
} }
static const struct i2c_algorithm ocores_algorithm = { static struct i2c_algorithm ocores_algorithm = {
.master_xfer = ocores_xfer, .master_xfer = ocores_xfer,
.master_xfer_atomic = ocores_xfer_polling,
.functionality = ocores_func, .functionality = ocores_func,
}; };
@ -673,13 +667,13 @@ static int ocores_i2c_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq == -ENXIO) { if (irq == -ENXIO) {
i2c->flags |= OCORES_FLAG_POLL; ocores_algorithm.master_xfer = ocores_xfer_polling;
} else { } else {
if (irq < 0) if (irq < 0)
return irq; return irq;
} }
if (!(i2c->flags & OCORES_FLAG_POLL)) { if (ocores_algorithm.master_xfer != ocores_xfer_polling) {
ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0, ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
pdev->name, i2c); pdev->name, i2c);
if (ret) { if (ret) {

View File

@ -269,6 +269,8 @@ static const u8 reg_map_ip_v2[] = {
[OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30, [OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
}; };
static int omap_i2c_xfer_data(struct omap_i2c_dev *omap);
static inline void omap_i2c_write_reg(struct omap_i2c_dev *omap, static inline void omap_i2c_write_reg(struct omap_i2c_dev *omap,
int reg, u16 val) int reg, u16 val)
{ {
@ -648,15 +650,28 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *omap, u8 size, bool is_rx)
(1000 * omap->speed / 8); (1000 * omap->speed / 8);
} }
static void omap_i2c_wait(struct omap_i2c_dev *omap)
{
u16 stat;
u16 mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
int count = 0;
do {
stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
count++;
} while (!(stat & mask) && count < 5);
}
/* /*
* Low level master read/write transaction. * Low level master read/write transaction.
*/ */
static int omap_i2c_xfer_msg(struct i2c_adapter *adap, static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
struct i2c_msg *msg, int stop) struct i2c_msg *msg, int stop, bool polling)
{ {
struct omap_i2c_dev *omap = i2c_get_adapdata(adap); struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
unsigned long timeout; unsigned long timeout;
u16 w; u16 w;
int ret;
dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
msg->addr, msg->len, msg->flags, stop); msg->addr, msg->len, msg->flags, stop);
@ -680,7 +695,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w); omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w);
reinit_completion(&omap->cmd_complete); if (!polling)
reinit_completion(&omap->cmd_complete);
omap->cmd_err = 0; omap->cmd_err = 0;
w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
@ -732,8 +748,18 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
* REVISIT: We should abort the transfer on signals, but the bus goes * REVISIT: We should abort the transfer on signals, but the bus goes
* into arbitration and we're currently unable to recover from it. * into arbitration and we're currently unable to recover from it.
*/ */
timeout = wait_for_completion_timeout(&omap->cmd_complete, if (!polling) {
OMAP_I2C_TIMEOUT); timeout = wait_for_completion_timeout(&omap->cmd_complete,
OMAP_I2C_TIMEOUT);
} else {
do {
omap_i2c_wait(omap);
ret = omap_i2c_xfer_data(omap);
} while (ret == -EAGAIN);
timeout = !ret;
}
if (timeout == 0) { if (timeout == 0) {
dev_err(omap->dev, "controller timed out\n"); dev_err(omap->dev, "controller timed out\n");
omap_i2c_reset(omap); omap_i2c_reset(omap);
@ -772,7 +798,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
* to do the work during IRQ processing. * to do the work during IRQ processing.
*/ */
static int static int
omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) omap_i2c_xfer_common(struct i2c_adapter *adap, struct i2c_msg msgs[], int num,
bool polling)
{ {
struct omap_i2c_dev *omap = i2c_get_adapdata(adap); struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
int i; int i;
@ -794,7 +821,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
omap->set_mpu_wkup_lat(omap->dev, omap->latency); omap->set_mpu_wkup_lat(omap->dev, omap->latency);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1))); r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)),
polling);
if (r != 0) if (r != 0)
break; break;
} }
@ -813,6 +841,18 @@ out:
return r; return r;
} }
static int
omap_i2c_xfer_irq(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
return omap_i2c_xfer_common(adap, msgs, num, false);
}
static int
omap_i2c_xfer_polling(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
return omap_i2c_xfer_common(adap, msgs, num, true);
}
static u32 static u32
omap_i2c_func(struct i2c_adapter *adap) omap_i2c_func(struct i2c_adapter *adap)
{ {
@ -1035,10 +1075,8 @@ omap_i2c_isr(int irq, void *dev_id)
return ret; return ret;
} }
static irqreturn_t static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
omap_i2c_isr_thread(int this_irq, void *dev_id)
{ {
struct omap_i2c_dev *omap = dev_id;
u16 bits; u16 bits;
u16 stat; u16 stat;
int err = 0, count = 0; int err = 0, count = 0;
@ -1056,7 +1094,8 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
if (!stat) { if (!stat) {
/* my work here is done */ /* my work here is done */
goto out; err = -EAGAIN;
break;
} }
dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat); dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat);
@ -1165,14 +1204,25 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
} }
} while (stat); } while (stat);
omap_i2c_complete_cmd(omap, err); return err;
}
static irqreturn_t
omap_i2c_isr_thread(int this_irq, void *dev_id)
{
int ret;
struct omap_i2c_dev *omap = dev_id;
ret = omap_i2c_xfer_data(omap);
if (ret != -EAGAIN)
omap_i2c_complete_cmd(omap, ret);
out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct i2c_algorithm omap_i2c_algo = { static const struct i2c_algorithm omap_i2c_algo = {
.master_xfer = omap_i2c_xfer, .master_xfer = omap_i2c_xfer_irq,
.master_xfer_atomic = omap_i2c_xfer_polling,
.functionality = omap_i2c_func, .functionality = omap_i2c_func,
}; };

View File

@ -19,6 +19,7 @@
Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800 ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800
AMD Hudson-2, ML, CZ AMD Hudson-2, ML, CZ
Hygon CZ
SMSC Victory66 SMSC Victory66
Note: we assume there can only be one device, with one or more Note: we assume there can only be one device, with one or more
@ -289,7 +290,9 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
PIIX4_dev->revision >= 0x41) || PIIX4_dev->revision >= 0x41) ||
(PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
PIIX4_dev->revision >= 0x49)) PIIX4_dev->revision >= 0x49) ||
(PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON &&
PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS))
smb_en = 0x00; smb_en = 0x00;
else else
smb_en = (aux) ? 0x28 : 0x2c; smb_en = (aux) ? 0x28 : 0x2c;
@ -361,7 +364,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
piix4_smba, i2ccfg >> 4); piix4_smba, i2ccfg >> 4);
/* Find which register is used for port selection */ /* Find which register is used for port selection */
if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD) { if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD ||
PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON) {
switch (PIIX4_dev->device) { switch (PIIX4_dev->device) {
case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS: case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS:
piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ; piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ;
@ -794,6 +798,7 @@ static const struct pci_device_id piix4_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_OSB4) }, PCI_DEVICE_ID_SERVERWORKS_OSB4) },
{ PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
@ -904,11 +909,13 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
if ((dev->vendor == PCI_VENDOR_ID_ATI && if ((dev->vendor == PCI_VENDOR_ID_ATI &&
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
dev->revision >= 0x40) || dev->revision >= 0x40) ||
dev->vendor == PCI_VENDOR_ID_AMD) { dev->vendor == PCI_VENDOR_ID_AMD ||
dev->vendor == PCI_VENDOR_ID_HYGON) {
bool notify_imc = false; bool notify_imc = false;
is_sb800 = true; is_sb800 = true;
if (dev->vendor == PCI_VENDOR_ID_AMD && if ((dev->vendor == PCI_VENDOR_ID_AMD ||
dev->vendor == PCI_VENDOR_ID_HYGON) &&
dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) { dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) {
u8 imc; u8 imc;

View File

@ -85,6 +85,7 @@
/* ICFBSCR */ /* ICFBSCR */
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */ #define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
#define RCAR_MIN_DMA_LEN 8
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG) #define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
#define RCAR_BUS_PHASE_DATA (MDBS | MIE) #define RCAR_BUS_PHASE_DATA (MDBS | MIE)
@ -398,7 +399,7 @@ static void rcar_i2c_dma_callback(void *data)
rcar_i2c_dma_unmap(priv); rcar_i2c_dma_unmap(priv);
} }
static void rcar_i2c_dma(struct rcar_i2c_priv *priv) static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
{ {
struct device *dev = rcar_i2c_priv_to_dev(priv); struct device *dev = rcar_i2c_priv_to_dev(priv);
struct i2c_msg *msg = priv->msg; struct i2c_msg *msg = priv->msg;
@ -412,9 +413,9 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
int len; int len;
/* Do various checks to see if DMA is feasible at all */ /* Do various checks to see if DMA is feasible at all */
if (IS_ERR(chan) || msg->len < 8 || !(msg->flags & I2C_M_DMA_SAFE) || if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
(read && priv->flags & ID_P_NO_RXDMA)) !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
return; return false;
if (read) { if (read) {
/* /*
@ -434,7 +435,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
dma_addr = dma_map_single(chan->device->dev, buf, len, dir); dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
if (dma_mapping_error(chan->device->dev, dma_addr)) { if (dma_mapping_error(chan->device->dev, dma_addr)) {
dev_dbg(dev, "dma map failed, using PIO\n"); dev_dbg(dev, "dma map failed, using PIO\n");
return; return false;
} }
sg_dma_len(&priv->sg) = len; sg_dma_len(&priv->sg) = len;
@ -448,7 +449,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
if (!txdesc) { if (!txdesc) {
dev_dbg(dev, "dma prep slave sg failed, using PIO\n"); dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
rcar_i2c_cleanup_dma(priv); rcar_i2c_cleanup_dma(priv);
return; return false;
} }
txdesc->callback = rcar_i2c_dma_callback; txdesc->callback = rcar_i2c_dma_callback;
@ -458,7 +459,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
if (dma_submit_error(cookie)) { if (dma_submit_error(cookie)) {
dev_dbg(dev, "submitting dma failed, using PIO\n"); dev_dbg(dev, "submitting dma failed, using PIO\n");
rcar_i2c_cleanup_dma(priv); rcar_i2c_cleanup_dma(priv);
return; return false;
} }
/* Enable DMA Master Received/Transmitted */ /* Enable DMA Master Received/Transmitted */
@ -468,6 +469,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICDMAER, TMDMAE); rcar_i2c_write(priv, ICDMAER, TMDMAE);
dma_async_issue_pending(chan); dma_async_issue_pending(chan);
return true;
} }
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
@ -478,6 +480,10 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
if (!(msr & MDE)) if (!(msr & MDE))
return; return;
/* Check if DMA can be enabled and take over */
if (priv->pos == 1 && rcar_i2c_dma(priv))
return;
if (priv->pos < msg->len) { if (priv->pos < msg->len) {
/* /*
* Prepare next data to ICRXTX register. * Prepare next data to ICRXTX register.
@ -488,13 +494,6 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
*/ */
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]); rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
priv->pos++; priv->pos++;
/*
* Try to use DMA to transmit the rest of the data if
* address transfer phase just finished.
*/
if (msr & MAT)
rcar_i2c_dma(priv);
} else { } else {
/* /*
* The last data was pushed to ICRXTX on _PREV_ empty irq. * The last data was pushed to ICRXTX on _PREV_ empty irq.
@ -921,6 +920,9 @@ static int rcar_i2c_probe(struct platform_device *pdev)
struct i2c_timings i2c_t; struct i2c_timings i2c_t;
int irq, ret; int irq, ret;
/* Otherwise logic will break because some bytes must always use PIO */
BUILD_BUG_ON_MSG(RCAR_MIN_DMA_LEN < 3, "Invalid min DMA length");
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;

View File

@ -43,6 +43,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#define RIIC_ICCR1 0x00 #define RIIC_ICCR1 0x00
#define RIIC_ICCR2 0x04 #define RIIC_ICCR2 0x04
@ -112,12 +113,10 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{ {
struct riic_dev *riic = i2c_get_adapdata(adap); struct riic_dev *riic = i2c_get_adapdata(adap);
unsigned long time_left; unsigned long time_left;
int i, ret; int i;
u8 start_bit; u8 start_bit;
ret = clk_prepare_enable(riic->clk); pm_runtime_get_sync(adap->dev.parent);
if (ret)
return ret;
if (readb(riic->base + RIIC_ICCR2) & ICCR2_BBSY) { if (readb(riic->base + RIIC_ICCR2) & ICCR2_BBSY) {
riic->err = -EBUSY; riic->err = -EBUSY;
@ -150,7 +149,7 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
} }
out: out:
clk_disable_unprepare(riic->clk); pm_runtime_put(adap->dev.parent);
return riic->err ?: num; return riic->err ?: num;
} }
@ -281,20 +280,18 @@ static const struct i2c_algorithm riic_algo = {
static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t) static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
{ {
int ret; int ret = 0;
unsigned long rate; unsigned long rate;
int total_ticks, cks, brl, brh; int total_ticks, cks, brl, brh;
ret = clk_prepare_enable(riic->clk); pm_runtime_get_sync(riic->adapter.dev.parent);
if (ret)
return ret;
if (t->bus_freq_hz > 400000) { if (t->bus_freq_hz > 400000) {
dev_err(&riic->adapter.dev, dev_err(&riic->adapter.dev,
"unsupported bus speed (%dHz). 400000 max\n", "unsupported bus speed (%dHz). 400000 max\n",
t->bus_freq_hz); t->bus_freq_hz);
clk_disable_unprepare(riic->clk); ret = -EINVAL;
return -EINVAL; goto out;
} }
rate = clk_get_rate(riic->clk); rate = clk_get_rate(riic->clk);
@ -332,8 +329,8 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
if (brl > (0x1F + 3)) { if (brl > (0x1F + 3)) {
dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n", dev_err(&riic->adapter.dev, "invalid speed (%lu). Too slow.\n",
(unsigned long)t->bus_freq_hz); (unsigned long)t->bus_freq_hz);
clk_disable_unprepare(riic->clk); ret = -EINVAL;
return -EINVAL; goto out;
} }
brh = total_ticks - brl; brh = total_ticks - brl;
@ -378,9 +375,9 @@ static int riic_init_hw(struct riic_dev *riic, struct i2c_timings *t)
riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1); riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
clk_disable_unprepare(riic->clk); out:
pm_runtime_put(riic->adapter.dev.parent);
return 0; return ret;
} }
static struct riic_irq_desc riic_irqs[] = { static struct riic_irq_desc riic_irqs[] = {
@ -439,28 +436,36 @@ static int riic_i2c_probe(struct platform_device *pdev)
i2c_parse_fw_timings(&pdev->dev, &i2c_t, true); i2c_parse_fw_timings(&pdev->dev, &i2c_t, true);
pm_runtime_enable(&pdev->dev);
ret = riic_init_hw(riic, &i2c_t); ret = riic_init_hw(riic, &i2c_t);
if (ret) if (ret)
return ret; goto out;
ret = i2c_add_adapter(adap); ret = i2c_add_adapter(adap);
if (ret) if (ret)
return ret; goto out;
platform_set_drvdata(pdev, riic); platform_set_drvdata(pdev, riic);
dev_info(&pdev->dev, "registered with %dHz bus speed\n", dev_info(&pdev->dev, "registered with %dHz bus speed\n",
i2c_t.bus_freq_hz); i2c_t.bus_freq_hz);
return 0; return 0;
out:
pm_runtime_disable(&pdev->dev);
return ret;
} }
static int riic_i2c_remove(struct platform_device *pdev) static int riic_i2c_remove(struct platform_device *pdev)
{ {
struct riic_dev *riic = platform_get_drvdata(pdev); struct riic_dev *riic = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
writeb(0, riic->base + RIIC_ICIER); writeb(0, riic->base + RIIC_ICIER);
pm_runtime_put(&pdev->dev);
i2c_del_adapter(&riic->adapter); i2c_del_adapter(&riic->adapter);
pm_runtime_disable(&pdev->dev);
return 0; return 0;
} }

View File

@ -476,8 +476,12 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
list_add_tail(&v->node, list_add_tail(&v->node,
&solutions); &solutions);
break;
} }
} }
if (p_prev == p)
break;
} }
} }

View File

@ -328,12 +328,6 @@ static int stu300_start_and_await_event(struct stu300_dev *dev,
{ {
int ret; int ret;
if (unlikely(irqs_disabled())) {
/* TODO: implement polling for this case if need be. */
WARN(1, "irqs are disabled, cannot poll for event\n");
return -EIO;
}
/* Lock command issue, fill in an event we wait for */ /* Lock command issue, fill in an event we wait for */
spin_lock_irq(&dev->cmd_issue_lock); spin_lock_irq(&dev->cmd_issue_lock);
init_completion(&dev->cmd_complete); init_completion(&dev->cmd_complete);
@ -380,13 +374,6 @@ static int stu300_await_event(struct stu300_dev *dev,
{ {
int ret; int ret;
if (unlikely(irqs_disabled())) {
/* TODO: implement polling for this case if need be. */
dev_err(&dev->pdev->dev, "irqs are disabled on this "
"system!\n");
return -EIO;
}
/* Is it already here? */ /* Is it already here? */
spin_lock_irq(&dev->cmd_issue_lock); spin_lock_irq(&dev->cmd_issue_lock);
dev->cmd_err = STU300_ERROR_NONE; dev->cmd_err = STU300_ERROR_NONE;
@ -846,6 +833,13 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
return num; return num;
} }
static int stu300_xfer_todo(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
/* TODO: implement polling for this case if need be. */
WARN(1, "%s: atomic transfers not implemented\n", dev_name(&adap->dev));
return -EOPNOTSUPP;
}
static u32 stu300_func(struct i2c_adapter *adap) static u32 stu300_func(struct i2c_adapter *adap)
{ {
/* This is the simplest thing you can think of... */ /* This is the simplest thing you can think of... */
@ -853,8 +847,9 @@ static u32 stu300_func(struct i2c_adapter *adap)
} }
static const struct i2c_algorithm stu300_algo = { static const struct i2c_algorithm stu300_algo = {
.master_xfer = stu300_xfer, .master_xfer = stu300_xfer,
.functionality = stu300_func, .master_xfer_atomic = stu300_xfer_todo,
.functionality = stu300_func,
}; };
static const struct i2c_adapter_quirks stu300_quirks = { static const struct i2c_adapter_quirks stu300_quirks = {

View File

@ -207,7 +207,8 @@ static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num)
static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c, static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
struct mrq_i2c_request *request, struct mrq_i2c_request *request,
struct mrq_i2c_response *response) struct mrq_i2c_response *response,
bool atomic)
{ {
struct tegra_bpmp_message msg; struct tegra_bpmp_message msg;
int err; int err;
@ -222,7 +223,7 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
msg.rx.data = response; msg.rx.data = response;
msg.rx.size = sizeof(*response); msg.rx.size = sizeof(*response);
if (irqs_disabled()) if (atomic)
err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg); err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
else else
err = tegra_bpmp_transfer(i2c->bpmp, &msg); err = tegra_bpmp_transfer(i2c->bpmp, &msg);
@ -230,8 +231,9 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
return err; return err;
} }
static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter, static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num) struct i2c_msg *msgs, int num,
bool atomic)
{ {
struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter); struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
struct mrq_i2c_response response; struct mrq_i2c_response response;
@ -253,7 +255,7 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
return err; return err;
} }
err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response); err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic);
if (err < 0) { if (err < 0) {
dev_err(i2c->dev, "failed to transfer message: %d\n", err); dev_err(i2c->dev, "failed to transfer message: %d\n", err);
return err; return err;
@ -268,6 +270,18 @@ static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
return num; return num;
} }
static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, false);
}
static int tegra_bpmp_i2c_xfer_atomic(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
return tegra_bpmp_i2c_xfer_common(adapter, msgs, num, true);
}
static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter) static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
{ {
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
@ -276,6 +290,7 @@ static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
static const struct i2c_algorithm tegra_bpmp_i2c_algo = { static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
.master_xfer = tegra_bpmp_i2c_xfer, .master_xfer = tegra_bpmp_i2c_xfer,
.master_xfer_atomic = tegra_bpmp_i2c_xfer_atomic,
.functionality = tegra_bpmp_i2c_func, .functionality = tegra_bpmp_i2c_func,
}; };

View File

@ -1871,8 +1871,10 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (WARN_ON(!msgs || num < 1)) if (WARN_ON(!msgs || num < 1))
return -EINVAL; return -EINVAL;
if (WARN_ON(test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)))
return -ESHUTDOWN; ret = __i2c_check_suspended(adap);
if (ret)
return ret;
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -1894,7 +1896,11 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
/* Retry automatically on arbitration loss */ /* Retry automatically on arbitration loss */
orig_jiffies = jiffies; orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) { for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num); if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic)
ret = adap->algo->master_xfer_atomic(adap, msgs, num);
else
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN) if (ret != -EAGAIN)
break; break;
if (time_after(jiffies, orig_jiffies + adap->timeout)) if (time_after(jiffies, orig_jiffies + adap->timeout))
@ -1950,14 +1956,9 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
* one (discarding status on the second message) or errno * one (discarding status on the second message) or errno
* (discarding status on the first one). * (discarding status on the first one).
*/ */
if (in_atomic() || irqs_disabled()) { ret = __i2c_lock_bus_helper(adap);
ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); if (ret)
if (!ret) return ret;
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
}
ret = __i2c_transfer(adap, msgs, num); ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);

View File

@ -20,6 +20,8 @@
#include <linux/i2c-smbus.h> #include <linux/i2c-smbus.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "i2c-core.h"
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <trace/events/smbus.h> #include <trace/events/smbus.h>
@ -530,7 +532,10 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
{ {
s32 res; s32 res;
i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); res = __i2c_lock_bus_helper(adapter);
if (res)
return res;
res = __i2c_smbus_xfer(adapter, addr, flags, read_write, res = __i2c_smbus_xfer(adapter, addr, flags, read_write,
command, protocol, data); command, protocol, data);
i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
@ -543,10 +548,17 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
unsigned short flags, char read_write, unsigned short flags, char read_write,
u8 command, int protocol, union i2c_smbus_data *data) u8 command, int protocol, union i2c_smbus_data *data)
{ {
int (*xfer_func)(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
unsigned long orig_jiffies; unsigned long orig_jiffies;
int try; int try;
s32 res; s32 res;
res = __i2c_check_suspended(adapter);
if (res)
return res;
/* If enabled, the following two tracepoints are conditional on /* If enabled, the following two tracepoints are conditional on
* read_write and protocol. * read_write and protocol.
*/ */
@ -557,13 +569,20 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
if (adapter->algo->smbus_xfer) { xfer_func = adapter->algo->smbus_xfer;
if (i2c_in_atomic_xfer_mode()) {
if (adapter->algo->smbus_xfer_atomic)
xfer_func = adapter->algo->smbus_xfer_atomic;
else if (adapter->algo->master_xfer_atomic)
xfer_func = NULL; /* fallback to I2C emulation */
}
if (xfer_func) {
/* Retry automatically on arbitration loss */ /* Retry automatically on arbitration loss */
orig_jiffies = jiffies; orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) { for (res = 0, try = 0; try <= adapter->retries; try++) {
res = adapter->algo->smbus_xfer(adapter, addr, flags, res = xfer_func(adapter, addr, flags, read_write,
read_write, command, command, protocol, data);
protocol, data);
if (res != -EAGAIN) if (res != -EAGAIN)
break; break;
if (time_after(jiffies, if (time_after(jiffies,

View File

@ -29,6 +29,42 @@ extern int __i2c_first_dynamic_bus_num;
int i2c_check_7bit_addr_validity_strict(unsigned short addr); int i2c_check_7bit_addr_validity_strict(unsigned short addr);
/*
* We only allow atomic transfers for very late communication, e.g. to send
* the powerdown command to a PMIC. Atomic transfers are a corner case and not
* for generic use!
*/
static inline bool i2c_in_atomic_xfer_mode(void)
{
return system_state > SYSTEM_RUNNING && irqs_disabled();
}
static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap)
{
int ret = 0;
if (i2c_in_atomic_xfer_mode()) {
WARN(!adap->algo->master_xfer_atomic && !adap->algo->smbus_xfer_atomic,
"No atomic I2C transfer handler for '%s'\n", dev_name(&adap->dev));
ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT) ? 0 : -EAGAIN;
} else {
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
}
return ret;
}
static inline int __i2c_check_suspended(struct i2c_adapter *adap)
{
if (test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)) {
if (!test_and_set_bit(I2C_ALF_SUSPEND_REPORTED, &adap->locked_flags))
dev_WARN(&adap->dev, "Transfer while suspended\n");
return -ESHUTDOWN;
}
return 0;
}
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
const struct acpi_device_id * const struct acpi_device_id *
i2c_acpi_match_device(const struct acpi_device_id *matches, i2c_acpi_match_device(const struct acpi_device_id *matches,

View File

@ -310,12 +310,18 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
else else
priv->algo.master_xfer = __i2c_mux_master_xfer; priv->algo.master_xfer = __i2c_mux_master_xfer;
} }
if (parent->algo->master_xfer_atomic)
priv->algo.master_xfer_atomic = priv->algo.master_xfer;
if (parent->algo->smbus_xfer) { if (parent->algo->smbus_xfer) {
if (muxc->mux_locked) if (muxc->mux_locked)
priv->algo.smbus_xfer = i2c_mux_smbus_xfer; priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
else else
priv->algo.smbus_xfer = __i2c_mux_smbus_xfer; priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
} }
if (parent->algo->smbus_xfer_atomic)
priv->algo.smbus_xfer_atomic = priv->algo.smbus_xfer;
priv->algo.functionality = i2c_mux_functionality; priv->algo.functionality = i2c_mux_functionality;
/* Now fill out new adapter structure */ /* Now fill out new adapter structure */

View File

@ -99,6 +99,8 @@ static int i2c_demux_activate_master(struct i2c_demux_pinctrl_priv *priv, u32 ne
/* Now fill out current adapter structure. cur_chan must be up to date */ /* Now fill out current adapter structure. cur_chan must be up to date */
priv->algo.master_xfer = i2c_demux_master_xfer; priv->algo.master_xfer = i2c_demux_master_xfer;
if (adap->algo->master_xfer_atomic)
priv->algo.master_xfer_atomic = i2c_demux_master_xfer;
priv->algo.functionality = i2c_demux_functionality; priv->algo.functionality = i2c_demux_functionality;
snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name), snprintf(priv->cur_adap.name, sizeof(priv->cur_adap.name),
@ -219,8 +221,8 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
priv = devm_kzalloc(&pdev->dev, sizeof(*priv) priv = devm_kzalloc(&pdev->dev, struct_size(priv, chan, num_chan),
+ num_chan * sizeof(struct i2c_demux_pinctrl_chan), GFP_KERNEL); GFP_KERNEL);
props = devm_kcalloc(&pdev->dev, num_chan, sizeof(*props), GFP_KERNEL); props = devm_kcalloc(&pdev->dev, num_chan, sizeof(*props), GFP_KERNEL);

View File

@ -22,7 +22,6 @@
#include <linux/i2c-mux.h> #include <linux/i2c-mux.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_data/pca954x.h>
#include <linux/slab.h> #include <linux/slab.h>
/* /*
@ -287,10 +286,8 @@ static int pca9541_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct i2c_adapter *adap = client->adapter; struct i2c_adapter *adap = client->adapter;
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct i2c_mux_core *muxc; struct i2c_mux_core *muxc;
struct pca9541 *data; struct pca9541 *data;
int force;
int ret; int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
@ -306,9 +303,6 @@ static int pca9541_probe(struct i2c_client *client,
/* Create mux adapter */ /* Create mux adapter */
force = 0;
if (pdata)
force = pdata->modes[0].adap_id;
muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data),
I2C_MUX_ARBITRATOR, I2C_MUX_ARBITRATOR,
pca9541_select_chan, pca9541_release_chan); pca9541_select_chan, pca9541_release_chan);
@ -320,7 +314,7 @@ static int pca9541_probe(struct i2c_client *client,
i2c_set_clientdata(client, muxc); i2c_set_clientdata(client, muxc);
ret = i2c_mux_add_adapter(muxc, force, 0, 0); ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
if (ret) if (ret)
return ret; return ret;

View File

@ -46,10 +46,10 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/platform_data/pca954x.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <dt-bindings/mux/mux.h>
#define PCA954X_MAX_NCHANS 8 #define PCA954X_MAX_NCHANS 8
@ -85,7 +85,9 @@ struct pca954x {
const struct chip_desc *chip; const struct chip_desc *chip;
u8 last_chan; /* last register value */ u8 last_chan; /* last register value */
u8 deselect; /* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
s8 idle_state;
struct i2c_client *client; struct i2c_client *client;
struct irq_domain *irq; struct irq_domain *irq;
@ -254,15 +256,71 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
{ {
struct pca954x *data = i2c_mux_priv(muxc); struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client; struct i2c_client *client = data->client;
s8 idle_state;
if (!(data->deselect & (1 << chan))) idle_state = READ_ONCE(data->idle_state);
return 0; if (idle_state >= 0)
/* Set the mux back to a predetermined channel */
return pca954x_select_chan(muxc, idle_state);
/* Deselect active channel */ if (idle_state == MUX_IDLE_DISCONNECT) {
data->last_chan = 0; /* Deselect active channel */
return pca954x_reg_write(muxc->parent, client, data->last_chan); data->last_chan = 0;
return pca954x_reg_write(muxc->parent, client,
data->last_chan);
}
/* otherwise leave as-is */
return 0;
} }
static ssize_t idle_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
}
static ssize_t idle_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
(val < 0 || val >= data->chip->nchans))
return -EINVAL;
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
WRITE_ONCE(data->idle_state, val);
/*
* Set the mux into a state consistent with the new
* idle_state.
*/
if (data->last_chan || val != MUX_IDLE_DISCONNECT)
ret = pca954x_deselect_mux(muxc, 0);
i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(idle_state);
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id) static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
{ {
struct pca954x *data = dev_id; struct pca954x *data = dev_id;
@ -329,8 +387,11 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
static void pca954x_cleanup(struct i2c_mux_core *muxc) static void pca954x_cleanup(struct i2c_mux_core *muxc)
{ {
struct pca954x *data = i2c_mux_priv(muxc); struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
int c, irq; int c, irq;
device_remove_file(&client->dev, &dev_attr_idle_state);
if (data->irq) { if (data->irq) {
for (c = 0; c < data->chip->nchans; c++) { for (c = 0; c < data->chip->nchans; c++) {
irq = irq_find_mapping(data->irq, c); irq = irq_find_mapping(data->irq, c);
@ -348,14 +409,13 @@ static int pca954x_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct i2c_adapter *adap = client->adapter; struct i2c_adapter *adap = client->adapter;
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
bool idle_disconnect_dt; bool idle_disconnect_dt;
struct gpio_desc *gpio; struct gpio_desc *gpio;
int num, force, class;
struct i2c_mux_core *muxc; struct i2c_mux_core *muxc;
struct pca954x *data; struct pca954x *data;
int num;
int ret; int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
@ -412,9 +472,12 @@ static int pca954x_probe(struct i2c_client *client,
} }
data->last_chan = 0; /* force the first selection */ data->last_chan = 0; /* force the first selection */
data->idle_state = MUX_IDLE_AS_IS;
idle_disconnect_dt = np && idle_disconnect_dt = np &&
of_property_read_bool(np, "i2c-mux-idle-disconnect"); of_property_read_bool(np, "i2c-mux-idle-disconnect");
if (idle_disconnect_dt)
data->idle_state = MUX_IDLE_DISCONNECT;
ret = pca954x_irq_setup(muxc); ret = pca954x_irq_setup(muxc);
if (ret) if (ret)
@ -422,24 +485,7 @@ static int pca954x_probe(struct i2c_client *client,
/* Now create an adapter for each channel */ /* Now create an adapter for each channel */
for (num = 0; num < data->chip->nchans; num++) { for (num = 0; num < data->chip->nchans; num++) {
bool idle_disconnect_pd = false; ret = i2c_mux_add_adapter(muxc, 0, num, 0);
force = 0; /* dynamic adap number */
class = 0; /* no class by default */
if (pdata) {
if (num < pdata->num_modes) {
/* force static number */
force = pdata->modes[num].adap_id;
class = pdata->modes[num].class;
} else
/* discard unconfigured channels */
break;
idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
}
data->deselect |= (idle_disconnect_pd ||
idle_disconnect_dt) << num;
ret = i2c_mux_add_adapter(muxc, force, num, class);
if (ret) if (ret)
goto fail_cleanup; goto fail_cleanup;
} }
@ -453,6 +499,12 @@ static int pca954x_probe(struct i2c_client *client,
goto fail_cleanup; goto fail_cleanup;
} }
/*
* The attr probably isn't going to be needed in most cases,
* so don't fail completely on error.
*/
device_create_file(dev, &dev_attr_idle_state);
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n", dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
num, data->chip->muxtype == pca954x_ismux num, data->chip->muxtype == pca954x_ismux
? "mux" : "switch", client->name); ? "mux" : "switch", client->name);

View File

@ -33,6 +33,7 @@ struct i2c_algo_bit_data {
minimum 5 us for standard-mode I2C and SMBus, minimum 5 us for standard-mode I2C and SMBus,
maximum 50 us for SMBus */ maximum 50 us for SMBus */
int timeout; /* in jiffies */ int timeout; /* in jiffies */
bool can_do_atomic; /* callbacks don't sleep, we can be atomic */
}; };
int i2c_bit_add_bus(struct i2c_adapter *); int i2c_bit_add_bus(struct i2c_adapter *);

View File

@ -499,9 +499,13 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
* @master_xfer: Issue a set of i2c transactions to the given I2C adapter * @master_xfer: Issue a set of i2c transactions to the given I2C adapter
* defined by the msgs array, with num messages available to transfer via * defined by the msgs array, with num messages available to transfer via
* the adapter specified by adap. * the adapter specified by adap.
* @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context
* so e.g. PMICs can be accessed very late before shutdown. Optional.
* @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
* is not present, then the bus layer will try and convert the SMBus calls * is not present, then the bus layer will try and convert the SMBus calls
* into I2C transfers instead. * into I2C transfers instead.
* @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context
* so e.g. PMICs can be accessed very late before shutdown. Optional.
* @functionality: Return the flags that this algorithm/adapter pair supports * @functionality: Return the flags that this algorithm/adapter pair supports
* from the I2C_FUNC_* flags. * from the I2C_FUNC_* flags.
* @reg_slave: Register given client to I2C slave mode of this adapter * @reg_slave: Register given client to I2C slave mode of this adapter
@ -512,25 +516,33 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info,
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
* to name two of the most common. * to name two of the most common.
* *
* The return codes from the @master_xfer field should indicate the type of * The return codes from the @master_xfer{_atomic} fields should indicate the
* error code that occurred during the transfer, as documented in the kernel * type of error code that occurred during the transfer, as documented in the
* Documentation file Documentation/i2c/fault-codes. * Kernel Documentation file Documentation/i2c/fault-codes.
*/ */
struct i2c_algorithm { struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer /*
to NULL. If an adapter algorithm can do SMBus access, set * If an adapter algorithm can't do I2C-level access, set master_xfer
smbus_xfer. If set to NULL, the SMBus protocol is simulated * to NULL. If an adapter algorithm can do SMBus access, set
using common I2C messages */ * smbus_xfer. If set to NULL, the SMBus protocol is simulated
/* master_xfer should return the number of messages successfully * using common I2C messages.
processed, or a negative value on error */ *
* master_xfer should return the number of messages successfully
* processed, or a negative value on error
*/
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num); int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, int (*master_xfer_atomic)(struct i2c_adapter *adap,
unsigned short flags, char read_write, struct i2c_msg *msgs, int num);
u8 command, int size, union i2c_smbus_data *data); int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */ /* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *); u32 (*functionality)(struct i2c_adapter *adap);
#if IS_ENABLED(CONFIG_I2C_SLAVE) #if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client); int (*reg_slave)(struct i2c_client *client);
@ -682,7 +694,8 @@ struct i2c_adapter {
int retries; int retries;
struct device dev; /* the adapter device */ struct device dev; /* the adapter device */
unsigned long locked_flags; /* owned by the I2C core */ unsigned long locked_flags; /* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED 0 #define I2C_ALF_IS_SUSPENDED 0
#define I2C_ALF_SUSPEND_REPORTED 1
int nr; int nr;
char name[48]; char name[48];

View File

@ -1,48 +0,0 @@
/*
*
* pca954x.h - I2C multiplexer/switch support
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
* Michael Lawnick <michael.lawnick.ext@nsn.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _LINUX_I2C_PCA954X_H
#define _LINUX_I2C_PCA954X_H
/* Platform data for the PCA954x I2C multiplexers */
/* Per channel initialisation data:
* @adap_id: bus number for the adapter. 0 = don't care
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
* of this channel after transaction.
*
*/
struct pca954x_platform_mode {
int adap_id;
unsigned int deselect_on_exit:1;
unsigned int class;
};
/* Per mux/switch data, used with i2c_register_board_info */
struct pca954x_platform_data {
struct pca954x_platform_mode *modes;
int num_modes;
};
#endif /* _LINUX_I2C_PCA954X_H */