mirror of
https://github.com/a52xq-dev/kernel.git
synced 2026-10-18 21:58:11 +07:00
Merge branch 'i2c/for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - the first series of making i2c_device_id optional instead of mandatory (in favor of alternatives like of_device_id). This involves adding a new probe callback (probe_new) which removes some peculiarities I2C had for a long time now. The new probe is matching the other subsystems now and the old one will be removed once all users are converted. It is expected to take a while but there is ongoing interest in that. - SMBus Host Notify introduced 4.9 got refactored. They are now using interrupts instead of the alert callback which solves multiple issues. - new drivers for iMX LowPower I2C, Mellanox CPLD and its I2C mux - significant refactoring for bcm2835 driver - the usual set of driver updates and improvements * 'i2c/for-4.10' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (46 commits) i2c: fsl-lpi2c: read lpi2c fifo size in probe() i2c: octeon: thunderx: Remove double-check after interrupt i2c: octeon: thunderx: TWSI software reset in recovery i2c: cadence: Allow Cadence I2C to be selected for Cadence Xtensa CPUs i2c: sh_mobile: Add per-Generation fallback bindings i2c: rcar: Add per-Generation fallback bindings i2c: imx-lpi2c: add low power i2c bus driver dt-bindings: i2c: imx-lpi2c: add devicetree bindings i2c: designware-pcidrv: Add 10bit address feature to medfield/merrifield i2c: pxa: Add support for the I2C units found in Armada 3700 i2c: pxa: Add definition of fast and high speed modes via the regs layout dt-bindings: i2c: pxa: Update the documentation for the Armada 3700 i2c: qup: support SMBus block read i2c: qup: add ACPI support i2c: designware: Consolidate default functionality bits i2c: i2c-mux-gpio: update mux with gpiod_set_array_value_cansleep i2c: mux: pca954x: Add ACPI support for pca954x i2c: use an IRQ to report Host Notify events, not alert i2c: i801: remove SMBNTFDDAT reads as they always seem to return 0 i2c: i801: use the BIT() macro for FEATURES_* also ...
This commit is contained in:
commit
8600b697cd
42 changed files with 2264 additions and 429 deletions
20
Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.txt
Normal file
20
Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
* Freescale Low Power Inter IC (LPI2C) for i.MX
|
||||
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "fsl,imx7ulp-lpi2c" for LPI2C compatible with the one integrated on i.MX7ULP soc
|
||||
- "fsl,imx8dv-lpi2c" for LPI2C compatible with the one integrated on i.MX8DV soc
|
||||
- reg : address and length of the lpi2c master registers
|
||||
- interrupt-parent : core interrupt controller
|
||||
- interrupts : lpi2c interrupt
|
||||
- clocks : lpi2c clock specifier
|
||||
|
||||
Examples:
|
||||
|
||||
lpi2c7: lpi2c7@40A50000 {
|
||||
compatible = "fsl,imx8dv-lpi2c";
|
||||
reg = <0x40A50000 0x10000>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX7ULP_CLK_LPI2C7>;
|
||||
};
|
||||
|
|
@ -7,6 +7,7 @@ Required properties :
|
|||
compatible processor, e.g. pxa168, pxa910, mmp2, mmp3.
|
||||
For the pxa2xx/pxa3xx, an additional node "mrvl,pxa-i2c" is required
|
||||
as shown in the example below.
|
||||
For the Armada 3700, the compatible should be "marvell,armada-3700-i2c".
|
||||
|
||||
Recommended properties :
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,25 @@
|
|||
I2C for R-Car platforms
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of
|
||||
"renesas,i2c-rcar"
|
||||
"renesas,i2c-r8a7778"
|
||||
"renesas,i2c-r8a7779"
|
||||
"renesas,i2c-r8a7790"
|
||||
"renesas,i2c-r8a7791"
|
||||
"renesas,i2c-r8a7792"
|
||||
"renesas,i2c-r8a7793"
|
||||
"renesas,i2c-r8a7794"
|
||||
"renesas,i2c-r8a7795"
|
||||
"renesas,i2c-r8a7796"
|
||||
- compatible:
|
||||
"renesas,i2c-r8a7778" if the device is a part of a R8A7778 SoC.
|
||||
"renesas,i2c-r8a7779" if the device is a part of a R8A7779 SoC.
|
||||
"renesas,i2c-r8a7790" if the device is a part of a R8A7790 SoC.
|
||||
"renesas,i2c-r8a7791" if the device is a part of a R8A7791 SoC.
|
||||
"renesas,i2c-r8a7792" if the device is a part of a R8A7792 SoC.
|
||||
"renesas,i2c-r8a7793" if the device is a part of a R8A7793 SoC.
|
||||
"renesas,i2c-r8a7794" if the device is a part of a R8A7794 SoC.
|
||||
"renesas,i2c-r8a7795" if the device is a part of a R8A7795 SoC.
|
||||
"renesas,i2c-r8a7796" if the device is a part of a R8A7796 SoC.
|
||||
"renesas,rcar-gen1-i2c" for a generic R-Car Gen1 compatible device.
|
||||
"renesas,rcar-gen2-i2c" for a generic R-Car Gen2 compatible device.
|
||||
"renesas,rcar-gen3-i2c" for a generic R-Car Gen3 compatible device.
|
||||
"renesas,i2c-rcar" (deprecated)
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first followed
|
||||
by the generic version.
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: interrupt specifier.
|
||||
|
|
@ -33,7 +41,7 @@ Examples :
|
|||
i2c0: i2c@e6508000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "renesas,i2c-r8a7791";
|
||||
compatible = "renesas,i2c-r8a7791", "renesas,rcar-gen2-i2c";
|
||||
reg = <0 0xe6508000 0 0x40>;
|
||||
interrupts = <0 287 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp9_clks R8A7791_CLK_I2C0>;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
Device tree configuration for Renesas IIC (sh_mobile) driver
|
||||
|
||||
Required properties:
|
||||
- compatible : "renesas,iic-<soctype>". "renesas,rmobile-iic" as fallback
|
||||
Examples with soctypes are:
|
||||
- compatible :
|
||||
- "renesas,iic-r8a73a4" (R-Mobile APE6)
|
||||
- "renesas,iic-r8a7740" (R-Mobile A1)
|
||||
- "renesas,iic-r8a7790" (R-Car H2)
|
||||
|
|
@ -12,6 +11,17 @@ Required properties:
|
|||
- "renesas,iic-r8a7794" (R-Car E2)
|
||||
- "renesas,iic-r8a7795" (R-Car H3)
|
||||
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
||||
- "renesas,rcar-gen2-iic" (generic R-Car Gen2 compatible device)
|
||||
- "renesas,rcar-gen3-iic" (generic R-Car Gen3 compatible device)
|
||||
- "renesas,rmobile-iic" (generic device)
|
||||
|
||||
When compatible with a generic R-Car version, nodes
|
||||
must list the SoC-specific version corresponding to
|
||||
the platform first followed by the generic R-Car
|
||||
version.
|
||||
|
||||
renesas,rmobile-iic must always follow.
|
||||
|
||||
- reg : address start and address range size of device
|
||||
- interrupts : interrupt of device
|
||||
- clocks : clock for device
|
||||
|
|
@ -31,7 +41,8 @@ Pinctrl properties might be needed, too. See there.
|
|||
Example:
|
||||
|
||||
iic0: i2c@e6500000 {
|
||||
compatible = "renesas,iic-r8a7790", "renesas,rmobile-iic";
|
||||
compatible = "renesas,iic-r8a7790", "renesas,rcar-gen2-iic",
|
||||
"renesas,rmobile-iic";
|
||||
reg = <0 0xe6500000 0 0x425>;
|
||||
interrupts = <0 174 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp3_clks R8A7790_CLK_IIC0>;
|
||||
|
|
|
|||
47
Documentation/i2c/busses/i2c-mlxcpld
Normal file
47
Documentation/i2c/busses/i2c-mlxcpld
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
Driver i2c-mlxcpld
|
||||
|
||||
Author: Michael Shych <michaelsh@mellanox.com>
|
||||
|
||||
This is the Mellanox I2C controller logic, implemented in Lattice CPLD
|
||||
device.
|
||||
Device supports:
|
||||
- Master mode.
|
||||
- One physical bus.
|
||||
- Polling mode.
|
||||
|
||||
This controller is equipped within the next Mellanox systems:
|
||||
"msx6710", "msx6720", "msb7700", "msn2700", "msx1410", "msn2410", "msb7800",
|
||||
"msn2740", "msn2100".
|
||||
|
||||
The next transaction types are supported:
|
||||
- Receive Byte/Block.
|
||||
- Send Byte/Block.
|
||||
- Read Byte/Block.
|
||||
- Write Byte/Block.
|
||||
|
||||
Registers:
|
||||
CTRL 0x1 - control reg.
|
||||
Resets all the registers.
|
||||
HALF_CYC 0x4 - cycle reg.
|
||||
Configure the width of I2C SCL half clock cycle (in 4 LPC_CLK
|
||||
units).
|
||||
I2C_HOLD 0x5 - hold reg.
|
||||
OE (output enable) is delayed by value set to this register
|
||||
(in LPC_CLK units)
|
||||
CMD 0x6 - command reg.
|
||||
Bit 0, 0 = write, 1 = read.
|
||||
Bits [7:1] - the 7bit Address of the I2C device.
|
||||
It should be written last as it triggers an I2C transaction.
|
||||
NUM_DATA 0x7 - data size reg.
|
||||
Number of data bytes to write in read transaction
|
||||
NUM_ADDR 0x8 - address reg.
|
||||
Number of address bytes to write in read transaction.
|
||||
STATUS 0x9 - status reg.
|
||||
Bit 0 - transaction is completed.
|
||||
Bit 4 - ACK/NACK.
|
||||
DATAx 0xa - 0x54 - 68 bytes data buffer regs.
|
||||
For write transaction address is specified in four first bytes
|
||||
(DATA1 - DATA4), data starting from DATA4.
|
||||
For read transactions address is sent in a separate transaction and
|
||||
specified in the four first bytes (DATA0 - DATA3). Data is read
|
||||
starting from DATA0.
|
||||
|
|
@ -200,10 +200,14 @@ alerting device's address.
|
|||
[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]
|
||||
|
||||
This is implemented in the following way in the Linux kernel:
|
||||
* I2C bus drivers which support SMBus Host Notify should call
|
||||
i2c_setup_smbus_host_notify() to setup SMBus Host Notify support.
|
||||
* I2C drivers for devices which can trigger SMBus Host Notify should implement
|
||||
the optional alert() callback.
|
||||
* I2C bus drivers which support SMBus Host Notify should report
|
||||
I2C_FUNC_SMBUS_HOST_NOTIFY.
|
||||
* I2C bus drivers trigger SMBus Host Notify by a call to
|
||||
i2c_handle_smbus_host_notify().
|
||||
* I2C drivers for devices which can trigger SMBus Host Notify will have
|
||||
client->irq assigned to a Host Notify IRQ if noone else specified an other.
|
||||
|
||||
There is currently no way to retrieve the data parameter from the client.
|
||||
|
||||
|
||||
Packet Error Checking (PEC)
|
||||
|
|
|
|||
|
|
@ -8023,6 +8023,15 @@ W: http://www.mellanox.com
|
|||
Q: http://patchwork.ozlabs.org/project/netdev/list/
|
||||
F: drivers/net/ethernet/mellanox/mlxsw/
|
||||
|
||||
MELLANOX MLXCPLD I2C AND MUX DRIVER
|
||||
M: Vadim Pasternak <vadimp@mellanox.com>
|
||||
M: Michael Shych <michaelsh@mellanox.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/i2c/busses/i2c-mlxcpld.c
|
||||
F: drivers/i2c/muxes/i2c-mux-mlxcpld.c
|
||||
F: Documentation/i2c/busses/i2c-mlxcpld
|
||||
|
||||
MELLANOX MLXCPLD LED DRIVER
|
||||
M: Vadim Pasternak <vadimp@mellanox.com>
|
||||
L: linux-leds@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ menu "I2C support"
|
|||
config I2C
|
||||
tristate "I2C support"
|
||||
select RT_MUTEXES
|
||||
select IRQ_DOMAIN
|
||||
---help---
|
||||
I2C (pronounce: I-squared-C) is a slow serial bus protocol used in
|
||||
many micro controller applications and developed by Philips. SMBus,
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ config I2C_BLACKFIN_TWI_CLK_KHZ
|
|||
|
||||
config I2C_CADENCE
|
||||
tristate "Cadence I2C Controller"
|
||||
depends on ARCH_ZYNQ || ARM64
|
||||
depends on ARCH_ZYNQ || ARM64 || XTENSA
|
||||
help
|
||||
Say yes here to select Cadence I2C Host Controller. This controller is
|
||||
e.g. used by Xilinx Zynq.
|
||||
|
|
@ -597,6 +597,16 @@ config I2C_IMX
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-imx.
|
||||
|
||||
config I2C_IMX_LPI2C
|
||||
tristate "IMX Low Power I2C interface"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want to use the Low Power IIC bus controller
|
||||
on the Freescale i.MX processors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-imx-lpi2c.
|
||||
|
||||
config I2C_IOP3XX
|
||||
tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
|
||||
depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX
|
||||
|
|
@ -763,7 +773,7 @@ config I2C_PUV3
|
|||
|
||||
config I2C_PXA
|
||||
tristate "Intel PXA2XX I2C adapter"
|
||||
depends on ARCH_PXA || ARCH_MMP || (X86_32 && PCI && OF)
|
||||
depends on ARCH_PXA || ARCH_MMP || ARCH_MVEBU || (X86_32 && PCI && OF)
|
||||
help
|
||||
If you have devices in the PXA I2C bus, say yes to this option.
|
||||
This driver can also be built as a module. If so, the module
|
||||
|
|
@ -1150,6 +1160,17 @@ config I2C_ELEKTOR
|
|||
This support is also available as a module. If so, the module
|
||||
will be called i2c-elektor.
|
||||
|
||||
config I2C_MLXCPLD
|
||||
tristate "Mellanox I2C driver"
|
||||
depends on X86_64
|
||||
help
|
||||
This exposes the Mellanox platform I2C busses to the linux I2C layer
|
||||
for X86 based systems.
|
||||
Controller is implemented as CPLD logic.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called as i2c-mlxcpld.
|
||||
|
||||
config I2C_PCA_ISA
|
||||
tristate "PCA9564/PCA9665 on an ISA bus"
|
||||
depends on ISA
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o
|
|||
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
|
||||
obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o
|
||||
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
|
||||
obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
|
||||
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
|
||||
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
|
||||
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
|
||||
|
|
@ -116,6 +117,7 @@ obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o
|
|||
obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
|
||||
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
|
||||
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
|
||||
obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o
|
||||
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
|
||||
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
||||
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
|
||||
|
|
|
|||
|
|
@ -489,7 +489,7 @@ static const struct i2c_algorithm axxia_i2c_algo = {
|
|||
.functionality = axxia_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter_quirks axxia_i2c_quirks = {
|
||||
static const struct i2c_adapter_quirks axxia_i2c_quirks = {
|
||||
.max_read_len = 255,
|
||||
.max_write_len = 255,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ static const struct i2c_algorithm bcm_iproc_algo = {
|
|||
.functionality = bcm_iproc_i2c_functionality,
|
||||
};
|
||||
|
||||
static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
|
||||
static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
|
||||
/* need to reserve one byte in the FIFO for the slave address */
|
||||
.max_read_len = M_TX_RX_FIFO_SIZE - 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,20 +50,19 @@
|
|||
#define BCM2835_I2C_S_CLKT BIT(9)
|
||||
#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */
|
||||
|
||||
#define BCM2835_I2C_BITMSK_S 0x03FF
|
||||
|
||||
#define BCM2835_I2C_CDIV_MIN 0x0002
|
||||
#define BCM2835_I2C_CDIV_MAX 0xFFFE
|
||||
|
||||
#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
|
||||
|
||||
struct bcm2835_i2c_dev {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
u32 bus_clk_rate;
|
||||
struct i2c_adapter adapter;
|
||||
struct completion completion;
|
||||
struct i2c_msg *curr_msg;
|
||||
int num_msgs;
|
||||
u32 msg_err;
|
||||
u8 *msg_buf;
|
||||
size_t msg_buf_remaining;
|
||||
|
|
@ -80,6 +79,30 @@ static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg)
|
|||
return readl(i2c_dev->regs + reg);
|
||||
}
|
||||
|
||||
static int bcm2835_i2c_set_divider(struct bcm2835_i2c_dev *i2c_dev)
|
||||
{
|
||||
u32 divider;
|
||||
|
||||
divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk),
|
||||
i2c_dev->bus_clk_rate);
|
||||
/*
|
||||
* Per the datasheet, the register is always interpreted as an even
|
||||
* number, by rounding down. In other words, the LSB is ignored. So,
|
||||
* if the LSB is set, increment the divider to avoid any issue.
|
||||
*/
|
||||
if (divider & 1)
|
||||
divider++;
|
||||
if ((divider < BCM2835_I2C_CDIV_MIN) ||
|
||||
(divider > BCM2835_I2C_CDIV_MAX)) {
|
||||
dev_err_ratelimited(i2c_dev->dev, "Invalid clock-frequency\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev)
|
||||
{
|
||||
u32 val;
|
||||
|
|
@ -110,106 +133,159 @@ static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeated Start Condition (Sr)
|
||||
* The BCM2835 ARM Peripherals datasheet mentions a way to trigger a Sr when it
|
||||
* talks about reading from a slave with 10 bit address. This is achieved by
|
||||
* issuing a write, poll the I2CS.TA flag and wait for it to be set, and then
|
||||
* issue a read.
|
||||
* A comment in https://github.com/raspberrypi/linux/issues/254 shows how the
|
||||
* firmware actually does it using polling and says that it's a workaround for
|
||||
* a problem in the state machine.
|
||||
* It turns out that it is possible to use the TXW interrupt to know when the
|
||||
* transfer is active, provided the FIFO has not been prefilled.
|
||||
*/
|
||||
|
||||
static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_dev *i2c_dev)
|
||||
{
|
||||
u32 c = BCM2835_I2C_C_ST | BCM2835_I2C_C_I2CEN;
|
||||
struct i2c_msg *msg = i2c_dev->curr_msg;
|
||||
bool last_msg = (i2c_dev->num_msgs == 1);
|
||||
|
||||
if (!i2c_dev->num_msgs)
|
||||
return;
|
||||
|
||||
i2c_dev->num_msgs--;
|
||||
i2c_dev->msg_buf = msg->buf;
|
||||
i2c_dev->msg_buf_remaining = msg->len;
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
c |= BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
|
||||
else
|
||||
c |= BCM2835_I2C_C_INTT;
|
||||
|
||||
if (last_msg)
|
||||
c |= BCM2835_I2C_C_INTD;
|
||||
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note about I2C_C_CLEAR on error:
|
||||
* The I2C_C_CLEAR on errors will take some time to resolve -- if you were in
|
||||
* non-idle state and I2C_C_READ, it sets an abort_rx flag and runs through
|
||||
* the state machine to send a NACK and a STOP. Since we're setting CLEAR
|
||||
* without I2CEN, that NACK will be hanging around queued up for next time
|
||||
* we start the engine.
|
||||
*/
|
||||
|
||||
static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
|
||||
{
|
||||
struct bcm2835_i2c_dev *i2c_dev = data;
|
||||
u32 val, err;
|
||||
|
||||
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
|
||||
val &= BCM2835_I2C_BITMSK_S;
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);
|
||||
|
||||
err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
|
||||
if (err) {
|
||||
i2c_dev->msg_err = err;
|
||||
complete(&i2c_dev->completion);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (val & BCM2835_I2C_S_RXD) {
|
||||
bcm2835_drain_rxfifo(i2c_dev);
|
||||
if (!(val & BCM2835_I2C_S_DONE))
|
||||
return IRQ_HANDLED;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (val & BCM2835_I2C_S_DONE) {
|
||||
if (i2c_dev->msg_buf_remaining)
|
||||
if (i2c_dev->curr_msg->flags & I2C_M_RD) {
|
||||
bcm2835_drain_rxfifo(i2c_dev);
|
||||
val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
|
||||
}
|
||||
|
||||
if ((val & BCM2835_I2C_S_RXD) || i2c_dev->msg_buf_remaining)
|
||||
i2c_dev->msg_err = BCM2835_I2C_S_LEN;
|
||||
else
|
||||
i2c_dev->msg_err = 0;
|
||||
complete(&i2c_dev->completion);
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (val & BCM2835_I2C_S_TXW) {
|
||||
if (!i2c_dev->msg_buf_remaining) {
|
||||
i2c_dev->msg_err = val | BCM2835_I2C_S_LEN;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
bcm2835_fill_txfifo(i2c_dev);
|
||||
|
||||
if (i2c_dev->num_msgs && !i2c_dev->msg_buf_remaining) {
|
||||
i2c_dev->curr_msg++;
|
||||
bcm2835_i2c_start_transfer(i2c_dev);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (val & BCM2835_I2C_S_TXD) {
|
||||
bcm2835_fill_txfifo(i2c_dev);
|
||||
if (val & BCM2835_I2C_S_RXR) {
|
||||
if (!i2c_dev->msg_buf_remaining) {
|
||||
i2c_dev->msg_err = val | BCM2835_I2C_S_LEN;
|
||||
goto complete;
|
||||
}
|
||||
|
||||
bcm2835_drain_rxfifo(i2c_dev);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
u32 c;
|
||||
unsigned long time_left;
|
||||
|
||||
i2c_dev->msg_buf = msg->buf;
|
||||
i2c_dev->msg_buf_remaining = msg->len;
|
||||
reinit_completion(&i2c_dev->completion);
|
||||
|
||||
complete:
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, BCM2835_I2C_S_CLKT |
|
||||
BCM2835_I2C_S_ERR | BCM2835_I2C_S_DONE);
|
||||
complete(&i2c_dev->completion);
|
||||
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
|
||||
} else {
|
||||
c = BCM2835_I2C_C_INTT;
|
||||
bcm2835_fill_txfifo(i2c_dev);
|
||||
}
|
||||
c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN;
|
||||
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
|
||||
|
||||
time_left = wait_for_completion_timeout(&i2c_dev->completion,
|
||||
BCM2835_I2C_TIMEOUT);
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
|
||||
if (!time_left) {
|
||||
dev_err(i2c_dev->dev, "i2c transfer timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (likely(!i2c_dev->msg_err))
|
||||
return 0;
|
||||
|
||||
if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) &&
|
||||
(msg->flags & I2C_M_IGNORE_NAK))
|
||||
return 0;
|
||||
|
||||
dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
|
||||
|
||||
if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
|
||||
return -EREMOTEIO;
|
||||
else
|
||||
return -EIO;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
||||
int num)
|
||||
{
|
||||
struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||
int i;
|
||||
int ret = 0;
|
||||
unsigned long time_left;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]);
|
||||
if (ret)
|
||||
break;
|
||||
for (i = 0; i < (num - 1); i++)
|
||||
if (msgs[i].flags & I2C_M_RD) {
|
||||
dev_warn_once(i2c_dev->dev,
|
||||
"only one read message supported, has to be last\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ret = bcm2835_i2c_set_divider(i2c_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_dev->curr_msg = msgs;
|
||||
i2c_dev->num_msgs = num;
|
||||
reinit_completion(&i2c_dev->completion);
|
||||
|
||||
bcm2835_i2c_start_transfer(i2c_dev);
|
||||
|
||||
time_left = wait_for_completion_timeout(&i2c_dev->completion,
|
||||
adap->timeout);
|
||||
if (!time_left) {
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C,
|
||||
BCM2835_I2C_C_CLEAR);
|
||||
dev_err(i2c_dev->dev, "i2c transfer timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return ret ?: i;
|
||||
if (!i2c_dev->msg_err)
|
||||
return num;
|
||||
|
||||
dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
|
||||
|
||||
if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
|
||||
return -EREMOTEIO;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static u32 bcm2835_i2c_func(struct i2c_adapter *adap)
|
||||
|
|
@ -235,7 +311,6 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct bcm2835_i2c_dev *i2c_dev;
|
||||
struct resource *mem, *irq;
|
||||
u32 bus_clk_rate, divider;
|
||||
int ret;
|
||||
struct i2c_adapter *adap;
|
||||
|
||||
|
|
@ -259,28 +334,13 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||
&bus_clk_rate);
|
||||
&i2c_dev->bus_clk_rate);
|
||||
if (ret < 0) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Could not read clock-frequency property\n");
|
||||
bus_clk_rate = 100000;
|
||||
i2c_dev->bus_clk_rate = 100000;
|
||||
}
|
||||
|
||||
divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);
|
||||
/*
|
||||
* Per the datasheet, the register is always interpreted as an even
|
||||
* number, by rounding down. In other words, the LSB is ignored. So,
|
||||
* if the LSB is set, increment the divider to avoid any issue.
|
||||
*/
|
||||
if (divider & 1)
|
||||
divider++;
|
||||
if ((divider < BCM2835_I2C_CDIV_MIN) ||
|
||||
(divider > BCM2835_I2C_CDIV_MAX)) {
|
||||
dev_err(&pdev->dev, "Invalid clock-frequency\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "No IRQ resource\n");
|
||||
|
|
|
|||
|
|
@ -536,6 +536,8 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||
intr_mask = DW_IC_INTR_DEFAULT_MASK;
|
||||
|
||||
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
|
||||
u32 flags = msgs[dev->msg_write_idx].flags;
|
||||
|
||||
/*
|
||||
* if target address has changed, we need to
|
||||
* reprogram the target address in the i2c
|
||||
|
|
@ -581,8 +583,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||
* detected from the registers so we set it always
|
||||
* when writing/reading the last byte.
|
||||
*/
|
||||
|
||||
/*
|
||||
* i2c-core.c always sets the buffer length of
|
||||
* I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will
|
||||
* be adjusted when receiving the first byte.
|
||||
* Thus we can't stop the transaction here.
|
||||
*/
|
||||
if (dev->msg_write_idx == dev->msgs_num - 1 &&
|
||||
buf_len == 1)
|
||||
buf_len == 1 && !(flags & I2C_M_RECV_LEN))
|
||||
cmd |= BIT(9);
|
||||
|
||||
if (need_restart) {
|
||||
|
|
@ -607,7 +616,12 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||
dev->tx_buf = buf;
|
||||
dev->tx_buf_len = buf_len;
|
||||
|
||||
if (buf_len > 0) {
|
||||
/*
|
||||
* Because we don't know the buffer length in the
|
||||
* I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop
|
||||
* the transaction here.
|
||||
*/
|
||||
if (buf_len > 0 || flags & I2C_M_RECV_LEN) {
|
||||
/* more bytes to be written */
|
||||
dev->status |= STATUS_WRITE_IN_PROGRESS;
|
||||
break;
|
||||
|
|
@ -628,6 +642,24 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
|
|||
dw_writel(dev, intr_mask, DW_IC_INTR_MASK);
|
||||
}
|
||||
|
||||
static u8
|
||||
i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len)
|
||||
{
|
||||
struct i2c_msg *msgs = dev->msgs;
|
||||
u32 flags = msgs[dev->msg_read_idx].flags;
|
||||
|
||||
/*
|
||||
* Adjust the buffer length and mask the flag
|
||||
* after receiving the first byte.
|
||||
*/
|
||||
len += (flags & I2C_CLIENT_PEC) ? 2 : 1;
|
||||
dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding);
|
||||
msgs[dev->msg_read_idx].len = len;
|
||||
msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
i2c_dw_read(struct dw_i2c_dev *dev)
|
||||
{
|
||||
|
|
@ -652,7 +684,15 @@ i2c_dw_read(struct dw_i2c_dev *dev)
|
|||
rx_valid = dw_readl(dev, DW_IC_RXFLR);
|
||||
|
||||
for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
|
||||
*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
|
||||
u32 flags = msgs[dev->msg_read_idx].flags;
|
||||
|
||||
*buf = dw_readl(dev, DW_IC_DATA_CMD);
|
||||
/* Ensure length byte is a valid value */
|
||||
if (flags & I2C_M_RECV_LEN &&
|
||||
*buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) {
|
||||
len = i2c_dw_recv_len(dev, *buf);
|
||||
}
|
||||
buf++;
|
||||
dev->rx_outstanding--;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
|
||||
I2C_FUNC_SMBUS_BYTE | \
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | \
|
||||
I2C_FUNC_SMBUS_WORD_DATA | \
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA | \
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)
|
||||
|
||||
#define DW_IC_CON_MASTER 0x1
|
||||
#define DW_IC_CON_SPEED_STD 0x2
|
||||
|
|
|
|||
|
|
@ -71,12 +71,6 @@ struct dw_pci_controller {
|
|||
DW_IC_CON_SLAVE_DISABLE | \
|
||||
DW_IC_CON_RESTART_EN)
|
||||
|
||||
#define DW_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
|
||||
I2C_FUNC_SMBUS_BYTE | \
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | \
|
||||
I2C_FUNC_SMBUS_WORD_DATA | \
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK)
|
||||
|
||||
/* Merrifield HCNT/LCNT/SDA hold time */
|
||||
static struct dw_scl_sda_cfg mrfld_config = {
|
||||
.ss_hcnt = 0x2f8,
|
||||
|
|
@ -147,6 +141,7 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
|||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 32,
|
||||
.rx_fifo_depth = 32,
|
||||
.functionality = I2C_FUNC_10BIT_ADDR,
|
||||
.clk_khz = 25000,
|
||||
.setup = mfld_setup,
|
||||
},
|
||||
|
|
@ -155,6 +150,7 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
|||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||
.tx_fifo_depth = 64,
|
||||
.rx_fifo_depth = 64,
|
||||
.functionality = I2C_FUNC_10BIT_ADDR,
|
||||
.scl_sda_cfg = &mrfld_config,
|
||||
.setup = mrfld_setup,
|
||||
},
|
||||
|
|
@ -249,7 +245,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
|||
}
|
||||
|
||||
dev->functionality = controller->functionality |
|
||||
DW_DEFAULT_FUNCTIONALITY;
|
||||
DW_IC_DEFAULT_FUNCTIONALITY;
|
||||
|
||||
dev->master_cfg = controller->bus_cfg;
|
||||
if (controller->scl_sda_cfg) {
|
||||
|
|
|
|||
|
|
@ -176,9 +176,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
|||
dev->irq = irq;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
/* fast mode by default because of legacy reasons */
|
||||
dev->clk_freq = 400000;
|
||||
|
||||
if (pdata) {
|
||||
dev->clk_freq = pdata->i2c_scl_freq;
|
||||
} else {
|
||||
|
|
@ -193,8 +190,16 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
|
||||
if (acpi_speed)
|
||||
dev->clk_freq = acpi_speed;
|
||||
/*
|
||||
* Find bus speed from the "clock-frequency" device property, ACPI
|
||||
* or by using fast mode if neither is set.
|
||||
*/
|
||||
if (acpi_speed && dev->clk_freq)
|
||||
dev->clk_freq = min(dev->clk_freq, acpi_speed);
|
||||
else if (acpi_speed || dev->clk_freq)
|
||||
dev->clk_freq = max(dev->clk_freq, acpi_speed);
|
||||
else
|
||||
dev->clk_freq = 400000;
|
||||
|
||||
if (has_acpi_companion(&pdev->dev))
|
||||
dw_i2c_acpi_configure(pdev);
|
||||
|
|
@ -214,13 +219,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
|||
if (r)
|
||||
return r;
|
||||
|
||||
dev->functionality =
|
||||
I2C_FUNC_I2C |
|
||||
I2C_FUNC_10BIT_ADDR |
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
|
||||
|
||||
dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
|
||||
DW_IC_CON_RESTART_EN;
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ static const struct i2c_algorithm dln2_i2c_usb_algorithm = {
|
|||
.functionality = dln2_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter_quirks dln2_i2c_quirks = {
|
||||
static const struct i2c_adapter_quirks dln2_i2c_quirks = {
|
||||
.max_read_len = DLN2_I2C_MAX_XFER_SIZE,
|
||||
.max_write_len = DLN2_I2C_MAX_XFER_SIZE,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -118,7 +118,6 @@
|
|||
#define SMBSLVSTS(p) (16 + (p)->smba) /* ICH3 and later */
|
||||
#define SMBSLVCMD(p) (17 + (p)->smba) /* ICH3 and later */
|
||||
#define SMBNTFDADD(p) (20 + (p)->smba) /* ICH3 and later */
|
||||
#define SMBNTFDDAT(p) (22 + (p)->smba) /* ICH3 and later */
|
||||
|
||||
/* PCI Address Constants */
|
||||
#define SMBBAR 4
|
||||
|
|
@ -137,27 +136,27 @@
|
|||
#define SBREG_SMBCTRL 0xc6000c
|
||||
|
||||
/* Host status bits for SMBPCISTS */
|
||||
#define SMBPCISTS_INTS 0x08
|
||||
#define SMBPCISTS_INTS BIT(3)
|
||||
|
||||
/* Control bits for SMBPCICTL */
|
||||
#define SMBPCICTL_INTDIS 0x0400
|
||||
#define SMBPCICTL_INTDIS BIT(10)
|
||||
|
||||
/* Host configuration bits for SMBHSTCFG */
|
||||
#define SMBHSTCFG_HST_EN 1
|
||||
#define SMBHSTCFG_SMB_SMI_EN 2
|
||||
#define SMBHSTCFG_I2C_EN 4
|
||||
#define SMBHSTCFG_SPD_WD 0x10
|
||||
#define SMBHSTCFG_HST_EN BIT(0)
|
||||
#define SMBHSTCFG_SMB_SMI_EN BIT(1)
|
||||
#define SMBHSTCFG_I2C_EN BIT(2)
|
||||
#define SMBHSTCFG_SPD_WD BIT(4)
|
||||
|
||||
/* TCO configuration bits for TCOCTL */
|
||||
#define TCOCTL_EN 0x0100
|
||||
#define TCOCTL_EN BIT(8)
|
||||
|
||||
/* Auxiliary status register bits, ICH4+ only */
|
||||
#define SMBAUXSTS_CRCE 1
|
||||
#define SMBAUXSTS_STCO 2
|
||||
#define SMBAUXSTS_CRCE BIT(0)
|
||||
#define SMBAUXSTS_STCO BIT(1)
|
||||
|
||||
/* Auxiliary control register bits, ICH4+ only */
|
||||
#define SMBAUXCTL_CRC 1
|
||||
#define SMBAUXCTL_E32B 2
|
||||
#define SMBAUXCTL_CRC BIT(0)
|
||||
#define SMBAUXCTL_E32B BIT(1)
|
||||
|
||||
/* Other settings */
|
||||
#define MAX_RETRIES 400
|
||||
|
|
@ -172,27 +171,27 @@
|
|||
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
|
||||
|
||||
/* I801 Host Control register bits */
|
||||
#define SMBHSTCNT_INTREN 0x01
|
||||
#define SMBHSTCNT_KILL 0x02
|
||||
#define SMBHSTCNT_LAST_BYTE 0x20
|
||||
#define SMBHSTCNT_START 0x40
|
||||
#define SMBHSTCNT_PEC_EN 0x80 /* ICH3 and later */
|
||||
#define SMBHSTCNT_INTREN BIT(0)
|
||||
#define SMBHSTCNT_KILL BIT(1)
|
||||
#define SMBHSTCNT_LAST_BYTE BIT(5)
|
||||
#define SMBHSTCNT_START BIT(6)
|
||||
#define SMBHSTCNT_PEC_EN BIT(7) /* ICH3 and later */
|
||||
|
||||
/* I801 Hosts Status register bits */
|
||||
#define SMBHSTSTS_BYTE_DONE 0x80
|
||||
#define SMBHSTSTS_INUSE_STS 0x40
|
||||
#define SMBHSTSTS_SMBALERT_STS 0x20
|
||||
#define SMBHSTSTS_FAILED 0x10
|
||||
#define SMBHSTSTS_BUS_ERR 0x08
|
||||
#define SMBHSTSTS_DEV_ERR 0x04
|
||||
#define SMBHSTSTS_INTR 0x02
|
||||
#define SMBHSTSTS_HOST_BUSY 0x01
|
||||
#define SMBHSTSTS_BYTE_DONE BIT(7)
|
||||
#define SMBHSTSTS_INUSE_STS BIT(6)
|
||||
#define SMBHSTSTS_SMBALERT_STS BIT(5)
|
||||
#define SMBHSTSTS_FAILED BIT(4)
|
||||
#define SMBHSTSTS_BUS_ERR BIT(3)
|
||||
#define SMBHSTSTS_DEV_ERR BIT(2)
|
||||
#define SMBHSTSTS_INTR BIT(1)
|
||||
#define SMBHSTSTS_HOST_BUSY BIT(0)
|
||||
|
||||
/* Host Notify Status registers bits */
|
||||
#define SMBSLVSTS_HST_NTFY_STS 1
|
||||
/* Host Notify Status register bits */
|
||||
#define SMBSLVSTS_HST_NTFY_STS BIT(0)
|
||||
|
||||
/* Host Notify Command registers bits */
|
||||
#define SMBSLVCMD_HST_NTFY_INTREN 0x01
|
||||
/* Host Notify Command register bits */
|
||||
#define SMBSLVCMD_HST_NTFY_INTREN BIT(0)
|
||||
|
||||
#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \
|
||||
SMBHSTSTS_DEV_ERR)
|
||||
|
|
@ -243,6 +242,7 @@ struct i801_priv {
|
|||
struct i2c_adapter adapter;
|
||||
unsigned long smba;
|
||||
unsigned char original_hstcfg;
|
||||
unsigned char original_slvcmd;
|
||||
struct pci_dev *pci_dev;
|
||||
unsigned int features;
|
||||
|
||||
|
|
@ -269,20 +269,17 @@ struct i801_priv {
|
|||
*/
|
||||
bool acpi_reserved;
|
||||
struct mutex acpi_lock;
|
||||
struct smbus_host_notify *host_notify;
|
||||
};
|
||||
|
||||
#define SMBHSTNTFY_SIZE 8
|
||||
|
||||
#define FEATURE_SMBUS_PEC (1 << 0)
|
||||
#define FEATURE_BLOCK_BUFFER (1 << 1)
|
||||
#define FEATURE_BLOCK_PROC (1 << 2)
|
||||
#define FEATURE_I2C_BLOCK_READ (1 << 3)
|
||||
#define FEATURE_IRQ (1 << 4)
|
||||
#define FEATURE_HOST_NOTIFY (1 << 5)
|
||||
#define FEATURE_SMBUS_PEC BIT(0)
|
||||
#define FEATURE_BLOCK_BUFFER BIT(1)
|
||||
#define FEATURE_BLOCK_PROC BIT(2)
|
||||
#define FEATURE_I2C_BLOCK_READ BIT(3)
|
||||
#define FEATURE_IRQ BIT(4)
|
||||
#define FEATURE_HOST_NOTIFY BIT(5)
|
||||
/* Not really a feature, but it's convenient to handle it as such */
|
||||
#define FEATURE_IDF (1 << 15)
|
||||
#define FEATURE_TCO (1 << 16)
|
||||
#define FEATURE_IDF BIT(15)
|
||||
#define FEATURE_TCO BIT(16)
|
||||
|
||||
static const char *i801_feature_names[] = {
|
||||
"SMBus PEC",
|
||||
|
|
@ -582,12 +579,15 @@ static void i801_isr_byte_done(struct i801_priv *priv)
|
|||
static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
|
||||
{
|
||||
unsigned short addr;
|
||||
unsigned int data;
|
||||
|
||||
addr = inb_p(SMBNTFDADD(priv)) >> 1;
|
||||
data = inw_p(SMBNTFDDAT(priv));
|
||||
|
||||
i2c_handle_smbus_host_notify(priv->host_notify, addr, data);
|
||||
/*
|
||||
* With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba)
|
||||
* always returns 0. Our current implementation doesn't provide
|
||||
* data, so we just ignore it.
|
||||
*/
|
||||
i2c_handle_smbus_host_notify(&priv->adapter, addr);
|
||||
|
||||
/* clear Host Notify bit and return */
|
||||
outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
|
||||
|
|
@ -950,23 +950,29 @@ static u32 i801_func(struct i2c_adapter *adapter)
|
|||
I2C_FUNC_SMBUS_HOST_NOTIFY : 0);
|
||||
}
|
||||
|
||||
static int i801_enable_host_notify(struct i2c_adapter *adapter)
|
||||
static void i801_enable_host_notify(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i801_priv *priv = i2c_get_adapdata(adapter);
|
||||
|
||||
if (!(priv->features & FEATURE_HOST_NOTIFY))
|
||||
return -ENOTSUPP;
|
||||
return;
|
||||
|
||||
if (!priv->host_notify)
|
||||
priv->host_notify = i2c_setup_smbus_host_notify(adapter);
|
||||
if (!priv->host_notify)
|
||||
return -ENOMEM;
|
||||
priv->original_slvcmd = inb_p(SMBSLVCMD(priv));
|
||||
|
||||
if (!(SMBSLVCMD_HST_NTFY_INTREN & priv->original_slvcmd))
|
||||
outb_p(SMBSLVCMD_HST_NTFY_INTREN | priv->original_slvcmd,
|
||||
SMBSLVCMD(priv));
|
||||
|
||||
outb_p(SMBSLVCMD_HST_NTFY_INTREN, SMBSLVCMD(priv));
|
||||
/* clear Host Notify bit to allow a new notification */
|
||||
outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void i801_disable_host_notify(struct i801_priv *priv)
|
||||
{
|
||||
if (!(priv->features & FEATURE_HOST_NOTIFY))
|
||||
return;
|
||||
|
||||
outb_p(priv->original_slvcmd, SMBSLVCMD(priv));
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm smbus_algorithm = {
|
||||
|
|
@ -1633,14 +1639,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable Host Notify for chips that supports it.
|
||||
* It is done after i2c_add_adapter() so that we are sure the work queue
|
||||
* is not used if i2c_add_adapter() fails.
|
||||
*/
|
||||
err = i801_enable_host_notify(&priv->adapter);
|
||||
if (err && err != -ENOTSUPP)
|
||||
dev_warn(&dev->dev, "Unable to enable SMBus Host Notify\n");
|
||||
i801_enable_host_notify(&priv->adapter);
|
||||
|
||||
i801_probe_optional_slaves(priv);
|
||||
/* We ignore errors - multiplexing is optional */
|
||||
|
|
@ -1663,6 +1662,7 @@ static void i801_remove(struct pci_dev *dev)
|
|||
pm_runtime_forbid(&dev->dev);
|
||||
pm_runtime_get_noresume(&dev->dev);
|
||||
|
||||
i801_disable_host_notify(priv);
|
||||
i801_del_mux(priv);
|
||||
i2c_del_adapter(&priv->adapter);
|
||||
i801_acpi_remove(priv);
|
||||
|
|
@ -1690,11 +1690,8 @@ static int i801_resume(struct device *dev)
|
|||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct i801_priv *priv = pci_get_drvdata(pci_dev);
|
||||
int err;
|
||||
|
||||
err = i801_enable_host_notify(&priv->adapter);
|
||||
if (err && err != -ENOTSUPP)
|
||||
dev_warn(dev, "Unable to enable SMBus Host Notify\n");
|
||||
i801_enable_host_notify(&priv->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
652
drivers/i2c/busses/i2c-imx-lpi2c.c
Normal file
652
drivers/i2c/busses/i2c-imx-lpi2c.c
Normal file
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* This is i.MX low power i2c controller driver.
|
||||
*
|
||||
* Copyright 2016 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRIVER_NAME "imx-lpi2c"
|
||||
|
||||
#define LPI2C_PARAM 0x04 /* i2c RX/TX FIFO size */
|
||||
#define LPI2C_MCR 0x10 /* i2c contrl register */
|
||||
#define LPI2C_MSR 0x14 /* i2c status register */
|
||||
#define LPI2C_MIER 0x18 /* i2c interrupt enable */
|
||||
#define LPI2C_MCFGR0 0x20 /* i2c master configuration */
|
||||
#define LPI2C_MCFGR1 0x24 /* i2c master configuration */
|
||||
#define LPI2C_MCFGR2 0x28 /* i2c master configuration */
|
||||
#define LPI2C_MCFGR3 0x2C /* i2c master configuration */
|
||||
#define LPI2C_MCCR0 0x48 /* i2c master clk configuration */
|
||||
#define LPI2C_MCCR1 0x50 /* i2c master clk configuration */
|
||||
#define LPI2C_MFCR 0x58 /* i2c master FIFO control */
|
||||
#define LPI2C_MFSR 0x5C /* i2c master FIFO status */
|
||||
#define LPI2C_MTDR 0x60 /* i2c master TX data register */
|
||||
#define LPI2C_MRDR 0x70 /* i2c master RX data register */
|
||||
|
||||
/* i2c command */
|
||||
#define TRAN_DATA 0X00
|
||||
#define RECV_DATA 0X01
|
||||
#define GEN_STOP 0X02
|
||||
#define RECV_DISCARD 0X03
|
||||
#define GEN_START 0X04
|
||||
#define START_NACK 0X05
|
||||
#define START_HIGH 0X06
|
||||
#define START_HIGH_NACK 0X07
|
||||
|
||||
#define MCR_MEN BIT(0)
|
||||
#define MCR_RST BIT(1)
|
||||
#define MCR_DOZEN BIT(2)
|
||||
#define MCR_DBGEN BIT(3)
|
||||
#define MCR_RTF BIT(8)
|
||||
#define MCR_RRF BIT(9)
|
||||
#define MSR_TDF BIT(0)
|
||||
#define MSR_RDF BIT(1)
|
||||
#define MSR_SDF BIT(9)
|
||||
#define MSR_NDF BIT(10)
|
||||
#define MSR_ALF BIT(11)
|
||||
#define MSR_MBF BIT(24)
|
||||
#define MSR_BBF BIT(25)
|
||||
#define MIER_TDIE BIT(0)
|
||||
#define MIER_RDIE BIT(1)
|
||||
#define MIER_SDIE BIT(9)
|
||||
#define MIER_NDIE BIT(10)
|
||||
#define MCFGR1_AUTOSTOP BIT(8)
|
||||
#define MCFGR1_IGNACK BIT(9)
|
||||
#define MRDR_RXEMPTY BIT(14)
|
||||
|
||||
#define I2C_CLK_RATIO 2
|
||||
#define CHUNK_DATA 256
|
||||
|
||||
#define LPI2C_DEFAULT_RATE 100000
|
||||
#define STARDARD_MAX_BITRATE 400000
|
||||
#define FAST_MAX_BITRATE 1000000
|
||||
#define FAST_PLUS_MAX_BITRATE 3400000
|
||||
#define HIGHSPEED_MAX_BITRATE 5000000
|
||||
|
||||
enum lpi2c_imx_mode {
|
||||
STANDARD, /* 100+Kbps */
|
||||
FAST, /* 400+Kbps */
|
||||
FAST_PLUS, /* 1.0+Mbps */
|
||||
HS, /* 3.4+Mbps */
|
||||
ULTRA_FAST, /* 5.0+Mbps */
|
||||
};
|
||||
|
||||
enum lpi2c_imx_pincfg {
|
||||
TWO_PIN_OD,
|
||||
TWO_PIN_OO,
|
||||
TWO_PIN_PP,
|
||||
FOUR_PIN_PP,
|
||||
};
|
||||
|
||||
struct lpi2c_imx_struct {
|
||||
struct i2c_adapter adapter;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
__u8 *rx_buf;
|
||||
__u8 *tx_buf;
|
||||
struct completion complete;
|
||||
unsigned int msglen;
|
||||
unsigned int delivered;
|
||||
unsigned int block_data;
|
||||
unsigned int bitrate;
|
||||
unsigned int txfifosize;
|
||||
unsigned int rxfifosize;
|
||||
enum lpi2c_imx_mode mode;
|
||||
};
|
||||
|
||||
static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
|
||||
unsigned int enable)
|
||||
{
|
||||
writel(enable, lpi2c_imx->base + LPI2C_MIER);
|
||||
}
|
||||
|
||||
static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
unsigned int temp;
|
||||
|
||||
while (1) {
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MSR);
|
||||
|
||||
/* check for arbitration lost, clear if set */
|
||||
if (temp & MSR_ALF) {
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MSR);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (temp & (MSR_BBF | MSR_MBF))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
|
||||
dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned int bitrate = lpi2c_imx->bitrate;
|
||||
enum lpi2c_imx_mode mode;
|
||||
|
||||
if (bitrate < STARDARD_MAX_BITRATE)
|
||||
mode = STANDARD;
|
||||
else if (bitrate < FAST_MAX_BITRATE)
|
||||
mode = FAST;
|
||||
else if (bitrate < FAST_PLUS_MAX_BITRATE)
|
||||
mode = FAST_PLUS;
|
||||
else if (bitrate < HIGHSPEED_MAX_BITRATE)
|
||||
mode = HS;
|
||||
else
|
||||
mode = ULTRA_FAST;
|
||||
|
||||
lpi2c_imx->mode = mode;
|
||||
}
|
||||
|
||||
static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
|
||||
struct i2c_msg *msgs)
|
||||
{
|
||||
unsigned int temp;
|
||||
u8 read;
|
||||
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MCR);
|
||||
temp |= MCR_RRF | MCR_RTF;
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCR);
|
||||
writel(0x7f00, lpi2c_imx->base + LPI2C_MSR);
|
||||
|
||||
read = msgs->flags & I2C_M_RD;
|
||||
temp = (msgs->addr << 1 | read) | (GEN_START << 8);
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
|
||||
|
||||
return lpi2c_imx_bus_busy(lpi2c_imx);
|
||||
}
|
||||
|
||||
static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
unsigned int temp;
|
||||
|
||||
writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
|
||||
|
||||
do {
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MSR);
|
||||
if (temp & MSR_SDF)
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
|
||||
dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
|
||||
break;
|
||||
}
|
||||
schedule();
|
||||
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */
|
||||
static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
u8 prescale, filt, sethold, clkhi, clklo, datavd;
|
||||
unsigned int clk_rate, clk_cycle;
|
||||
enum lpi2c_imx_pincfg pincfg;
|
||||
unsigned int temp;
|
||||
|
||||
lpi2c_imx_set_mode(lpi2c_imx);
|
||||
|
||||
clk_rate = clk_get_rate(lpi2c_imx->clk);
|
||||
if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
|
||||
filt = 0;
|
||||
else
|
||||
filt = 2;
|
||||
|
||||
for (prescale = 0; prescale <= 7; prescale++) {
|
||||
clk_cycle = clk_rate / ((1 << prescale) * lpi2c_imx->bitrate)
|
||||
- 3 - (filt >> 1);
|
||||
clkhi = (clk_cycle + I2C_CLK_RATIO) / (I2C_CLK_RATIO + 1);
|
||||
clklo = clk_cycle - clkhi;
|
||||
if (clklo < 64)
|
||||
break;
|
||||
}
|
||||
|
||||
if (prescale > 7)
|
||||
return -EINVAL;
|
||||
|
||||
/* set MCFGR1: PINCFG, PRESCALE, IGNACK */
|
||||
if (lpi2c_imx->mode == ULTRA_FAST)
|
||||
pincfg = TWO_PIN_OO;
|
||||
else
|
||||
pincfg = TWO_PIN_OD;
|
||||
temp = prescale | pincfg << 24;
|
||||
|
||||
if (lpi2c_imx->mode == ULTRA_FAST)
|
||||
temp |= MCFGR1_IGNACK;
|
||||
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCFGR1);
|
||||
|
||||
/* set MCFGR2: FILTSDA, FILTSCL */
|
||||
temp = (filt << 16) | (filt << 24);
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCFGR2);
|
||||
|
||||
/* set MCCR: DATAVD, SETHOLD, CLKHI, CLKLO */
|
||||
sethold = clkhi;
|
||||
datavd = clkhi >> 1;
|
||||
temp = datavd << 24 | sethold << 16 | clkhi << 8 | clklo;
|
||||
|
||||
if (lpi2c_imx->mode == HS)
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCCR1);
|
||||
else
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCCR0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned int temp;
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(lpi2c_imx->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
temp = MCR_RST;
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCR);
|
||||
writel(0, lpi2c_imx->base + LPI2C_MCR);
|
||||
|
||||
ret = lpi2c_imx_config(lpi2c_imx);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MCR);
|
||||
temp |= MCR_MEN;
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCR);
|
||||
|
||||
return 0;
|
||||
|
||||
clk_disable:
|
||||
clk_disable(lpi2c_imx->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MCR);
|
||||
temp &= ~MCR_MEN;
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MCR);
|
||||
|
||||
clk_disable(lpi2c_imx->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpi2c_imx_msg_complete(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = wait_for_completion_timeout(&lpi2c_imx->complete, HZ);
|
||||
|
||||
return timeout ? 0 : -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
u32 txcnt;
|
||||
|
||||
do {
|
||||
txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
|
||||
|
||||
if (readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) {
|
||||
dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
|
||||
dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
schedule();
|
||||
|
||||
} while (txcnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpi2c_imx_set_tx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
writel(lpi2c_imx->txfifosize >> 1, lpi2c_imx->base + LPI2C_MFCR);
|
||||
}
|
||||
|
||||
static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned int temp, remaining;
|
||||
|
||||
remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
|
||||
|
||||
if (remaining > (lpi2c_imx->rxfifosize >> 1))
|
||||
temp = lpi2c_imx->rxfifosize >> 1;
|
||||
else
|
||||
temp = 0;
|
||||
|
||||
writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR);
|
||||
}
|
||||
|
||||
static void lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned int data, txcnt;
|
||||
|
||||
txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
|
||||
|
||||
while (txcnt < lpi2c_imx->txfifosize) {
|
||||
if (lpi2c_imx->delivered == lpi2c_imx->msglen)
|
||||
break;
|
||||
|
||||
data = lpi2c_imx->tx_buf[lpi2c_imx->delivered++];
|
||||
writel(data, lpi2c_imx->base + LPI2C_MTDR);
|
||||
txcnt++;
|
||||
}
|
||||
|
||||
if (lpi2c_imx->delivered < lpi2c_imx->msglen)
|
||||
lpi2c_imx_intctrl(lpi2c_imx, MIER_TDIE | MIER_NDIE);
|
||||
else
|
||||
complete(&lpi2c_imx->complete);
|
||||
}
|
||||
|
||||
static void lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
{
|
||||
unsigned int blocklen, remaining;
|
||||
unsigned int temp, data;
|
||||
|
||||
do {
|
||||
data = readl(lpi2c_imx->base + LPI2C_MRDR);
|
||||
if (data & MRDR_RXEMPTY)
|
||||
break;
|
||||
|
||||
lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff;
|
||||
} while (1);
|
||||
|
||||
/*
|
||||
* First byte is the length of remaining packet in the SMBus block
|
||||
* data read. Add it to msgs->len.
|
||||
*/
|
||||
if (lpi2c_imx->block_data) {
|
||||
blocklen = lpi2c_imx->rx_buf[0];
|
||||
lpi2c_imx->msglen += blocklen;
|
||||
}
|
||||
|
||||
remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
|
||||
|
||||
if (!remaining) {
|
||||
complete(&lpi2c_imx->complete);
|
||||
return;
|
||||
}
|
||||
|
||||
/* not finished, still waiting for rx data */
|
||||
lpi2c_imx_set_rx_watermark(lpi2c_imx);
|
||||
|
||||
/* multiple receive commands */
|
||||
if (lpi2c_imx->block_data) {
|
||||
lpi2c_imx->block_data = 0;
|
||||
temp = remaining;
|
||||
temp |= (RECV_DATA << 8);
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
|
||||
} else if (!(lpi2c_imx->delivered & 0xff)) {
|
||||
temp = (remaining > CHUNK_DATA ? CHUNK_DATA : remaining) - 1;
|
||||
temp |= (RECV_DATA << 8);
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
|
||||
}
|
||||
|
||||
lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE);
|
||||
}
|
||||
|
||||
static void lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
|
||||
struct i2c_msg *msgs)
|
||||
{
|
||||
lpi2c_imx->tx_buf = msgs->buf;
|
||||
lpi2c_imx_set_tx_watermark(lpi2c_imx);
|
||||
lpi2c_imx_write_txfifo(lpi2c_imx);
|
||||
}
|
||||
|
||||
static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
|
||||
struct i2c_msg *msgs)
|
||||
{
|
||||
unsigned int temp;
|
||||
|
||||
lpi2c_imx->rx_buf = msgs->buf;
|
||||
lpi2c_imx->block_data = msgs->flags & I2C_M_RECV_LEN;
|
||||
|
||||
lpi2c_imx_set_rx_watermark(lpi2c_imx);
|
||||
temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1;
|
||||
temp |= (RECV_DATA << 8);
|
||||
writel(temp, lpi2c_imx->base + LPI2C_MTDR);
|
||||
|
||||
lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
|
||||
}
|
||||
|
||||
static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter);
|
||||
unsigned int temp;
|
||||
int i, result;
|
||||
|
||||
result = lpi2c_imx_master_enable(lpi2c_imx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
|
||||
if (result)
|
||||
goto disable;
|
||||
|
||||
/* quick smbus */
|
||||
if (num == 1 && msgs[0].len == 0)
|
||||
goto stop;
|
||||
|
||||
lpi2c_imx->delivered = 0;
|
||||
lpi2c_imx->msglen = msgs[i].len;
|
||||
init_completion(&lpi2c_imx->complete);
|
||||
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
lpi2c_imx_read(lpi2c_imx, &msgs[i]);
|
||||
else
|
||||
lpi2c_imx_write(lpi2c_imx, &msgs[i]);
|
||||
|
||||
result = lpi2c_imx_msg_complete(lpi2c_imx);
|
||||
if (result)
|
||||
goto stop;
|
||||
|
||||
if (!(msgs[i].flags & I2C_M_RD)) {
|
||||
result = lpi2c_imx_txfifo_empty(lpi2c_imx);
|
||||
if (result)
|
||||
goto stop;
|
||||
}
|
||||
}
|
||||
|
||||
stop:
|
||||
lpi2c_imx_stop(lpi2c_imx);
|
||||
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MSR);
|
||||
if ((temp & MSR_NDF) && !result)
|
||||
result = -EIO;
|
||||
|
||||
disable:
|
||||
lpi2c_imx_master_disable(lpi2c_imx);
|
||||
|
||||
dev_dbg(&lpi2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
|
||||
(result < 0) ? "error" : "success msg",
|
||||
(result < 0) ? result : num);
|
||||
|
||||
return (result < 0) ? result : num;
|
||||
}
|
||||
|
||||
static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct lpi2c_imx_struct *lpi2c_imx = dev_id;
|
||||
unsigned int temp;
|
||||
|
||||
lpi2c_imx_intctrl(lpi2c_imx, 0);
|
||||
temp = readl(lpi2c_imx->base + LPI2C_MSR);
|
||||
|
||||
if (temp & MSR_RDF)
|
||||
lpi2c_imx_read_rxfifo(lpi2c_imx);
|
||||
|
||||
if (temp & MSR_TDF)
|
||||
lpi2c_imx_write_txfifo(lpi2c_imx);
|
||||
|
||||
if (temp & MSR_NDF)
|
||||
complete(&lpi2c_imx->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u32 lpi2c_imx_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
|
||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm lpi2c_imx_algo = {
|
||||
.master_xfer = lpi2c_imx_xfer,
|
||||
.functionality = lpi2c_imx_func,
|
||||
};
|
||||
|
||||
static const struct of_device_id lpi2c_imx_of_match[] = {
|
||||
{ .compatible = "fsl,imx7ulp-lpi2c" },
|
||||
{ .compatible = "fsl,imx8dv-lpi2c" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
|
||||
|
||||
static int lpi2c_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpi2c_imx_struct *lpi2c_imx;
|
||||
struct resource *res;
|
||||
unsigned int temp;
|
||||
int irq, ret;
|
||||
|
||||
lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx), GFP_KERNEL);
|
||||
if (!lpi2c_imx)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
lpi2c_imx->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(lpi2c_imx->base))
|
||||
return PTR_ERR(lpi2c_imx->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "can't get irq number\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
lpi2c_imx->adapter.owner = THIS_MODULE;
|
||||
lpi2c_imx->adapter.algo = &lpi2c_imx_algo;
|
||||
lpi2c_imx->adapter.dev.parent = &pdev->dev;
|
||||
lpi2c_imx->adapter.dev.of_node = pdev->dev.of_node;
|
||||
strlcpy(lpi2c_imx->adapter.name, pdev->name,
|
||||
sizeof(lpi2c_imx->adapter.name));
|
||||
|
||||
lpi2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(lpi2c_imx->clk)) {
|
||||
dev_err(&pdev->dev, "can't get I2C peripheral clock\n");
|
||||
return PTR_ERR(lpi2c_imx->clk);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"clock-frequency", &lpi2c_imx->bitrate);
|
||||
if (ret)
|
||||
lpi2c_imx->bitrate = LPI2C_DEFAULT_RATE;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, 0,
|
||||
pdev->name, lpi2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);
|
||||
platform_set_drvdata(pdev, lpi2c_imx);
|
||||
|
||||
ret = clk_prepare_enable(lpi2c_imx->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clk enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
temp = readl(lpi2c_imx->base + LPI2C_PARAM);
|
||||
lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
|
||||
lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
|
||||
|
||||
clk_disable(lpi2c_imx->clk);
|
||||
|
||||
ret = i2c_add_adapter(&lpi2c_imx->adapter);
|
||||
if (ret)
|
||||
goto clk_unprepare;
|
||||
|
||||
dev_info(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n");
|
||||
|
||||
return 0;
|
||||
|
||||
clk_unprepare:
|
||||
clk_unprepare(lpi2c_imx->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lpi2c_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lpi2c_imx_struct *lpi2c_imx = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&lpi2c_imx->adapter);
|
||||
|
||||
clk_unprepare(lpi2c_imx->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver lpi2c_imx_driver = {
|
||||
.probe = lpi2c_imx_probe,
|
||||
.remove = lpi2c_imx_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = lpi2c_imx_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(lpi2c_imx_driver);
|
||||
|
||||
MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
|
||||
MODULE_DESCRIPTION("I2C adapter driver for LPI2C bus");
|
||||
MODULE_LICENSE("GPL");
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue