mirror of https://gitee.com/openkylin/linux.git
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
|
@ -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>;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
|
@ -0,0 +1,504 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* General defines */
|
||||
#define MLXPLAT_CPLD_LPC_I2C_BASE_ADDR 0x2000
|
||||
#define MLXCPLD_I2C_DEVICE_NAME "i2c_mlxcpld"
|
||||
#define MLXCPLD_I2C_VALID_FLAG (I2C_M_RECV_LEN | I2C_M_RD)
|
||||
#define MLXCPLD_I2C_BUS_NUM 1
|
||||
#define MLXCPLD_I2C_DATA_REG_SZ 36
|
||||
#define MLXCPLD_I2C_MAX_ADDR_LEN 4
|
||||
#define MLXCPLD_I2C_RETR_NUM 2
|
||||
#define MLXCPLD_I2C_XFER_TO 500000 /* usec */
|
||||
#define MLXCPLD_I2C_POLL_TIME 2000 /* usec */
|
||||
|
||||
/* LPC I2C registers */
|
||||
#define MLXCPLD_LPCI2C_LPF_REG 0x0
|
||||
#define MLXCPLD_LPCI2C_CTRL_REG 0x1
|
||||
#define MLXCPLD_LPCI2C_HALF_CYC_REG 0x4
|
||||
#define MLXCPLD_LPCI2C_I2C_HOLD_REG 0x5
|
||||
#define MLXCPLD_LPCI2C_CMD_REG 0x6
|
||||
#define MLXCPLD_LPCI2C_NUM_DAT_REG 0x7
|
||||
#define MLXCPLD_LPCI2C_NUM_ADDR_REG 0x8
|
||||
#define MLXCPLD_LPCI2C_STATUS_REG 0x9
|
||||
#define MLXCPLD_LPCI2C_DATA_REG 0xa
|
||||
|
||||
/* LPC I2C masks and parametres */
|
||||
#define MLXCPLD_LPCI2C_RST_SEL_MASK 0x1
|
||||
#define MLXCPLD_LPCI2C_TRANS_END 0x1
|
||||
#define MLXCPLD_LPCI2C_STATUS_NACK 0x10
|
||||
#define MLXCPLD_LPCI2C_NO_IND 0
|
||||
#define MLXCPLD_LPCI2C_ACK_IND 1
|
||||
#define MLXCPLD_LPCI2C_NACK_IND 2
|
||||
|
||||
struct mlxcpld_i2c_curr_xfer {
|
||||
u8 cmd;
|
||||
u8 addr_width;
|
||||
u8 data_len;
|
||||
u8 msg_num;
|
||||
struct i2c_msg *msg;
|
||||
};
|
||||
|
||||
struct mlxcpld_i2c_priv {
|
||||
struct i2c_adapter adap;
|
||||
u32 base_addr;
|
||||
struct mutex lock;
|
||||
struct mlxcpld_i2c_curr_xfer xfer;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static void mlxcpld_i2c_lpc_write_buf(u8 *data, u8 len, u32 addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len - len % 4; i += 4)
|
||||
outl(*(u32 *)(data + i), addr + i);
|
||||
for (; i < len; ++i)
|
||||
outb(*(data + i), addr + i);
|
||||
}
|
||||
|
||||
static void mlxcpld_i2c_lpc_read_buf(u8 *data, u8 len, u32 addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len - len % 4; i += 4)
|
||||
*(u32 *)(data + i) = inl(addr + i);
|
||||
for (; i < len; ++i)
|
||||
*(data + i) = inb(addr + i);
|
||||
}
|
||||
|
||||
static void mlxcpld_i2c_read_comm(struct mlxcpld_i2c_priv *priv, u8 offs,
|
||||
u8 *data, u8 datalen)
|
||||
{
|
||||
u32 addr = priv->base_addr + offs;
|
||||
|
||||
switch (datalen) {
|
||||
case 1:
|
||||
*(data) = inb(addr);
|
||||
break;
|
||||
case 2:
|
||||
*((u16 *)data) = inw(addr);
|
||||
break;
|
||||
case 3:
|
||||
*((u16 *)data) = inw(addr);
|
||||
*(data + 2) = inb(addr + 2);
|
||||
break;
|
||||
case 4:
|
||||
*((u32 *)data) = inl(addr);
|
||||
break;
|
||||
default:
|
||||
mlxcpld_i2c_lpc_read_buf(data, datalen, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mlxcpld_i2c_write_comm(struct mlxcpld_i2c_priv *priv, u8 offs,
|
||||
u8 *data, u8 datalen)
|
||||
{
|
||||
u32 addr = priv->base_addr + offs;
|
||||
|
||||
switch (datalen) {
|
||||
case 1:
|
||||
outb(*(data), addr);
|
||||
break;
|
||||
case 2:
|
||||
outw(*((u16 *)data), addr);
|
||||
break;
|
||||
case 3:
|
||||
outw(*((u16 *)data), addr);
|
||||
outb(*(data + 2), addr + 2);
|
||||
break;
|
||||
case 4:
|
||||
outl(*((u32 *)data), addr);
|
||||
break;
|
||||
default:
|
||||
mlxcpld_i2c_lpc_write_buf(data, datalen, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check validity of received i2c messages parameters.
|
||||
* Returns 0 if OK, other - in case of invalid parameters.
|
||||
*/
|
||||
static int mlxcpld_i2c_check_msg_params(struct mlxcpld_i2c_priv *priv,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!num) {
|
||||
dev_err(priv->dev, "Incorrect 0 num of messages\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (unlikely(msgs[0].addr > 0x7f)) {
|
||||
dev_err(priv->dev, "Invalid address 0x%03x\n",
|
||||
msgs[0].addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
if (unlikely(!msgs[i].buf)) {
|
||||
dev_err(priv->dev, "Invalid buf in msg[%d]\n",
|
||||
i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (unlikely(msgs[0].addr != msgs[i].addr)) {
|
||||
dev_err(priv->dev, "Invalid addr in msg[%d]\n",
|
||||
i);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if transfer is completed and status of operation.
|
||||
* Returns 0 - transfer completed (both ACK or NACK),
|
||||
* negative - transfer isn't finished.
|
||||
*/
|
||||
static int mlxcpld_i2c_check_status(struct mlxcpld_i2c_priv *priv, int *status)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1);
|
||||
|
||||
if (val & MLXCPLD_LPCI2C_TRANS_END) {
|
||||
if (val & MLXCPLD_LPCI2C_STATUS_NACK)
|
||||
/*
|
||||
* The slave is unable to accept the data. No such
|
||||
* slave, command not understood, or unable to accept
|
||||
* any more data.
|
||||
*/
|
||||
*status = MLXCPLD_LPCI2C_NACK_IND;
|
||||
else
|
||||
*status = MLXCPLD_LPCI2C_ACK_IND;
|
||||
return 0;
|
||||
}
|
||||
*status = MLXCPLD_LPCI2C_NO_IND;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void mlxcpld_i2c_set_transf_data(struct mlxcpld_i2c_priv *priv,
|
||||
struct i2c_msg *msgs, int num,
|
||||
u8 comm_len)
|
||||
{
|
||||
priv->xfer.msg = msgs;
|
||||
priv->xfer.msg_num = num;
|
||||
|
||||
/*
|
||||
* All upper layers currently are never use transfer with more than
|
||||
* 2 messages. Actually, it's also not so relevant in Mellanox systems
|
||||
* because of HW limitation. Max size of transfer is not more than 32
|
||||
* bytes in the current x86 LPCI2C bridge.
|
||||
*/
|
||||
priv->xfer.cmd = msgs[num - 1].flags & I2C_M_RD;
|
||||
|
||||
if (priv->xfer.cmd == I2C_M_RD && comm_len != msgs[0].len) {
|
||||
priv->xfer.addr_width = msgs[0].len;
|
||||
priv->xfer.data_len = comm_len - priv->xfer.addr_width;
|
||||
} else {
|
||||
priv->xfer.addr_width = 0;
|
||||
priv->xfer.data_len = comm_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset CPLD LPCI2C block */
|
||||
static void mlxcpld_i2c_reset(struct mlxcpld_i2c_priv *priv)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1);
|
||||
val &= ~MLXCPLD_LPCI2C_RST_SEL_MASK;
|
||||
mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CTRL_REG, &val, 1);
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
/* Make sure the CPLD is ready to start transmitting. */
|
||||
static int mlxcpld_i2c_check_busy(struct mlxcpld_i2c_priv *priv)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_STATUS_REG, &val, 1);
|
||||
|
||||
if (val & MLXCPLD_LPCI2C_TRANS_END)
|
||||
return 0;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int mlxcpld_i2c_wait_for_free(struct mlxcpld_i2c_priv *priv)
|
||||
{
|
||||
int timeout = 0;
|
||||
|
||||
do {
|
||||
if (!mlxcpld_i2c_check_busy(priv))
|
||||
break;
|
||||
usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
|
||||
timeout += MLXCPLD_I2C_POLL_TIME;
|
||||
} while (timeout <= MLXCPLD_I2C_XFER_TO);
|
||||
|
||||
if (timeout > MLXCPLD_I2C_XFER_TO)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for master transfer to complete.
|
||||
* It puts current process to sleep until we get interrupt or timeout expires.
|
||||
* Returns the number of transferred or read bytes or error (<0).
|
||||
*/
|
||||
static int mlxcpld_i2c_wait_for_tc(struct mlxcpld_i2c_priv *priv)
|
||||
{
|
||||
int status, i, timeout = 0;
|
||||
u8 datalen;
|
||||
|
||||
do {
|
||||
usleep_range(MLXCPLD_I2C_POLL_TIME / 2, MLXCPLD_I2C_POLL_TIME);
|
||||
if (!mlxcpld_i2c_check_status(priv, &status))
|
||||
break;
|
||||
timeout += MLXCPLD_I2C_POLL_TIME;
|
||||
} while (status == 0 && timeout < MLXCPLD_I2C_XFER_TO);
|
||||
|
||||
switch (status) {
|
||||
case MLXCPLD_LPCI2C_NO_IND:
|
||||
return -ETIMEDOUT;
|
||||
|
||||
case MLXCPLD_LPCI2C_ACK_IND:
|
||||
if (priv->xfer.cmd != I2C_M_RD)
|
||||
return (priv->xfer.addr_width + priv->xfer.data_len);
|
||||
|
||||
if (priv->xfer.msg_num == 1)
|
||||
i = 0;
|
||||
else
|
||||
i = 1;
|
||||
|
||||
if (!priv->xfer.msg[i].buf)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Actual read data len will be always the same as
|
||||
* requested len. 0xff (line pull-up) will be returned
|
||||
* if slave has no data to return. Thus don't read
|
||||
* MLXCPLD_LPCI2C_NUM_DAT_REG reg from CPLD.
|
||||
*/
|
||||
datalen = priv->xfer.data_len;
|
||||
|
||||
mlxcpld_i2c_read_comm(priv, MLXCPLD_LPCI2C_DATA_REG,
|
||||
priv->xfer.msg[i].buf, datalen);
|
||||
|
||||
return datalen;
|
||||
|
||||
case MLXCPLD_LPCI2C_NACK_IND:
|
||||
return -ENXIO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void mlxcpld_i2c_xfer_msg(struct mlxcpld_i2c_priv *priv)
|
||||
{
|
||||
int i, len = 0;
|
||||
u8 cmd;
|
||||
|
||||
mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_DAT_REG,
|
||||
&priv->xfer.data_len, 1);
|
||||
mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_NUM_ADDR_REG,
|
||||
&priv->xfer.addr_width, 1);
|
||||
|
||||
for (i = 0; i < priv->xfer.msg_num; i++) {
|
||||
if ((priv->xfer.msg[i].flags & I2C_M_RD) != I2C_M_RD) {
|
||||
/* Don't write to CPLD buffer in read transaction */
|
||||
mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_DATA_REG +
|
||||
len, priv->xfer.msg[i].buf,
|
||||
priv->xfer.msg[i].len);
|
||||
len += priv->xfer.msg[i].len;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set target slave address with command for master transfer.
|
||||
* It should be latest executed function before CPLD transaction.
|
||||
*/
|
||||
cmd = (priv->xfer.msg[0].addr << 1) | priv->xfer.cmd;
|
||||
mlxcpld_i2c_write_comm(priv, MLXCPLD_LPCI2C_CMD_REG, &cmd, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic lpc-i2c transfer.
|
||||
* Returns the number of processed messages or error (<0).
|
||||
*/
|
||||
static int mlxcpld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct mlxcpld_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
u8 comm_len = 0;
|
||||
int i, err;
|
||||
|
||||
err = mlxcpld_i2c_check_msg_params(priv, msgs, num);
|
||||
if (err) {
|
||||
dev_err(priv->dev, "Incorrect message\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; ++i)
|
||||
comm_len += msgs[i].len;
|
||||
|
||||
/* Check bus state */
|
||||
if (mlxcpld_i2c_wait_for_free(priv)) {
|
||||
dev_err(priv->dev, "LPCI2C bridge is busy\n");
|
||||
|
||||
/*
|
||||
* Usually it means something serious has happened.
|
||||
* We can not have unfinished previous transfer
|
||||
* so it doesn't make any sense to try to stop it.
|
||||
* Probably we were not able to recover from the
|
||||
* previous error.
|
||||
* The only reasonable thing - is soft reset.
|
||||
*/
|
||||
mlxcpld_i2c_reset(priv);
|
||||
if (mlxcpld_i2c_check_busy(priv)) {
|
||||
dev_err(priv->dev, "LPCI2C bridge is busy after reset\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
mlxcpld_i2c_set_transf_data(priv, msgs, num, comm_len);
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* Do real transfer. Can't fail */
|
||||
mlxcpld_i2c_xfer_msg(priv);
|
||||
|
||||
/* Wait for transaction complete */
|
||||
err = mlxcpld_i2c_wait_for_tc(priv);
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return err < 0 ? err : num;
|
||||
}
|
||||
|
||||
static u32 mlxcpld_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm mlxcpld_i2c_algo = {
|
||||
.master_xfer = mlxcpld_i2c_xfer,
|
||||
.functionality = mlxcpld_i2c_func
|
||||
};
|
||||
|
||||
static struct i2c_adapter_quirks mlxcpld_i2c_quirks = {
|
||||
.flags = I2C_AQ_COMB_WRITE_THEN_READ,
|
||||
.max_read_len = MLXCPLD_I2C_DATA_REG_SZ - MLXCPLD_I2C_MAX_ADDR_LEN,
|
||||
.max_write_len = MLXCPLD_I2C_DATA_REG_SZ,
|
||||
.max_comb_1st_msg_len = 4,
|
||||
};
|
||||
|
||||
static struct i2c_adapter mlxcpld_i2c_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "i2c-mlxcpld",
|
||||
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
|
||||
.algo = &mlxcpld_i2c_algo,
|
||||
.quirks = &mlxcpld_i2c_quirks,
|
||||
.retries = MLXCPLD_I2C_RETR_NUM,
|
||||
.nr = MLXCPLD_I2C_BUS_NUM,
|
||||
};
|
||||
|
||||
static int mlxcpld_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxcpld_i2c_priv *priv;
|
||||
int err;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
|
||||
/* Register with i2c layer */
|
||||
mlxcpld_i2c_adapter.timeout = usecs_to_jiffies(MLXCPLD_I2C_XFER_TO);
|
||||
priv->adap = mlxcpld_i2c_adapter;
|
||||
priv->adap.dev.parent = &pdev->dev;
|
||||
priv->base_addr = MLXPLAT_CPLD_LPC_I2C_BASE_ADDR;
|
||||
i2c_set_adapdata(&priv->adap, priv);
|
||||
|
||||
err = i2c_add_numbered_adapter(&priv->adap);
|
||||
if (err)
|
||||
mutex_destroy(&priv->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxcpld_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxcpld_i2c_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&priv->adap);
|
||||
mutex_destroy(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mlxcpld_i2c_driver = {
|
||||
.probe = mlxcpld_i2c_probe,
|
||||
.remove = mlxcpld_i2c_remove,
|
||||
.driver = {
|
||||
.name = MLXCPLD_I2C_DEVICE_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mlxcpld_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Shych <michaels@mellanox.com>");
|
||||
MODULE_DESCRIPTION("Mellanox I2C-CPLD controller driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_ALIAS("platform:i2c-mlxcpld");
|
|
@ -36,24 +36,6 @@ static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c)
|
|||
return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG);
|
||||
}
|
||||
|
||||
static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first)
|
||||
{
|
||||
if (octeon_i2c_test_iflg(i2c))
|
||||
return true;
|
||||
|
||||
if (*first) {
|
||||
*first = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ has signaled an event but IFLG hasn't changed.
|
||||
* Sleep and retry once.
|
||||
*/
|
||||
usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
|
||||
return octeon_i2c_test_iflg(i2c);
|
||||
}
|
||||
|
||||
/**
|
||||
* octeon_i2c_wait - wait for the IFLG to be set
|
||||
* @i2c: The struct octeon_i2c
|
||||
|
@ -63,7 +45,6 @@ static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first)
|
|||
static int octeon_i2c_wait(struct octeon_i2c *i2c)
|
||||
{
|
||||
long time_left;
|
||||
bool first = true;
|
||||
|
||||
/*
|
||||
* Some chip revisions don't assert the irq in the interrupt
|
||||
|
@ -80,7 +61,7 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
|
|||
}
|
||||
|
||||
i2c->int_enable(i2c);
|
||||
time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first),
|
||||
time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c),
|
||||
i2c->adap.timeout);
|
||||
i2c->int_disable(i2c);
|
||||
|
||||
|
@ -102,25 +83,6 @@ static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c)
|
|||
return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
|
||||
}
|
||||
|
||||
static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first)
|
||||
{
|
||||
/* check if valid bit is cleared */
|
||||
if (octeon_i2c_hlc_test_valid(i2c))
|
||||
return true;
|
||||
|
||||
if (*first) {
|
||||
*first = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ has signaled an event but valid bit isn't cleared.
|
||||
* Sleep and retry once.
|
||||
*/
|
||||
usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
|
||||
return octeon_i2c_hlc_test_valid(i2c);
|
||||
}
|
||||
|
||||
static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c)
|
||||
{
|
||||
/* clear ST/TS events, listen for neither */
|
||||
|
@ -176,7 +138,6 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c)
|
|||
*/
|
||||
static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
|
||||
{
|
||||
bool first = true;
|
||||
int time_left;
|
||||
|
||||
/*
|
||||
|
@ -195,7 +156,7 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
|
|||
|
||||
i2c->hlc_int_enable(i2c);
|
||||
time_left = wait_event_timeout(i2c->queue,
|
||||
octeon_i2c_hlc_test_ready(i2c, &first),
|
||||
octeon_i2c_hlc_test_valid(i2c),
|
||||
i2c->adap.timeout);
|
||||
i2c->hlc_int_disable(i2c);
|
||||
if (!time_left)
|
||||
|
@ -789,6 +750,9 @@ static void octeon_i2c_prepare_recovery(struct i2c_adapter *adap)
|
|||
struct octeon_i2c *i2c = i2c_get_adapdata(adap);
|
||||
|
||||
octeon_i2c_hlc_disable(i2c);
|
||||
octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0);
|
||||
/* wait for software reset to settle */
|
||||
udelay(5);
|
||||
|
||||
/*
|
||||
* Bring control register to a good state regardless
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
/*
|
||||
* CE4100 PCI-I2C glue code for PXA's driver
|
||||
* Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
* License: GPL v2
|
||||
*
|
||||
* The CE4100's I2C device is more or less the same one as found on PXA.
|
||||
* It does not support slave mode, the register slightly moved. This PCI
|
||||
* device provides three bars, every contains a single I2C controller.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c/pxa-i2c.h>
|
||||
|
@ -134,35 +138,17 @@ static int ce4100_i2c_probe(struct pci_dev *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ce4100_i2c_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct ce4100_devices *sds;
|
||||
unsigned int i;
|
||||
|
||||
sds = pci_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sds->pdev); i++)
|
||||
platform_device_unregister(sds->pdev[i]);
|
||||
|
||||
pci_disable_device(dev);
|
||||
kfree(sds);
|
||||
}
|
||||
|
||||
static const struct pci_device_id ce4100_i2c_devices[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e68)},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ce4100_i2c_devices);
|
||||
|
||||
static struct pci_driver ce4100_i2c_driver = {
|
||||
.driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.name = "ce4100_i2c",
|
||||
.id_table = ce4100_i2c_devices,
|
||||
.probe = ce4100_i2c_probe,
|
||||
.remove = ce4100_i2c_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(ce4100_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("CE4100 PCI-I2C glue code for PXA's driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
|
||||
builtin_pci_driver(ce4100_i2c_driver);
|
||||
|
|
|
@ -48,6 +48,8 @@ struct pxa_reg_layout {
|
|||
u32 isar;
|
||||
u32 ilcr;
|
||||
u32 iwcr;
|
||||
u32 fm;
|
||||
u32 hs;
|
||||
};
|
||||
|
||||
enum pxa_i2c_types {
|
||||
|
@ -55,8 +57,12 @@ enum pxa_i2c_types {
|
|||
REGS_PXA3XX,
|
||||
REGS_CE4100,
|
||||
REGS_PXA910,
|
||||
REGS_A3700,
|
||||
};
|
||||
|
||||
#define ICR_BUSMODE_FM (1 << 16) /* shifted fast mode for armada-3700 */
|
||||
#define ICR_BUSMODE_HS (1 << 17) /* shifted high speed mode for armada-3700 */
|
||||
|
||||
/*
|
||||
* I2C registers definitions
|
||||
*/
|
||||
|
@ -91,6 +97,15 @@ static struct pxa_reg_layout pxa_reg_layout[] = {
|
|||
.ilcr = 0x28,
|
||||
.iwcr = 0x30,
|
||||
},
|
||||
[REGS_A3700] = {
|
||||
.ibmr = 0x00,
|
||||
.idbr = 0x04,
|
||||
.icr = 0x08,
|
||||
.isr = 0x0c,
|
||||
.isar = 0x10,
|
||||
.fm = ICR_BUSMODE_FM,
|
||||
.hs = ICR_BUSMODE_HS,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct platform_device_id i2c_pxa_id_table[] = {
|
||||
|
@ -98,6 +113,7 @@ static const struct platform_device_id i2c_pxa_id_table[] = {
|
|||
{ "pxa3xx-pwri2c", REGS_PXA3XX },
|
||||
{ "ce4100-i2c", REGS_CE4100 },
|
||||
{ "pxa910-i2c", REGS_PXA910 },
|
||||
{ "armada-3700-i2c", REGS_A3700 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table);
|
||||
|
@ -193,6 +209,8 @@ struct pxa_i2c {
|
|||
unsigned char master_code;
|
||||
unsigned long rate;
|
||||
bool highmode_enter;
|
||||
u32 fm_mask;
|
||||
u32 hs_mask;
|
||||
};
|
||||
|
||||
#define _IBMR(i2c) ((i2c)->reg_ibmr)
|
||||
|
@ -503,8 +521,8 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c)
|
|||
writel(i2c->slave_addr, _ISAR(i2c));
|
||||
|
||||
/* set control register values */
|
||||
writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c));
|
||||
writel(readl(_ICR(i2c)) | (i2c->high_mode ? ICR_HS : 0), _ICR(i2c));
|
||||
writel(I2C_ICR_INIT | (i2c->fast_mode ? i2c->fm_mask : 0), _ICR(i2c));
|
||||
writel(readl(_ICR(i2c)) | (i2c->high_mode ? i2c->hs_mask : 0), _ICR(i2c));
|
||||
|
||||
#ifdef CONFIG_I2C_PXA_SLAVE
|
||||
dev_info(&i2c->adap.dev, "Enabling slave mode\n");
|
||||
|
@ -1137,6 +1155,7 @@ static const struct of_device_id i2c_pxa_dt_ids[] = {
|
|||
{ .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
|
||||
{ .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
|
||||
{ .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA910 },
|
||||
{ .compatible = "marvell,armada-3700-i2c", .data = (void *)REGS_A3700 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
|
||||
|
@ -1234,6 +1253,9 @@ static int i2c_pxa_probe(struct platform_device *dev)
|
|||
i2c->reg_idbr = i2c->reg_base + pxa_reg_layout[i2c_type].idbr;
|
||||
i2c->reg_icr = i2c->reg_base + pxa_reg_layout[i2c_type].icr;
|
||||
i2c->reg_isr = i2c->reg_base + pxa_reg_layout[i2c_type].isr;
|
||||
i2c->fm_mask = pxa_reg_layout[i2c_type].fm ? : ICR_FM;
|
||||
i2c->hs_mask = pxa_reg_layout[i2c_type].hs ? : ICR_HS;
|
||||
|
||||
if (i2c_type != REGS_CE4100)
|
||||
i2c->reg_isar = i2c->reg_base + pxa_reg_layout[i2c_type].isar;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -132,6 +133,10 @@
|
|||
/* Max timeout in ms for 32k bytes */
|
||||
#define TOUT_MAX 300
|
||||
|
||||
/* Default values. Use these if FW query fails */
|
||||
#define DEFAULT_CLK_FREQ 100000
|
||||
#define DEFAULT_SRC_CLK 20000000
|
||||
|
||||
struct qup_i2c_block {
|
||||
int count;
|
||||
int pos;
|
||||
|
@ -525,6 +530,33 @@ static int qup_i2c_get_data_len(struct qup_i2c_dev *qup)
|
|||
return data_len;
|
||||
}
|
||||
|
||||
static bool qup_i2c_check_msg_len(struct i2c_msg *msg)
|
||||
{
|
||||
return ((msg->flags & I2C_M_RD) && (msg->flags & I2C_M_RECV_LEN));
|
||||
}
|
||||
|
||||
static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (msg->len > 1) {
|
||||
tags[len++] = QUP_TAG_V2_DATARD_STOP;
|
||||
tags[len++] = qup_i2c_get_data_len(qup) - 1;
|
||||
} else {
|
||||
tags[len++] = QUP_TAG_V2_START;
|
||||
tags[len++] = addr & 0xff;
|
||||
|
||||
if (msg->flags & I2C_M_TEN)
|
||||
tags[len++] = addr >> 8;
|
||||
|
||||
tags[len++] = QUP_TAG_V2_DATARD;
|
||||
/* Read 1 byte indicating the length of the SMBus message */
|
||||
tags[len++] = 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
|
||||
struct i2c_msg *msg, int is_dma)
|
||||
{
|
||||
|
@ -534,6 +566,10 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
|
|||
|
||||
int last = (qup->blk.pos == (qup->blk.count - 1)) && (qup->is_last);
|
||||
|
||||
/* Handle tags for SMBus block read */
|
||||
if (qup_i2c_check_msg_len(msg))
|
||||
return qup_i2c_set_tags_smb(addr, tags, qup, msg);
|
||||
|
||||
if (qup->blk.pos == 0) {
|
||||
tags[len++] = QUP_TAG_V2_START;
|
||||
tags[len++] = addr & 0xff;
|
||||
|
@ -1056,9 +1092,17 @@ static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
|
|||
struct i2c_msg *msg)
|
||||
{
|
||||
u32 val;
|
||||
int idx, pos = 0, ret = 0, total;
|
||||
int idx, pos = 0, ret = 0, total, msg_offset = 0;
|
||||
|
||||
/*
|
||||
* If the message length is already read in
|
||||
* the first byte of the buffer, account for
|
||||
* that by setting the offset
|
||||
*/
|
||||
if (qup_i2c_check_msg_len(msg) && (msg->len > 1))
|
||||
msg_offset = 1;
|
||||
total = qup_i2c_get_data_len(qup);
|
||||
total -= msg_offset;
|
||||
|
||||
/* 2 extra bytes for read tags */
|
||||
while (pos < (total + 2)) {
|
||||
|
@ -1078,8 +1122,8 @@ static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
|
|||
|
||||
if (pos >= (total + 2))
|
||||
goto out;
|
||||
|
||||
msg->buf[qup->pos++] = val & 0xff;
|
||||
msg->buf[qup->pos + msg_offset] = val & 0xff;
|
||||
qup->pos++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1119,6 +1163,20 @@ static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
|||
goto err;
|
||||
|
||||
qup->blk.pos++;
|
||||
|
||||
/* Handle SMBus block read length */
|
||||
if (qup_i2c_check_msg_len(msg) && (msg->len == 1)) {
|
||||
if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX) {
|
||||
ret = -EPROTO;
|
||||
goto err;
|
||||
}
|
||||
msg->len += msg->buf[0];
|
||||
qup->pos = 0;
|
||||
qup_i2c_set_blk_data(qup, msg);
|
||||
/* set tag length for block read */
|
||||
qup->blk.tx_tag_len = 2;
|
||||
qup_i2c_set_read_mode_v2(qup, msg->buf[0]);
|
||||
}
|
||||
} while (qup->blk.pos < qup->blk.count);
|
||||
|
||||
err:
|
||||
|
@ -1204,6 +1262,11 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (qup_i2c_check_msg_len(&msgs[idx])) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (msgs[idx].flags & I2C_M_RD)
|
||||
ret = qup_i2c_read_one(qup, &msgs[idx]);
|
||||
else
|
||||
|
@ -1358,14 +1421,13 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
|
|||
static int qup_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
static const int blk_sizes[] = {4, 16, 32};
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct qup_i2c_dev *qup;
|
||||
unsigned long one_bit_t;
|
||||
struct resource *res;
|
||||
u32 io_mode, hw_ver, size;
|
||||
int ret, fs_div, hs_div;
|
||||
int src_clk_freq;
|
||||
u32 clk_freq = 100000;
|
||||
u32 src_clk_freq = DEFAULT_SRC_CLK;
|
||||
u32 clk_freq = DEFAULT_CLK_FREQ;
|
||||
int blocks;
|
||||
|
||||
qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
|
||||
|
@ -1376,7 +1438,11 @@ static int qup_i2c_probe(struct platform_device *pdev)
|
|||
init_completion(&qup->xfer);
|
||||
platform_set_drvdata(pdev, qup);
|
||||
|
||||
of_property_read_u32(node, "clock-frequency", &clk_freq);
|
||||
ret = device_property_read_u32(qup->dev, "clock-frequency", &clk_freq);
|
||||
if (ret) {
|
||||
dev_notice(qup->dev, "using default clock-frequency %d",
|
||||
DEFAULT_CLK_FREQ);
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
|
||||
qup->adap.algo = &qup_i2c_algo;
|
||||
|
@ -1452,20 +1518,30 @@ static int qup_i2c_probe(struct platform_device *pdev)
|
|||
return qup->irq;
|
||||
}
|
||||
|
||||
qup->clk = devm_clk_get(qup->dev, "core");
|
||||
if (IS_ERR(qup->clk)) {
|
||||
dev_err(qup->dev, "Could not get core clock\n");
|
||||
return PTR_ERR(qup->clk);
|
||||
}
|
||||
if (has_acpi_companion(qup->dev)) {
|
||||
ret = device_property_read_u32(qup->dev,
|
||||
"src-clock-hz", &src_clk_freq);
|
||||
if (ret) {
|
||||
dev_notice(qup->dev, "using default src-clock-hz %d",
|
||||
DEFAULT_SRC_CLK);
|
||||
}
|
||||
ACPI_COMPANION_SET(&qup->adap.dev, ACPI_COMPANION(qup->dev));
|
||||
} else {
|
||||
qup->clk = devm_clk_get(qup->dev, "core");
|
||||
if (IS_ERR(qup->clk)) {
|
||||
dev_err(qup->dev, "Could not get core clock\n");
|
||||
return PTR_ERR(qup->clk);
|
||||
}
|
||||
|
||||
qup->pclk = devm_clk_get(qup->dev, "iface");
|
||||
if (IS_ERR(qup->pclk)) {
|
||||
dev_err(qup->dev, "Could not get iface clock\n");
|
||||
return PTR_ERR(qup->pclk);
|
||||
qup->pclk = devm_clk_get(qup->dev, "iface");
|
||||
if (IS_ERR(qup->pclk)) {
|
||||
dev_err(qup->dev, "Could not get iface clock\n");
|
||||
return PTR_ERR(qup->pclk);
|
||||
}
|
||||
qup_i2c_enable_clocks(qup);
|
||||
src_clk_freq = clk_get_rate(qup->clk);
|
||||
}
|
||||
|
||||
qup_i2c_enable_clocks(qup);
|
||||
|
||||
/*
|
||||
* Bootloaders might leave a pending interrupt on certain QUP's,
|
||||
* so we reset the core before registering for interrupts.
|
||||
|
@ -1512,7 +1588,6 @@ static int qup_i2c_probe(struct platform_device *pdev)
|
|||
size = QUP_INPUT_FIFO_SIZE(io_mode);
|
||||
qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
|
||||
|
||||
src_clk_freq = clk_get_rate(qup->clk);
|
||||
fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
|
||||
hs_div = 3;
|
||||
qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
|
||||
|
@ -1631,6 +1706,14 @@ static const struct of_device_id qup_i2c_dt_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
static const struct acpi_device_id qup_i2c_acpi_match[] = {
|
||||
{ "QCOM8010"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver qup_i2c_driver = {
|
||||
.probe = qup_i2c_probe,
|
||||
.remove = qup_i2c_remove,
|
||||
|
@ -1638,6 +1721,7 @@ static struct platform_driver qup_i2c_driver = {
|
|||
.name = "i2c_qup",
|
||||
.pm = &qup_i2c_qup_pm_ops,
|
||||
.of_match_table = qup_i2c_dt_match,
|
||||
.acpi_match_table = ACPI_PTR(qup_i2c_acpi_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -793,7 +793,6 @@ static const struct i2c_algorithm rcar_i2c_algo = {
|
|||
};
|
||||
|
||||
static const struct of_device_id rcar_i2c_dt_ids[] = {
|
||||
{ .compatible = "renesas,i2c-rcar", .data = (void *)I2C_RCAR_GEN1 },
|
||||
{ .compatible = "renesas,i2c-r8a7778", .data = (void *)I2C_RCAR_GEN1 },
|
||||
{ .compatible = "renesas,i2c-r8a7779", .data = (void *)I2C_RCAR_GEN1 },
|
||||
{ .compatible = "renesas,i2c-r8a7790", .data = (void *)I2C_RCAR_GEN2 },
|
||||
|
@ -803,6 +802,10 @@ static const struct of_device_id rcar_i2c_dt_ids[] = {
|
|||
{ .compatible = "renesas,i2c-r8a7794", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{ .compatible = "renesas,i2c-r8a7795", .data = (void *)I2C_RCAR_GEN3 },
|
||||
{ .compatible = "renesas,i2c-r8a7796", .data = (void *)I2C_RCAR_GEN3 },
|
||||
{ .compatible = "renesas,i2c-rcar", .data = (void *)I2C_RCAR_GEN1 }, /* Deprecated */
|
||||
{ .compatible = "renesas,rcar-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 },
|
||||
{ .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 },
|
||||
{ .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids);
|
||||
|
|
|
@ -827,7 +827,6 @@ static const struct sh_mobile_dt_config r8a7740_dt_config = {
|
|||
};
|
||||
|
||||
static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
|
||||
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config },
|
||||
|
@ -835,8 +834,11 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
|
|||
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids);
|
||||
|
|
|
@ -528,7 +528,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
|
|||
if (!clk_rate) {
|
||||
dev_err(dev, "input clock rate should not be zero\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
init_completion(&priv->comp);
|
||||
|
@ -547,11 +547,11 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
|
|||
pdev->name, priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
goto err;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&priv->adap);
|
||||
err:
|
||||
disable_clk:
|
||||
if (ret)
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
|
|
|
@ -373,7 +373,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
|
|||
if (!clk_rate) {
|
||||
dev_err(dev, "input clock rate should not be zero\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
init_completion(&priv->comp);
|
||||
|
@ -392,11 +392,11 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
|
|||
priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
goto err;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
ret = i2c_add_adapter(&priv->adap);
|
||||
err:
|
||||
disable_clk:
|
||||
if (ret)
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
|
|
|
@ -354,7 +354,7 @@ static const struct i2c_algorithm vprbrd_algorithm = {
|
|||
.functionality = vprbrd_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter_quirks vprbrd_quirks = {
|
||||
static const struct i2c_adapter_quirks vprbrd_quirks = {
|
||||
.max_read_len = 2048,
|
||||
.max_write_len = 2048,
|
||||
};
|
||||
|
|
|
@ -393,6 +393,7 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev)
|
|||
init_completion(&priv->msg_complete);
|
||||
priv->adapter.dev.parent = &pdev->dev;
|
||||
priv->adapter.algo = &xlp9xx_i2c_algo;
|
||||
ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev));
|
||||
priv->adapter.dev.of_node = pdev->dev.of_node;
|
||||
priv->dev = &pdev->dev;
|
||||
|
||||
|
|
|
@ -65,6 +65,9 @@
|
|||
#define I2C_ADDR_OFFSET_TEN_BIT 0xa000
|
||||
#define I2C_ADDR_OFFSET_SLAVE 0x1000
|
||||
|
||||
#define I2C_ADDR_7BITS_MAX 0x77
|
||||
#define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1)
|
||||
|
||||
/* core_lock protects i2c_adapter_idr, and guarantees
|
||||
that device detection, deletion of detected devices, and attach_adapter
|
||||
calls are serialized */
|
||||
|
@ -676,9 +679,12 @@ static inline int i2c_acpi_install_space_handler(struct i2c_adapter *adapter)
|
|||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||
const struct i2c_client *client)
|
||||
{
|
||||
if (!(id && client))
|
||||
return NULL;
|
||||
|
||||
while (id->name[0]) {
|
||||
if (strcmp(client->name, id->name) == 0)
|
||||
return id;
|
||||
|
@ -686,17 +692,16 @@ static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_match_id);
|
||||
|
||||
static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct i2c_client *client = i2c_verify_client(dev);
|
||||
struct i2c_driver *driver;
|
||||
|
||||
if (!client)
|
||||
return 0;
|
||||
|
||||
/* Attempt an OF style match */
|
||||
if (of_driver_match_device(dev, drv))
|
||||
if (i2c_of_match_device(drv->of_match_table, client))
|
||||
return 1;
|
||||
|
||||
/* Then ACPI style match */
|
||||
|
@ -704,9 +709,10 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
|||
return 1;
|
||||
|
||||
driver = to_i2c_driver(drv);
|
||||
/* match on an id table if there is one */
|
||||
if (driver->id_table)
|
||||
return i2c_match_id(driver->id_table, client) != NULL;
|
||||
|
||||
/* Finally an I2C match */
|
||||
if (i2c_match_id(driver->id_table, client))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -893,6 +899,25 @@ static void i2c_init_recovery(struct i2c_adapter *adap)
|
|||
adap->bus_recovery_info = NULL;
|
||||
}
|
||||
|
||||
static int i2c_smbus_host_notify_to_irq(const struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adap = client->adapter;
|
||||
unsigned int irq;
|
||||
|
||||
if (!adap->host_notify_domain)
|
||||
return -ENXIO;
|
||||
|
||||
if (client->flags & I2C_CLIENT_TEN)
|
||||
return -EINVAL;
|
||||
|
||||
irq = irq_find_mapping(adap->host_notify_domain, client->addr);
|
||||
if (!irq)
|
||||
irq = irq_create_mapping(adap->host_notify_domain,
|
||||
client->addr);
|
||||
|
||||
return irq > 0 ? irq : -ENXIO;
|
||||
}
|
||||
|
||||
static int i2c_device_probe(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = i2c_verify_client(dev);
|
||||
|
@ -914,6 +939,14 @@ static int i2c_device_probe(struct device *dev)
|
|||
}
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return irq;
|
||||
/*
|
||||
* ACPI and OF did not find any useful IRQ, try to see
|
||||
* if Host Notify can be used.
|
||||
*/
|
||||
if (irq < 0) {
|
||||
dev_dbg(dev, "Using Host Notify IRQ\n");
|
||||
irq = i2c_smbus_host_notify_to_irq(client);
|
||||
}
|
||||
if (irq < 0)
|
||||
irq = 0;
|
||||
|
||||
|
@ -921,7 +954,13 @@ static int i2c_device_probe(struct device *dev)
|
|||
}
|
||||
|
||||
driver = to_i2c_driver(dev->driver);
|
||||
if (!driver->probe || !driver->id_table)
|
||||
|
||||
/*
|
||||
* An I2C ID table is not mandatory, if and only if, a suitable Device
|
||||
* Tree match table entry is supplied for the probing device.
|
||||
*/
|
||||
if (!driver->id_table &&
|
||||
!i2c_of_match_device(dev->driver->of_match_table, client))
|
||||
return -ENODEV;
|
||||
|
||||
if (client->flags & I2C_CLIENT_WAKE) {
|
||||
|
@ -956,7 +995,18 @@ static int i2c_device_probe(struct device *dev)
|
|||
if (status == -EPROBE_DEFER)
|
||||
goto err_clear_wakeup_irq;
|
||||
|
||||
status = driver->probe(client, i2c_match_id(driver->id_table, client));
|
||||
/*
|
||||
* When there are no more users of probe(),
|
||||
* rename probe_new to probe.
|
||||
*/
|
||||
if (driver->probe_new)
|
||||
status = driver->probe_new(client);
|
||||
else if (driver->probe)
|
||||
status = driver->probe(client,
|
||||
i2c_match_id(driver->id_table, client));
|
||||
else
|
||||
status = -EINVAL;
|
||||
|
||||
if (status)
|
||||
goto err_detach_pm_domain;
|
||||
|
||||
|
@ -1767,6 +1817,52 @@ struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node)
|
|||
return adapter;
|
||||
}
|
||||
EXPORT_SYMBOL(of_get_i2c_adapter_by_node);
|
||||
|
||||
static const struct of_device_id*
|
||||
i2c_of_match_device_sysfs(const struct of_device_id *matches,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
for (; matches->compatible[0]; matches++) {
|
||||
/*
|
||||
* Adding devices through the i2c sysfs interface provides us
|
||||
* a string to match which may be compatible with the device
|
||||
* tree compatible strings, however with no actual of_node the
|
||||
* of_match_device() will not match
|
||||
*/
|
||||
if (sysfs_streq(client->name, matches->compatible))
|
||||
return matches;
|
||||
|
||||
name = strchr(matches->compatible, ',');
|
||||
if (!name)
|
||||
name = matches->compatible;
|
||||
else
|
||||
name++;
|
||||
|
||||
if (sysfs_streq(client->name, name))
|
||||
return matches;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct of_device_id
|
||||
*i2c_of_match_device(const struct of_device_id *matches,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (!(client && matches))
|
||||
return NULL;
|
||||
|
||||
match = of_match_device(matches, &client->dev);
|
||||
if (match)
|
||||
return match;
|
||||
|
||||
return i2c_of_match_device_sysfs(matches, client);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_of_match_device);
|
||||
#else
|
||||
static void of_i2c_register_devices(struct i2c_adapter *adap) { }
|
||||
#endif /* CONFIG_OF */
|
||||
|
@ -1800,6 +1896,79 @@ static const struct i2c_lock_operations i2c_adapter_lock_ops = {
|
|||
.unlock_bus = i2c_adapter_unlock_bus,
|
||||
};
|
||||
|
||||
static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap)
|
||||
{
|
||||
struct irq_domain *domain = adap->host_notify_domain;
|
||||
irq_hw_number_t hwirq;
|
||||
|
||||
if (!domain)
|
||||
return;
|
||||
|
||||
for (hwirq = 0 ; hwirq < I2C_ADDR_7BITS_COUNT ; hwirq++)
|
||||
irq_dispose_mapping(irq_find_mapping(domain, hwirq));
|
||||
|
||||
irq_domain_remove(domain);
|
||||
adap->host_notify_domain = NULL;
|
||||
}
|
||||
|
||||
static int i2c_host_notify_irq_map(struct irq_domain *h,
|
||||
unsigned int virq,
|
||||
irq_hw_number_t hw_irq_num)
|
||||
{
|
||||
irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops i2c_host_notify_irq_ops = {
|
||||
.map = i2c_host_notify_irq_map,
|
||||
};
|
||||
|
||||
static int i2c_setup_host_notify_irq_domain(struct i2c_adapter *adap)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_HOST_NOTIFY))
|
||||
return 0;
|
||||
|
||||
domain = irq_domain_create_linear(adap->dev.fwnode,
|
||||
I2C_ADDR_7BITS_COUNT,
|
||||
&i2c_host_notify_irq_ops, adap);
|
||||
if (!domain)
|
||||
return -ENOMEM;
|
||||
|
||||
adap->host_notify_domain = domain;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
|
||||
* I2C client.
|
||||
* @adap: the adapter
|
||||
* @addr: the I2C address of the notifying device
|
||||
* Context: can't sleep
|
||||
*
|
||||
* Helper function to be called from an I2C bus driver's interrupt
|
||||
* handler. It will schedule the Host Notify IRQ.
|
||||
*/
|
||||
int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr)
|
||||
{
|
||||
int irq;
|
||||
|
||||
if (!adap)
|
||||
return -EINVAL;
|
||||
|
||||
irq = irq_find_mapping(adap->host_notify_domain, addr);
|
||||
if (irq <= 0)
|
||||
return -ENXIO;
|
||||
|
||||
generic_handle_irq(irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
|
||||
|
||||
static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||
{
|
||||
int res = -EINVAL;
|
||||
|
@ -1831,6 +2000,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||
if (adap->timeout == 0)
|
||||
adap->timeout = HZ;
|
||||
|
||||
/* register soft irqs for Host Notify */
|
||||
res = i2c_setup_host_notify_irq_domain(adap);
|
||||
if (res) {
|
||||
pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
|
||||
adap->name, res);
|
||||
goto out_list;
|
||||
}
|
||||
|
||||
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
|
||||
adap->dev.bus = &i2c_bus_type;
|
||||
adap->dev.type = &i2c_adapter_type;
|
||||
|
@ -2068,6 +2245,8 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
|||
|
||||
pm_runtime_disable(&adap->dev);
|
||||
|
||||
i2c_host_notify_irq_teardown(adap);
|
||||
|
||||
/* wait until all references to the device are gone
|
||||
*
|
||||
* FIXME: This is old code and should ideally be replaced by an
|
||||
|
|
|
@ -241,108 +241,6 @@ int i2c_handle_smbus_alert(struct i2c_client *ara)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
|
||||
|
||||
static void smbus_host_notify_work(struct work_struct *work)
|
||||
{
|
||||
struct alert_data alert;
|
||||
struct i2c_adapter *adapter;
|
||||
unsigned long flags;
|
||||
u16 payload;
|
||||
u8 addr;
|
||||
struct smbus_host_notify *data;
|
||||
|
||||
data = container_of(work, struct smbus_host_notify, work);
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
payload = data->payload;
|
||||
addr = data->addr;
|
||||
adapter = data->adapter;
|
||||
|
||||
/* clear the pending bit and release the spinlock */
|
||||
data->pending = false;
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
|
||||
if (!adapter || !addr)
|
||||
return;
|
||||
|
||||
alert.type = I2C_PROTOCOL_SMBUS_HOST_NOTIFY;
|
||||
alert.addr = addr;
|
||||
alert.data = payload;
|
||||
|
||||
device_for_each_child(&adapter->dev, &alert, smbus_do_alert);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_setup_smbus_host_notify - Allocate a new smbus_host_notify for the given
|
||||
* I2C adapter.
|
||||
* @adapter: the adapter we want to associate a Host Notify function
|
||||
*
|
||||
* Returns a struct smbus_host_notify pointer on success, and NULL on failure.
|
||||
* The resulting smbus_host_notify must not be freed afterwards, it is a
|
||||
* managed resource already.
|
||||
*/
|
||||
struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
|
||||
{
|
||||
struct smbus_host_notify *host_notify;
|
||||
|
||||
host_notify = devm_kzalloc(&adap->dev, sizeof(struct smbus_host_notify),
|
||||
GFP_KERNEL);
|
||||
if (!host_notify)
|
||||
return NULL;
|
||||
|
||||
host_notify->adapter = adap;
|
||||
|
||||
spin_lock_init(&host_notify->lock);
|
||||
INIT_WORK(&host_notify->work, smbus_host_notify_work);
|
||||
|
||||
return host_notify;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_setup_smbus_host_notify);
|
||||
|
||||
/**
|
||||
* i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
|
||||
* I2C client.
|
||||
* @host_notify: the struct host_notify attached to the relevant adapter
|
||||
* @addr: the I2C address of the notifying device
|
||||
* @data: the payload of the notification
|
||||
* Context: can't sleep
|
||||
*
|
||||
* Helper function to be called from an I2C bus driver's interrupt
|
||||
* handler. It will schedule the Host Notify work, in turn calling the
|
||||
* corresponding I2C device driver's alert function.
|
||||
*
|
||||
* host_notify should be a valid pointer previously returned by
|
||||
* i2c_setup_smbus_host_notify().
|
||||
*/
|
||||
int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
|
||||
unsigned short addr, unsigned int data)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
if (!host_notify || !host_notify->adapter)
|
||||
return -EINVAL;
|
||||
|
||||
adapter = host_notify->adapter;
|
||||
|
||||
spin_lock_irqsave(&host_notify->lock, flags);
|
||||
|
||||
if (host_notify->pending) {
|
||||
spin_unlock_irqrestore(&host_notify->lock, flags);
|
||||
dev_warn(&adapter->dev, "Host Notify already scheduled.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
host_notify->payload = data;
|
||||
host_notify->addr = addr;
|
||||
|
||||
/* Mark that there is a pending notification and release the lock */
|
||||
host_notify->pending = true;
|
||||
spin_unlock_irqrestore(&host_notify->lock, flags);
|
||||
|
||||
return schedule_work(&host_notify->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
|
||||
|
||||
module_i2c_driver(smbalert_driver);
|
||||
|
||||
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
||||
|
|
|
@ -82,4 +82,15 @@ config I2C_DEMUX_PINCTRL
|
|||
demultiplexer that uses the pinctrl subsystem. This is useful if you
|
||||
want to change the I2C master at run-time depending on features.
|
||||
|
||||
config I2C_MUX_MLXCPLD
|
||||
tristate "Mellanox CPLD based I2C multiplexer"
|
||||
help
|
||||
If you say yes to this option, support will be included for a
|
||||
CPLD based I2C multiplexer. This driver provides access to
|
||||
I2C busses connected through a MUX, which is controlled
|
||||
by a CPLD register.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-mux-mlxcpld.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
|
|||
obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o
|
||||
|
||||
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
||||
obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
|
||||
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
struct gpiomux {
|
||||
struct i2c_mux_gpio_platform_data data;
|
||||
unsigned gpio_base;
|
||||
struct gpio_desc **gpios;
|
||||
int *values;
|
||||
};
|
||||
|
||||
static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
|
||||
|
@ -28,8 +30,10 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < mux->data.n_gpios; i++)
|
||||
gpio_set_value_cansleep(mux->gpio_base + mux->data.gpios[i],
|
||||
val & (1 << i));
|
||||
mux->values[i] = (val >> i) & 1;
|
||||
|
||||
gpiod_set_array_value_cansleep(mux->data.n_gpios,
|
||||
mux->gpios, mux->values);
|
||||
}
|
||||
|
||||
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
|
||||
|
@ -176,12 +180,16 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||
if (!parent)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0,
|
||||
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
|
||||
mux->data.n_gpios * sizeof(*mux->gpios) +
|
||||
mux->data.n_gpios * sizeof(*mux->values), 0,
|
||||
i2c_mux_gpio_select, NULL);
|
||||
if (!muxc) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_failed;
|
||||
}
|
||||
mux->gpios = muxc->priv;
|
||||
mux->values = (int *)(mux->gpios + mux->data.n_gpios);
|
||||
muxc->priv = mux;
|
||||
|
||||
platform_set_drvdata(pdev, muxc);
|
||||
|
@ -219,10 +227,12 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||
goto err_request_gpio;
|
||||
}
|
||||
|
||||
gpio_desc = gpio_to_desc(gpio_base + mux->data.gpios[i]);
|
||||
mux->gpios[i] = gpio_desc;
|
||||
|
||||
if (!muxc->mux_locked)
|
||||
continue;
|
||||
|
||||
gpio_desc = gpio_to_desc(gpio_base + mux->data.gpios[i]);
|
||||
gpio_dev = &gpio_desc->gdev->dev;
|
||||
muxc->mux_locked = i2c_root_adapter(gpio_dev) == root;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* drivers/i2c/muxes/i2c-mux-mlxcpld.c
|
||||
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/i2c/mlxcpld.h>
|
||||
|
||||
#define CPLD_MUX_MAX_NCHANS 8
|
||||
|
||||
/* mlxcpld_mux - mux control structure:
|
||||
* @last_chan - last register value
|
||||
* @client - I2C device client
|
||||
*/
|
||||
struct mlxcpld_mux {
|
||||
u8 last_chan;
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
/* MUX logic description.
|
||||
* Driver can support different mux control logic, according to CPLD
|
||||
* implementation.
|
||||
*
|
||||
* Connectivity schema.
|
||||
*
|
||||
* i2c-mlxcpld Digital Analog
|
||||
* driver
|
||||
* *--------* * -> mux1 (virt bus2) -> mux -> |
|
||||
* | I2CLPC | i2c physical * -> mux2 (virt bus3) -> mux -> |
|
||||
* | bridge | bus 1 *---------* |
|
||||
* | logic |---------------------> * mux reg * |
|
||||
* | in CPLD| *---------* |
|
||||
* *--------* i2c-mux-mlxpcld ^ * -> muxn (virt busn) -> mux -> |
|
||||
* | driver | |
|
||||
* | *---------------* | Devices
|
||||
* | * CPLD (i2c bus)* select |
|
||||
* | * registers for *--------*
|
||||
* | * mux selection * deselect
|
||||
* | *---------------*
|
||||
* | |
|
||||
* <--------> <----------->
|
||||
* i2c cntrl Board cntrl reg
|
||||
* reg space space (mux select,
|
||||
* IO, LED, WD, info)
|
||||
*
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id mlxcpld_mux_id[] = {
|
||||
{ "mlxcpld_mux_module", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mlxcpld_mux_id);
|
||||
|
||||
/* Write to mux register. Don't use i2c_transfer() and i2c_smbus_xfer()
|
||||
* for this as they will try to lock adapter a second time.
|
||||
*/
|
||||
static int mlxcpld_mux_reg_write(struct i2c_adapter *adap,
|
||||
struct i2c_client *client, u8 val)
|
||||
{
|
||||
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
if (adap->algo->master_xfer) {
|
||||
struct i2c_msg msg;
|
||||
u8 msgbuf[] = {pdata->sel_reg_addr, val};
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = 0;
|
||||
msg.len = 2;
|
||||
msg.buf = msgbuf;
|
||||
return __i2c_transfer(adap, &msg, 1);
|
||||
} else if (adap->algo->smbus_xfer) {
|
||||
union i2c_smbus_data data;
|
||||
|
||||
data.byte = val;
|
||||
return adap->algo->smbus_xfer(adap, client->addr,
|
||||
client->flags, I2C_SMBUS_WRITE,
|
||||
pdata->sel_reg_addr,
|
||||
I2C_SMBUS_BYTE_DATA, &data);
|
||||
} else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int mlxcpld_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct mlxcpld_mux *data = i2c_mux_priv(muxc);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 regval = chan + 1;
|
||||
int err = 0;
|
||||
|
||||
/* Only select the channel if its different from the last channel */
|
||||
if (data->last_chan != regval) {
|
||||
err = mlxcpld_mux_reg_write(muxc->parent, client, regval);
|
||||
if (err)
|
||||
data->last_chan = 0;
|
||||
else
|
||||
data->last_chan = regval;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxcpld_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct mlxcpld_mux *data = i2c_mux_priv(muxc);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
/* Deselect active channel */
|
||||
data->last_chan = 0;
|
||||
|
||||
return mlxcpld_mux_reg_write(muxc->parent, client, data->last_chan);
|
||||
}
|
||||
|
||||
/* Probe/reomove functions */
|
||||
static int mlxcpld_mux_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
|
||||
struct mlxcpld_mux_plat_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct i2c_mux_core *muxc;
|
||||
int num, force;
|
||||
struct mlxcpld_mux *data;
|
||||
int err;
|
||||
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
muxc = i2c_mux_alloc(adap, &client->dev, CPLD_MUX_MAX_NCHANS,
|
||||
sizeof(*data), 0, mlxcpld_mux_select_chan,
|
||||
mlxcpld_mux_deselect);
|
||||
if (!muxc)
|
||||
return -ENOMEM;
|
||||
|
||||
data = i2c_mux_priv(muxc);
|
||||
i2c_set_clientdata(client, muxc);
|
||||
data->client = client;
|
||||
data->last_chan = 0; /* force the first selection */
|
||||
|
||||
/* Create an adapter for each channel. */
|
||||
for (num = 0; num < CPLD_MUX_MAX_NCHANS; num++) {
|
||||
if (num >= pdata->num_adaps)
|
||||
/* discard unconfigured channels */
|
||||
break;
|
||||
|
||||
force = pdata->adap_ids[num];
|
||||
|
||||
err = i2c_mux_add_adapter(muxc, force, num, 0);
|
||||
if (err)
|
||||
goto virt_reg_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
virt_reg_failed:
|
||||
i2c_mux_del_adapters(muxc);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxcpld_mux_remove(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||
|
||||
i2c_mux_del_adapters(muxc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver mlxcpld_mux_driver = {
|
||||
.driver = {
|
||||
.name = "mlxcpld-mux",
|
||||
},
|
||||
.probe = mlxcpld_mux_probe,
|
||||
.remove = mlxcpld_mux_remove,
|
||||
.id_table = mlxcpld_mux_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mlxcpld_mux_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Shych (michaels@mellanox.com)");
|
||||
MODULE_DESCRIPTION("Mellanox I2C-CPLD-MUX driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_ALIAS("platform:i2c-mux-mlxcpld");
|
|
@ -35,6 +35,7 @@
|
|||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
|
@ -120,6 +121,21 @@ static const struct i2c_device_id pca954x_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca954x_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id pca954x_acpi_ids[] = {
|
||||
{ .id = "PCA9540", .driver_data = pca_9540 },
|
||||
{ .id = "PCA9542", .driver_data = pca_9540 },
|
||||
{ .id = "PCA9543", .driver_data = pca_9543 },
|
||||
{ .id = "PCA9544", .driver_data = pca_9544 },
|
||||
{ .id = "PCA9545", .driver_data = pca_9545 },
|
||||
{ .id = "PCA9546", .driver_data = pca_9545 },
|
||||
{ .id = "PCA9547", .driver_data = pca_9547 },
|
||||
{ .id = "PCA9548", .driver_data = pca_9548 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pca954x_acpi_ids);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pca954x_of_match[] = {
|
||||
{ .compatible = "nxp,pca9540", .data = &chips[pca_9540] },
|
||||
|
@ -245,8 +261,17 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
|
||||
if (match)
|
||||
data->chip = of_device_get_match_data(&client->dev);
|
||||
else
|
||||
else if (id)
|
||||
data->chip = &chips[id->driver_data];
|
||||
else {
|
||||
const struct acpi_device_id *acpi_id;
|
||||
|
||||
acpi_id = acpi_match_device(ACPI_PTR(pca954x_acpi_ids),
|
||||
&client->dev);
|
||||
if (!acpi_id)
|
||||
return -ENODEV;
|
||||
data->chip = &chips[acpi_id->driver_data];
|
||||
}
|
||||
|
||||
data->last_chan = 0; /* force the first selection */
|
||||
|
||||
|
@ -321,6 +346,7 @@ static struct i2c_driver pca954x_driver = {
|
|||
.name = "pca954x",
|
||||
.pm = &pca954x_pm,
|
||||
.of_match_table = of_match_ptr(pca954x_of_match),
|
||||
.acpi_match_table = ACPI_PTR(pca954x_acpi_ids),
|
||||
},
|
||||
.probe = pca954x_probe,
|
||||
.remove = pca954x_remove,
|
||||
|
|
|
@ -1132,8 +1132,7 @@ static int pm860x_dt_init(struct device_node *np,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pm860x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int pm860x_probe(struct i2c_client *client)
|
||||
{
|
||||
struct pm860x_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct device_node *node = client->dev.of_node;
|
||||
|
@ -1259,7 +1258,7 @@ static struct i2c_driver pm860x_driver = {
|
|||
.pm = &pm860x_pm_ops,
|
||||
.of_match_table = pm860x_dt_ids,
|
||||
},
|
||||
.probe = pm860x_probe,
|
||||
.probe_new = pm860x_probe,
|
||||
.remove = pm860x_remove,
|
||||
.id_table = pm860x_id_table,
|
||||
};
|
||||
|
|
|
@ -50,31 +50,4 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
|
|||
struct i2c_smbus_alert_setup *setup);
|
||||
int i2c_handle_smbus_alert(struct i2c_client *ara);
|
||||
|
||||
/**
|
||||
* smbus_host_notify - internal structure used by the Host Notify mechanism.
|
||||
* @adapter: the I2C adapter associated with this struct
|
||||
* @work: worker used to schedule the IRQ in the slave device
|
||||
* @lock: spinlock to check if a notification is already pending
|
||||
* @pending: flag set when a notification is pending (any new notification will
|
||||
* be rejected if pending is true)
|
||||
* @payload: the actual payload of the Host Notify event
|
||||
* @addr: the address of the slave device which raised the notification
|
||||
*
|
||||
* This struct needs to be allocated by i2c_setup_smbus_host_notify() and does
|
||||
* not need to be freed. Internally, i2c_setup_smbus_host_notify() uses a
|
||||
* managed resource to clean this up when the adapter get released.
|
||||
*/
|
||||
struct smbus_host_notify {
|
||||
struct i2c_adapter *adapter;
|
||||
struct work_struct work;
|
||||
spinlock_t lock;
|
||||
bool pending;
|
||||
u16 payload;
|
||||
u8 addr;
|
||||
};
|
||||
|
||||
struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap);
|
||||
int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
|
||||
unsigned short addr, unsigned int data);
|
||||
|
||||
#endif /* _LINUX_I2C_SMBUS_H */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/device.h> /* for struct device */
|
||||
#include <linux/sched.h> /* for completion */
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/irqdomain.h> /* for Host Notify IRQ */
|
||||
#include <linux/of.h> /* for struct device_node */
|
||||
#include <linux/swab.h> /* for swab16 */
|
||||
#include <uapi/linux/i2c.h>
|
||||
|
@ -135,7 +136,8 @@ enum i2c_alert_protocol {
|
|||
* struct i2c_driver - represent an I2C device driver
|
||||
* @class: What kind of i2c device we instantiate (for detect)
|
||||
* @attach_adapter: Callback for bus addition (deprecated)
|
||||
* @probe: Callback for device binding
|
||||
* @probe: Callback for device binding - soon to be deprecated
|
||||
* @probe_new: New callback for device binding
|
||||
* @remove: Callback for device unbinding
|
||||
* @shutdown: Callback for device shutdown
|
||||
* @alert: Alert callback, for example for the SMBus alert protocol
|
||||
|
@ -178,6 +180,11 @@ struct i2c_driver {
|
|||
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
|
||||
int (*remove)(struct i2c_client *);
|
||||
|
||||
/* New driver model interface to aid the seamless removal of the
|
||||
* current probe()'s, more commonly unused than used second parameter.
|
||||
*/
|
||||
int (*probe_new)(struct i2c_client *);
|
||||
|
||||
/* driver model interfaces that don't relate to enumeration */
|
||||
void (*shutdown)(struct i2c_client *);
|
||||
|
||||
|
@ -243,6 +250,8 @@ struct i2c_client {
|
|||
|
||||
extern struct i2c_client *i2c_verify_client(struct device *dev);
|
||||
extern struct i2c_adapter *i2c_verify_adapter(struct device *dev);
|
||||
extern const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||
const struct i2c_client *client);
|
||||
|
||||
static inline struct i2c_client *kobj_to_i2c_client(struct kobject *kobj)
|
||||
{
|
||||
|
@ -567,6 +576,8 @@ struct i2c_adapter {
|
|||
|
||||
struct i2c_bus_recovery_info *bus_recovery_info;
|
||||
const struct i2c_adapter_quirks *quirks;
|
||||
|
||||
struct irq_domain *host_notify_domain;
|
||||
};
|
||||
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
|
||||
|
||||
|
@ -739,6 +750,7 @@ static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
|
|||
return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
|
||||
}
|
||||
|
||||
int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
|
||||
/**
|
||||
* module_i2c_driver() - Helper macro for registering a modular I2C driver
|
||||
* @__i2c_driver: i2c_driver struct
|
||||
|
@ -774,6 +786,10 @@ extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
|
|||
/* must call i2c_put_adapter() when done with returned i2c_adapter device */
|
||||
struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node);
|
||||
|
||||
extern const struct of_device_id
|
||||
*i2c_of_match_device(const struct of_device_id *matches,
|
||||
struct i2c_client *client);
|
||||
|
||||
#else
|
||||
|
||||
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
|
||||
|
@ -790,6 +806,14 @@ static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline const struct of_device_id
|
||||
*i2c_of_match_device(const struct of_device_id *matches,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* mlxcpld.h - Mellanox I2C multiplexer support in CPLD
|
||||
*
|
||||
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2016 Michael Shych <michaels@mellanox.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_MLXCPLD_H
|
||||
#define _LINUX_I2C_MLXCPLD_H
|
||||
|
||||
/* Platform data for the CPLD I2C multiplexers */
|
||||
|
||||
/* mlxcpld_mux_plat_data - per mux data, used with i2c_register_board_info
|
||||
* @adap_ids - adapter array
|
||||
* @num_adaps - number of adapters
|
||||
* @sel_reg_addr - mux select register offset in CPLD space
|
||||
*/
|
||||
struct mlxcpld_mux_plat_data {
|
||||
int *adap_ids;
|
||||
int num_adaps;
|
||||
int sel_reg_addr;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_I2C_MLXCPLD_H */
|
Loading…
Reference in New Issue