summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDong Aisheng <aisheng.dong@nxp.com>2021-11-30 15:00:22 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2021-11-30 15:00:22 +0800
commit673a8f5ea2d6b9bc4e95fe5c7bf07eb5feb739cf (patch)
treee4c933420e9d70d29c187f0615467722b892e56f
parent853a3c8e3bbae9d7011e4591996d667a931eb68d (diff)
parent9a637d4fedff641a414995c53b49b0781dbcd021 (diff)
Merge branch 'i2c/next' into next
* i2c/next: (35 commits) MLK-25762 i2c: imx-lpi2c: change log level for eDMA related logs LF-4133-1 i2c: imx: flexio: Add FlexIO i2c master support LF-4475 i2c: imx: request dma before registering i2c bus devices LF-4174 i2c: imx-lpi2c: fix runtime pm issue when defer probe occurs Revert "i2c: slave: support I2C_SLAVE_STOP event for the read transactions" ...
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-rpmsg-imx.txt29
-rw-r--r--drivers/i2c/busses/Kconfig14
-rw-r--r--drivers/i2c/busses/Makefile2
-rw-r--r--drivers/i2c/busses/i2c-flexio.c994
-rw-r--r--drivers/i2c/busses/i2c-imx-lpi2c.c528
-rw-r--r--drivers/i2c/busses/i2c-imx.c330
-rw-r--r--drivers/i2c/busses/i2c-rpmsg-imx.c462
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c8
8 files changed, 2281 insertions, 86 deletions
diff --git a/Documentation/devicetree/bindings/i2c/i2c-rpmsg-imx.txt b/Documentation/devicetree/bindings/i2c/i2c-rpmsg-imx.txt
new file mode 100644
index 000000000000..fce660d0f179
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-rpmsg-imx.txt
@@ -0,0 +1,29 @@
+* Freescale Virtual I2C RPMSG bus driver for i.MX
+
+Required properties:
+- compatible :
+ - "fsl,i2c-rpbus" for I2C bus over RPMSG compatible on i.MX8QXP/QM soc
+The i2c-rpbus node should define its bus id (which is the node communicating
+with M4) in alias.
+
+Examples:
+
+aliases {
+ ...
+ i2c1 = &i2c_rpbus_1;
+ ...
+};
+
+&i2c_rpbus_1 {
+ compatible = "fsl,i2c-rpbus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ devs_in_this_i2c_bus__for_example: pca6416@20 {
+ compatible = "ti,tca6416";
+ reg = <0x20>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e17790fe35a7..07f87ac17830 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -696,6 +696,14 @@ config I2C_IMX_LPI2C
This driver can also be built as a module. If so, the module
will be called i2c-imx-lpi2c.
+config I2C_IMX_FLEXIO
+ tristate "NXP IMX FLEXIO I2C MASTER"
+ depends on ARCH_MXC || COMPILE_TEST
+ default y
+ help
+ If you say yes to this option, support will be included for the
+ I2C controller simulated by flexio embedded in NXP IMX8ULP SOCs.
+
config I2C_IOP3XX
tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
depends on ARCH_IOP32X || ARCH_IXP4XX || COMPILE_TEST
@@ -949,6 +957,12 @@ config I2C_RK3X
This driver can also be built as a module. If so, the module will
be called i2c-rk3x.
+config I2C_RPBUS
+ tristate "I2C proxy bus over RPMSG"
+ depends on I2C && RPMSG
+ help
+ This driver can support virtual i2c-rpmsg function.
+
config HAVE_S3C2410_I2C
bool
help
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 1336b04f40e2..1fb52010700b 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -69,6 +69,7 @@ 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_IMX_FLEXIO) += i2c-flexio.o
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
@@ -126,6 +127,7 @@ obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
+obj-$(CONFIG_I2C_RPBUS) += i2c-rpmsg-imx.o
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
diff --git a/drivers/i2c/busses/i2c-flexio.c b/drivers/i2c/busses/i2c-flexio.c
new file mode 100644
index 000000000000..4d80417eb731
--- /dev/null
+++ b/drivers/i2c/busses/i2c-flexio.c
@@ -0,0 +1,994 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * flexio i2c master driver
+ *
+ * Copyright 2021 NXP
+ *
+ * Author: Alice Guo <alice.guo@nxp.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/iopoll.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#define CTRL 0x8
+#define FLEXIO_CTRL_DOZEN_MASK 0x80000000
+#define FLEXIO_CTRL_DOZEN(x) (((u32)(((u32)(x)) << 31)) & 0x80000000)
+#define FLEXIO_CTRL_DBGE_MASK 0x40000000
+#define FLEXIO_CTRL_DBGE(x) (((u32)(((u32)(x)) << 30)) & 0x40000000)
+#define FLEXIO_CTRL_FASTACC_MASK 0x4
+#define FLEXIO_CTRL_FASTACC(x) (((u32)(((u32)(x)) << 2)) & 0x4)
+#define FLEXIO_CTRL_SWRST_MASK 0x2
+#define FLEXIO_CTRL_SWRST(x) (((u32)(((u32)(x)) << 1)) & 0x2)
+#define FLEXIO_CTRL_FLEXEN_MASK 0x1
+#define FLEXIO_CTRL_FLEXEN(x) (((u32)(((u32)(x)) << 0)) & 0x1)
+
+#define PIN 0xc
+#define SHIFTSTAT 0x10
+#define SHIFTERR 0x14
+#define TIMSTAT 0x18
+#define SHIFTSIEN 0x20
+#define SHIFTEIEN 0x24
+#define PINSTAT 0x50
+#define PINREN 0x58
+
+#define SHIFTCTL_0 0x80
+#define SHIFTCTL_1 0x84
+#define SHIFTCTL_TIMSEL_MASK 0x7000000
+#define SHIFTCTL_TIMSEL(x) (((u32)(((u32)(x)) << 24)) & 0x7000000)
+#define SHIFTCTL_TIMPOL_MASK 0x800000
+#define SHIFTCTL_TIMPOL(x) (((u32)(((u32)(x)) << 23)) & 0x800000)
+#define SHIFTCTL_PINCFG_MASK 0x30000
+#define SHIFTCTL_PINCFG(x) (((u32)(((u32)(x)) << 16)) & 0x30000)
+#define SHIFTCTL_PINSEL_MASK 0x1f00
+#define SHIFTCTL_PINSEL(x) (((u32)(((u32)(x)) << 8)) & 0x1f00)
+#define SHIFTCTL_PINPOL_MASK 0x80
+#define SHIFTCTL_PINPOL(x) (((u32)(((u32)(x)) << 7)) & 0x80)
+#define SHIFTCTL_SMOD_MASK 0x7
+#define SHIFTCTL_SMOD(x) (((u32)(((u32)(x)) << 0)) & 0x7)
+#define SHIFT_ON_POSEDGE 0x0
+#define SHIFT_ON_NEGEDGE 0x1
+#define SHIFTER_PIN_OUTPUT_DISABLE 0x0
+#define SHIFTER_PIN_OPEN_DRAIN_OUTPUT 0x1
+#define PIN_ACTIVE_HIGH 0x0
+#define PIN_ACTIVE_LOW 0x1
+#define SHIFTER_DISABLE 0x0
+#define SHIFTER_RECEIVE 0x1
+#define SHIFTER_TRANSMIT 0x2
+
+#define SHIFTCFG_0 0x100
+#define SHIFTCFG_1 0x104
+#define SHIFTCFG_INSRC_MASK 0x100
+#define SHIFTCFG_INSRC(x) (((u32)(((u32)(x)) << 8)) & 0x100)
+#define SHIFTCFG_SSTOP_MASK 0x30
+#define SHIFTCFG_SSTOP(x) (((u32)(((u32)(x)) << 4)) & 0x30)
+#define SHIFTCFG_SSTART_MASK 0x3
+#define SHIFTCFG_SSTART(x) (((u32)(((u32)(x)) << 0)) & 0x3)
+#define INPUT_SRC_PIN 0x0
+#define SSTOP_BIT_LOW 0x2
+#define SSTOP_BIT_HIGH 0x3
+#define SSTART_BIT_DISABLE 0X0
+#define SSTART_BIT_LOW 0x2
+
+#define SHIFTBUFBIS_1 0x284
+#define SHIFTBUFBBS_0 0x380
+
+#define TIMCTL_0 0x400
+#define TIMCTL_1 0x404
+#define TIMCTL_TRGSEL_MASK 0x3F000000
+#define TIMCTL_TRGSEL(x) (((u32)(((u32)(x)) << 24)) & 0x3F000000)
+#define TIMCTL_TRGPOL_MASK 0x800000
+#define TIMCTL_TRGPOL(x) (((u32)(((u32)(x)) << 23)) & 0x800000)
+#define TIMCTL_TRGSRC_MASK 0x400000
+#define TIMCTL_TRGSRC(x) (((u32)(((u32)(x)) << 22)) & 0x400000)
+#define TIMCTL_PINCFG_MASK 0x30000
+#define TIMCTL_PINCFG(x) (((u32)(((u32)(x)) << 16)) & 0x30000)
+#define TIMCTL_PINSEL_MASK 0x1f00
+#define TIMCTL_PINSEL(x) (((u32)(((u32)(x)) << 8)) & 0x1f00)
+#define TIMCTL_PINPOL_MASK 0x80
+#define TIMCTL_PINPOL(x) (((u32)(((u32)(x)) << 7)) & 0x80)
+#define TIMCTL_TIMOD_MASK 0x7
+#define TIMCTL_TIMOD(x) (((u32)(((u32)(x)) << 0)) & 0x7)
+#define TIMER_TRGSEL_SHIFTER(x) (((u32)(x) << 2U) | 0x1U)
+#define TIMER_TRG_ACTIVE_LOW 0x1
+#define TIMER_TRGSRC_INTER 0x1
+#define TIMPIN_OUTPUT_DISABLE 0x0
+#define TIMPIN_OPEN_DRAIN_OUTPUT 0x1
+#define TIMPIN_ACTIVE_HIGH 0x0
+#define TIMPIN_ACTIVE_LOW 0x1
+#define TIMER_DISABLE 0x0
+#define DUAL_8BIT_COUNTERS_BAUD 0x1
+#define SINGLE_16BIT_COUNTER 0x3
+
+#define TIMCFG_0 0x480
+#define TIMCFG_1 0x484
+#define TIMCFG_TIMOUT_MASK 0x3000000
+#define TIMCFG_TIMOUT(x) (((u32)(((u32)(x)) << 24)) & 0x3000000)
+#define TIMCFG_TIMDEC_MASK 0x700000
+#define TIMCFG_TIMDEC(x) (((u32)(((u32)(x)) << 20)) & 0x700000)
+#define TIMCFG_TIMRST_MASK 0x70000
+#define TIMCFG_TIMRST(x) (((u32)(((u32)(x)) << 16)) & 0x70000)
+#define TIMCFG_TIMDIS_MASK 0x7000
+#define TIMCFG_TIMDIS(x) (((u32)(((u32)(x)) << 12)) & 0x7000)
+#define TIMCFG_TIMENA_MASK 0x700
+#define TIMCFG_TIMENA(x) (((u32)(((u32)(x)) << 8)) & 0x700)
+#define TIMCFG_TSTOP_MASK 0x30
+#define TIMCFG_TSTOP(x) (((u32)(((u32)(x)) << 4)) & 0x30)
+#define TIMCFG_TSTART_MASK 0x2
+#define TIMCFG_TSTART(x) (((u32)(((u32)(x)) << 1)) & 0x2)
+#define TIMOUT_ONE_NOTAFFECT_BY_RESET 0x0
+#define TIMOUT_ZERO_NOTAFFECT_BY_RESET 0x1
+#define TIMDEC_FLEXIO_CLK 0x0
+#define TIMDEC_PIN_INPUT 0x2
+#define TIMRST_NEVER 0x0
+#define TIMRST_TIMPIN_EQUAL_TIMOUTPUT 0x2
+#define TIMDIS_TIMER_DISABLE 0x1
+#define TIMDIS_TIMER_COMPARE 0x2
+#define TIMDIS_PIN_EDGE 0x4
+#define TIMENA_PREV_TIMENA 0X1
+#define TIMENA_TRG_HIGH 0x2
+#define TSTOP_BIT_DISABLE 0x0
+#define TSTOP_BIT_ENABLE_TIMCMP 0x1
+#define TSTOP_BIT_ENABLE_TIMDIS 0x2
+#define TSTART_BIT_DISABLE 0x0
+#define TSTART_BIT_ENABLE 0x1
+
+#define TIMCMP_0 0x500
+#define TIMCMP_1 0x504
+
+#define TRANSMIT_STAT 0x1
+#define RECEIVE_STAT 0x2
+#define XFER_TIMEOUT 10000
+
+struct flexio_control {
+ bool dozen;
+ bool dbge;
+ bool fastacc;
+ bool swrst;
+ bool flexen;
+};
+
+struct flexio_shifter_control {
+ u8 timsel;
+ u8 timpol;
+ u8 pincfg;
+ u8 pinsel;
+ u8 pinpol;
+ u8 smod;
+};
+
+struct flexio_shifter_config {
+ u8 pwidth;
+ u8 sszie;
+ u8 latst;
+ u8 insrc;
+ u8 sstop;
+ u8 sstart;
+};
+
+struct flexio_timer_control {
+ u8 trgsel;
+ u8 trgpol;
+ u8 trgsrc;
+ u8 pincfg;
+ u8 pinsel;
+ u8 pinpol;
+ u8 timod;
+};
+
+struct flexio_timer_config {
+ u8 timout;
+ u8 timdec;
+ u8 timrst;
+ u8 timdis;
+ u8 timena;
+ u8 tstop;
+ u8 tstart;
+};
+
+static void flexio_writel(void *base, u32 val, unsigned int reg)
+{
+ writel_relaxed(val, base + reg);
+}
+
+static u32 flexio_readl(void *base, unsigned int reg)
+{
+ return readl_relaxed(base + reg);
+}
+
+static void flexio_sw_reset(void *base, unsigned int reg)
+{
+ u32 val = 0;
+
+ val = flexio_readl(base, reg) | FLEXIO_CTRL_SWRST_MASK;
+ flexio_writel(base, val, reg);
+
+ flexio_writel(base, 0, reg);
+}
+
+static void flexio_get_default_ctrl(struct flexio_control *ctrl)
+{
+ memset(ctrl, 0, sizeof(*ctrl));
+
+ ctrl->dozen = false;
+ ctrl->dbge = true;
+ ctrl->fastacc = false;
+ ctrl->flexen = true;
+}
+
+static void flexio_setup_ctrl(void *base, struct flexio_control *ctrl,
+ unsigned int reg)
+{
+ u32 val = 0;
+
+ val &= ~(FLEXIO_CTRL_DOZEN_MASK | FLEXIO_CTRL_DBGE_MASK |
+ FLEXIO_CTRL_FASTACC_MASK | FLEXIO_CTRL_FLEXEN_MASK);
+ val |= (FLEXIO_CTRL_DBGE(ctrl->dbge) |
+ FLEXIO_CTRL_FASTACC(ctrl->fastacc) |
+ FLEXIO_CTRL_FLEXEN(ctrl->flexen));
+
+ if (!ctrl->dozen)
+ val |= FLEXIO_CTRL_DOZEN_MASK;
+
+ flexio_writel(base, val, reg);
+}
+
+static void flexio_setup_shiftctl(void *base, struct flexio_shifter_control *ctl,
+ unsigned int reg)
+{
+ u32 val = 0;
+
+ val = SHIFTCTL_TIMSEL(ctl->timsel) | SHIFTCTL_TIMPOL(ctl->timpol) |
+ SHIFTCTL_PINCFG(ctl->pincfg) | SHIFTCTL_PINSEL(ctl->pinsel) |
+ SHIFTCTL_PINPOL(ctl->pinpol) | SHIFTCTL_SMOD(ctl->smod);
+
+ flexio_writel(base, val, reg);
+}
+
+static void flexio_setup_shiftcfg(void *base, struct flexio_shifter_config *cfg,
+ unsigned int reg)
+{
+ u32 val = 0;
+
+ val = SHIFTCFG_INSRC(cfg->insrc) | SHIFTCFG_SSTOP(cfg->sstop) |
+ SHIFTCFG_SSTART(cfg->sstart);
+
+ flexio_writel(base, val, reg);
+}
+
+static void flexio_setup_timerctl(void *base, struct flexio_timer_control *ctl,
+ unsigned int reg)
+{
+ u32 val = 0;
+
+ val = TIMCTL_TRGSEL(ctl->trgsel) | TIMCTL_TRGPOL(ctl->trgpol) |
+ TIMCTL_TRGSRC(ctl->trgsrc) | TIMCTL_PINCFG(ctl->pincfg) |
+ TIMCTL_PINSEL(ctl->pinsel) | TIMCTL_PINPOL(ctl->pinpol) |
+ TIMCTL_TIMOD(ctl->timod);
+
+ flexio_writel(base, val, reg);
+}
+
+static void flexio_setup_timercfg(void *base, struct flexio_timer_config *cfg,
+ unsigned int reg)
+{
+ u32 val = 0;
+
+ val = TIMCFG_TIMOUT(cfg->timout) | TIMCFG_TIMDEC(cfg->timdec) |
+ TIMCFG_TIMRST(cfg->timrst) | TIMCFG_TIMDIS(cfg->timdis) |
+ TIMCFG_TIMENA(cfg->timena) | TIMCFG_TSTOP(cfg->tstop) |
+ TIMCFG_TSTART(cfg->tstart);
+
+ flexio_writel(base, val, reg);
+}
+
+enum transfer_state {
+ START_ADDRESS_WRITE,
+ WRITE_DATA,
+ WRITE_END,
+ START_ADDRESS_READ,
+ READ_NOT_LAST_DATA,
+ READ_LAST_DATA,
+ READ_END,
+};
+
+enum stop_position {
+ NOT_LAST_8_BIT,
+ LAST_8_BIT,
+};
+
+struct imx_flexio_i2c_master_dev {
+ void __iomem *base;
+
+ struct device *dev;
+ struct clk *clk;
+ struct i2c_adapter adapter;
+ struct completion complete;
+ enum transfer_state state;
+ enum stop_position stop;
+
+ spinlock_t lock;
+
+ unsigned int irq;
+ unsigned int baudrate;
+ unsigned int src_clock;
+ u8 shifters[2];
+ u8 timers[2];
+ u8 sda_pin;
+ u8 scl_pin;
+ u8 slave_addr;
+ u8 *data;
+ u16 len;
+
+ bool need_check_ack;
+ bool nack;
+ bool read;
+ bool repeated_start;
+};
+
+enum shifter_flags {
+ TX_EMPTY_F = BIT(0),
+ RX_FULL_F = BIT(1),
+ TX_ERR_F = BIT(2),
+ RX_NAK_F = BIT(3),
+};
+
+static void i2c_master_enable_ack(struct imx_flexio_i2c_master_dev *i2c_dev,
+ bool enable)
+{
+ void __iomem *base = i2c_dev->base;
+ u32 cfg;
+
+ cfg = flexio_readl(base, SHIFTCFG_0);
+ cfg &= ~SHIFTCFG_SSTOP_MASK;
+ if (enable)
+ cfg |= SHIFTCFG_SSTOP(SSTOP_BIT_LOW);
+ else
+ cfg |= SHIFTCFG_SSTOP(SSTOP_BIT_HIGH);
+
+ flexio_writel(base, cfg, SHIFTCFG_0);
+}
+
+static irqreturn_t imx_flexio_i2c_isr(int irq, void *dev_id)
+{
+ struct imx_flexio_i2c_master_dev *i2c_dev = dev_id;
+ void __iomem *base = i2c_dev->base;
+ void __iomem *pinstat = i2c_dev->base + PINSTAT;
+ u32 shiftstat, ack, val, ctl;
+
+ shiftstat = flexio_readl(base, SHIFTSTAT);
+
+ if (shiftstat & TRANSMIT_STAT) {
+ switch (i2c_dev->state) {
+ case START_ADDRESS_WRITE:
+ flexio_writel(base, i2c_dev->slave_addr, SHIFTBUFBBS_0);
+
+ if (i2c_dev->len > 0)
+ i2c_dev->state = WRITE_DATA;
+ else
+ i2c_dev->state = WRITE_END;
+ break;
+ case WRITE_DATA:
+ flexio_writel(base, *i2c_dev->data, SHIFTBUFBBS_0);
+ i2c_dev->data++;
+ i2c_dev->len--;
+
+ if (!i2c_dev->len)
+ i2c_dev->state = WRITE_END;
+ break;
+ case WRITE_END:
+ if (i2c_dev->repeated_start)
+ flexio_writel(base, 0xff, SHIFTBUFBBS_0);
+
+ i2c_dev->stop = LAST_8_BIT;
+ break;
+ case START_ADDRESS_READ:
+ flexio_writel(base, i2c_dev->slave_addr, SHIFTBUFBBS_0);
+
+ if (i2c_dev->len > 1)
+ i2c_dev->state = READ_NOT_LAST_DATA;
+ else if (i2c_dev->len == 1)
+ i2c_dev->state = READ_LAST_DATA;
+ else if (i2c_dev->len == 0)
+ i2c_dev->state = READ_END;
+ break;
+ case READ_NOT_LAST_DATA:
+ flexio_writel(base, 0xff, SHIFTBUFBBS_0);
+ break;
+ case READ_LAST_DATA:
+ i2c_master_enable_ack(i2c_dev, false);
+ flexio_writel(base, 0xff, SHIFTBUFBBS_0);
+ break;
+ case READ_END:
+ i2c_dev->stop = LAST_8_BIT;
+ default:
+ break;
+ }
+ }
+
+ if (shiftstat & RECEIVE_STAT) {
+ if (i2c_dev->need_check_ack) {
+ ack = flexio_readl(base, SHIFTERR) & 2;
+ if (!ack) {
+ if (i2c_dev->stop == LAST_8_BIT) {
+ flexio_writel(base, 0, SHIFTSIEN);
+ complete(&i2c_dev->complete);
+ }
+
+ if (i2c_dev->read) {
+ i2c_dev->need_check_ack = false;
+ i2c_dev->read = false;
+ flexio_readl(base, SHIFTBUFBIS_1);
+
+ switch (i2c_dev->state) {
+ case READ_NOT_LAST_DATA:
+ i2c_master_enable_ack(i2c_dev, true);
+ break;
+ case READ_LAST_DATA:
+ i2c_master_enable_ack(i2c_dev, false);
+ i2c_dev->state = READ_END;
+ break;
+ case READ_END:
+ default:
+ flexio_writel(base, 0, SHIFTSIEN);
+ complete(&i2c_dev->complete);
+ break;
+ }
+ }
+ } else {
+ flexio_writel(base, 0, SHIFTSIEN);
+ i2c_dev->nack = true;
+
+ switch (i2c_dev->stop) {
+ case NOT_LAST_8_BIT:
+ goto stop;
+ case LAST_8_BIT:
+ flexio_writel(base, 0, SHIFTBUFBBS_0);
+ complete(&i2c_dev->complete);
+ break;
+ default:
+ complete(&i2c_dev->complete);
+ break;
+ }
+ }
+
+ flexio_readl(base, SHIFTBUFBIS_1);
+ } else {
+ *i2c_dev->data = flexio_readl(base, SHIFTBUFBIS_1);
+ i2c_dev->data++;
+ i2c_dev->len--;
+
+ if (i2c_dev->len == 1)
+ i2c_dev->state = READ_LAST_DATA;
+ else if (i2c_dev->len == 0) {
+ flexio_writel(base, 0, SHIFTBUFBBS_0);
+
+ flexio_writel(base, 0, SHIFTSIEN);
+ complete(&i2c_dev->complete);
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+
+stop:
+ /* generate STOP */
+ flexio_writel(base, 0, SHIFTBUFBBS_0);
+
+ /*
+ * Software should then wait for the next rising edge on SCL and then
+ * disable both timers.
+ */
+ flexio_writel(base, 1 << i2c_dev->scl_pin, PINREN);
+ readl_relaxed_poll_timeout(pinstat, val, val & (1 << i2c_dev->scl_pin), 0, 1000);
+
+ ctl = flexio_readl(base, TIMCTL_0);
+ ctl &= ~TIMCTL_TIMOD_MASK;
+ ctl |= TIMCTL_TIMOD(TIMER_DISABLE);
+ flexio_writel(base, ctl, TIMCTL_0);
+
+ ctl = flexio_readl(base, TIMCTL_1);
+ ctl &= ~TIMCTL_TIMOD_MASK;
+ ctl |= TIMCTL_TIMOD(TIMER_DISABLE);
+ flexio_writel(base, ctl, TIMCTL_1);
+
+ /*
+ * The transmit shifter should then be disabled after waiting the setup
+ * delay for a repeated START or STOP condition.
+ */
+ udelay(10);
+ ctl = flexio_readl(base, SHIFTCTL_0);
+ ctl &= ~SHIFTCTL_PINCFG_MASK;
+ ctl |= SHIFTCTL_PINCFG(SHIFTER_PIN_OUTPUT_DISABLE);
+ ctl &= ~SHIFTCTL_SMOD_MASK;
+ ctl |= SHIFTCTL_SMOD(SHIFTER_DISABLE);
+ flexio_writel(base, ctl, SHIFTCTL_0);
+
+ flexio_writel(base, 0, PINREN);
+ flexio_writel(base, 1 << i2c_dev->scl_pin, PINSTAT);
+
+ complete(&i2c_dev->complete);
+
+ return IRQ_HANDLED;
+}
+
+static void clear_shifter_flags(struct imx_flexio_i2c_master_dev *i2c_dev,
+ enum shifter_flags flags)
+{
+ void __iomem *base = i2c_dev->base;
+ u32 v = 1;
+ u8 tx = i2c_dev->shifters[0], rx = i2c_dev->shifters[1];
+
+ /*
+ * For SMOD = Transmit, the status flag is set when SHIFTBUF is empty or
+ * when initially configured for SMOD=Transmit.
+ * For SMOD = Receive, the status flag is set when SHIFTBUF is full, and
+ * the shifter error flag is set when the received start or stop bit
+ * does not match the expected value.
+ */
+ if (flags & TX_EMPTY_F)
+ flexio_writel(base, v << tx, SHIFTSTAT);
+ if (flags & RX_FULL_F)
+ flexio_writel(base, v << rx, SHIFTSTAT);
+ if (flags & TX_ERR_F)
+ flexio_writel(base, v << tx, SHIFTERR);
+ if (flags & RX_NAK_F)
+ flexio_writel(base, v << rx, SHIFTERR);
+}
+
+static void imx_flexio_init_hardware(struct imx_flexio_i2c_master_dev *i2c_dev)
+{
+ void __iomem *base = i2c_dev->base;
+ struct flexio_control ctrl;
+ struct flexio_shifter_control shiftctl;
+ struct flexio_shifter_config shiftcfg;
+ struct flexio_timer_control timerctl;
+ struct flexio_timer_config timercfg;
+ u32 timercmp;
+
+ flexio_sw_reset(base, CTRL);
+
+ /* configure the shifter 0 as transmitter */
+ shiftcfg.insrc = INPUT_SRC_PIN;
+ shiftcfg.sstop = SSTOP_BIT_HIGH;
+ shiftcfg.sstart = SSTART_BIT_LOW;
+ flexio_setup_shiftcfg(base, &shiftcfg, SHIFTCFG_0);
+
+ shiftctl.timsel = i2c_dev->timers[1];
+ shiftctl.timpol = SHIFT_ON_POSEDGE;
+ shiftctl.pincfg = SHIFTER_PIN_OPEN_DRAIN_OUTPUT;
+ shiftctl.pinsel = i2c_dev->sda_pin;
+ shiftctl.pinpol = PIN_ACTIVE_LOW;
+ shiftctl.smod = SHIFTER_TRANSMIT;
+ flexio_setup_shiftctl(base, &shiftctl, SHIFTCTL_0);
+
+ /* configure the shifter 1 as receiver */
+ shiftcfg.insrc = INPUT_SRC_PIN;
+ shiftcfg.sstop = SSTOP_BIT_LOW;
+ shiftcfg.sstart = SSTART_BIT_DISABLE;
+ flexio_setup_shiftcfg(base, &shiftcfg, SHIFTCFG_1);
+
+ shiftctl.timsel = i2c_dev->timers[1];
+ shiftctl.timpol = SHIFT_ON_NEGEDGE;
+ shiftctl.pincfg = SHIFTER_PIN_OUTPUT_DISABLE;
+ shiftctl.pinsel = i2c_dev->sda_pin;
+ shiftctl.pinpol = PIN_ACTIVE_HIGH;
+ shiftctl.smod = SHIFTER_RECEIVE;
+ flexio_setup_shiftctl(base, &shiftctl, SHIFTCTL_1);
+
+ /* configure the timer 0 for the SCL and to trigger the timer 1 */
+ timercfg.timout = TIMOUT_ZERO_NOTAFFECT_BY_RESET;
+ timercfg.timdec = TIMDEC_FLEXIO_CLK;
+ timercfg.timrst = TIMRST_TIMPIN_EQUAL_TIMOUTPUT;
+ timercfg.timdis = TIMDIS_TIMER_COMPARE;
+ timercfg.timena = TIMENA_TRG_HIGH;
+ timercfg.tstop = TSTOP_BIT_ENABLE_TIMDIS;
+ timercfg.tstart = TSTART_BIT_ENABLE;
+ flexio_setup_timercfg(base, &timercfg, TIMCFG_0);
+
+ /* the baud rate divider equal to (CMP[7:0] + 1) * 2 */
+ timercmp = (u32)(i2c_dev->src_clock / i2c_dev->baudrate) / 2 - 1;
+ flexio_writel(base, timercmp, TIMCMP_0);
+
+ timerctl.trgsel = TIMER_TRGSEL_SHIFTER(i2c_dev->shifters[0]);
+ timerctl.trgpol = TIMER_TRG_ACTIVE_LOW;
+ timerctl.trgsrc = TIMER_TRGSRC_INTER;
+ timerctl.pincfg = TIMPIN_OPEN_DRAIN_OUTPUT;
+ timerctl.pinsel = i2c_dev->scl_pin;
+ timerctl.pinpol = TIMPIN_ACTIVE_HIGH;
+ timerctl.timod = DUAL_8BIT_COUNTERS_BAUD;
+ flexio_setup_timerctl(base, &timerctl, TIMCTL_0);
+
+ /* configure the timer 1 to control shifters */
+ timercfg.timout = TIMOUT_ONE_NOTAFFECT_BY_RESET;
+ timercfg.timdec = TIMDEC_PIN_INPUT;
+ timercfg.timrst = TIMRST_NEVER;
+ timercfg.timdis = TIMDIS_TIMER_DISABLE;
+ timercfg.timena = TIMENA_PREV_TIMENA;
+ timercfg.tstop = TSTOP_BIT_ENABLE_TIMCMP;
+ timercfg.tstart = TSTART_BIT_ENABLE;
+ flexio_setup_timercfg(base, &timercfg, TIMCFG_1);
+
+ /* the number of bits in each word equal to (CMP[15:0] + 1) / 2 */
+ timercmp = 8 * 2 - 1;
+ flexio_writel(i2c_dev->base, timercmp, TIMCMP_1);
+
+ timerctl.trgsel = TIMER_TRGSEL_SHIFTER(i2c_dev->shifters[0]);
+ timerctl.trgpol = TIMER_TRG_ACTIVE_LOW;
+ timerctl.trgsrc = TIMER_TRGSRC_INTER;
+ timerctl.pincfg = TIMPIN_OUTPUT_DISABLE;
+ timerctl.pinsel = i2c_dev->scl_pin;
+ timerctl.pinpol = TIMPIN_ACTIVE_LOW;
+ timerctl.timod = SINGLE_16BIT_COUNTER;
+ flexio_setup_timerctl(base, &timerctl, TIMCTL_1);
+
+ /* disable the shifter status interrupt and shifter error interrupt */
+ flexio_writel(base, 0, SHIFTSIEN);
+ flexio_writel(base, 0, SHIFTEIEN);
+
+ flexio_get_default_ctrl(&ctrl);
+ flexio_setup_ctrl(base, &ctrl, CTRL);
+}
+
+static int i2c_bus_busy(struct imx_flexio_i2c_master_dev *i2c_dev)
+{
+ unsigned int i, mask;
+ void __iomem *base = i2c_dev->base;
+
+ /*
+ * If in certain loops the SDA/SCL is continuously pulled down, then
+ * return bus busy status.
+ */
+ for (i = 0; i < 100; i++) {
+ mask = 1 << i2c_dev->sda_pin | 1 << i2c_dev->scl_pin;
+
+ if ((flexio_readl(base, PIN) & mask) == mask)
+ return 0;
+ }
+
+ return -EBUSY;
+}
+
+static int setup_xfer_count(struct imx_flexio_i2c_master_dev *i2c_dev,
+ u32 xfer_len)
+{
+ void __iomem *base = i2c_dev->base;
+ u32 cmp, cfg;
+
+ if (xfer_len > ((0xff - 1) / (16 + 1 + 1))) {
+ dev_err(i2c_dev->dev, "more than 14 bytes to be transferred\n");
+ return -EINVAL;
+ }
+
+ /* configure the number of shift clock edges in the transfer */
+ cmp = flexio_readl(base, TIMCMP_0);
+ cmp &= 0x00ff;
+ cmp |= (xfer_len * 18 + 1) << 8;
+ flexio_writel(base, cmp, TIMCMP_0);
+
+ cfg = flexio_readl(base, TIMCFG_0);
+ cfg &= ~TIMCFG_TIMDIS_MASK;
+ cfg |= TIMCFG_TIMDIS(TIMDIS_TIMER_COMPARE);
+ flexio_writel(base, cfg, TIMCFG_0);
+
+ return 0;
+}
+
+static int i2c_master_write(struct imx_flexio_i2c_master_dev *i2c_dev,
+ struct i2c_msg *msg)
+{
+ void __iomem *base = i2c_dev->base;
+ void __iomem *timstat = base + TIMSTAT;
+ int timeout, val;
+
+ i2c_dev->state = START_ADDRESS_WRITE;
+ if (msg->len)
+ i2c_dev->stop = NOT_LAST_8_BIT;
+ else
+ i2c_dev->stop = LAST_8_BIT;
+ i2c_dev->need_check_ack = true;
+
+ i2c_dev->slave_addr = i2c_8bit_addr_from_msg(msg);
+ i2c_dev->data = msg->buf;
+ i2c_dev->len = msg->len;
+
+ flexio_writel(base, 3, SHIFTSIEN);
+
+ timeout = wait_for_completion_timeout(&i2c_dev->complete, XFER_TIMEOUT);
+ if (timeout) {
+ if (i2c_dev->nack)
+ return -EIO;
+ } else {
+ return -EIO;
+ }
+ reinit_completion(&i2c_dev->complete);
+
+ i2c_dev->nack = false;
+ flexio_writel(base, 0, SHIFTSIEN);
+
+ readl_relaxed_poll_timeout(timstat, val, val & 1, 0, 1000);
+ flexio_writel(base, 1, TIMSTAT);
+
+ return 0;
+}
+
+static int i2c_master_read(struct imx_flexio_i2c_master_dev *i2c_dev,
+ struct i2c_msg *msg)
+{
+ void __iomem *base = i2c_dev->base;
+ void __iomem *timstat = base + TIMSTAT;
+ int timeout, val;
+
+ clear_shifter_flags(i2c_dev, RX_FULL_F);
+
+ i2c_dev->state = START_ADDRESS_READ;
+ if (msg->len)
+ i2c_dev->stop = NOT_LAST_8_BIT;
+ else
+ i2c_dev->stop = LAST_8_BIT;
+ i2c_dev->need_check_ack = true;
+ i2c_dev->read = true;
+
+ i2c_dev->slave_addr = i2c_8bit_addr_from_msg(msg);
+ i2c_dev->data = msg->buf;
+ i2c_dev->len = msg->len;
+
+ flexio_writel(base, 3, SHIFTSIEN);
+
+ timeout = wait_for_completion_timeout(&i2c_dev->complete, XFER_TIMEOUT);
+ if (timeout) {
+ if (i2c_dev->nack)
+ return -EIO;
+ } else {
+ return -EIO;
+ }
+ reinit_completion(&i2c_dev->complete);
+
+ i2c_dev->nack = false;
+ flexio_writel(base, 0, SHIFTSIEN);
+
+ readl_relaxed_poll_timeout(timstat, val, val & 1, 0, 1000);
+ flexio_writel(base, 1, TIMSTAT);
+
+ return 0;
+}
+
+static int xfer_msg(struct imx_flexio_i2c_master_dev *i2c_dev, struct i2c_msg *msg)
+{
+ void __iomem *base = i2c_dev->base;
+ void __iomem *shiftstat = base + SHIFTSTAT;
+ u32 xfer_len, val;
+ bool msg_read = !!(msg->flags & I2C_M_RD);
+ int err;
+
+ xfer_len = msg->len + 1;
+
+ /*
+ * Sets the number of bytes to be transferred from a start signal to a
+ * stop signal. Timer 0 is used to generate SCL output and to trigger
+ * timer 1.
+ */
+ if (setup_xfer_count(i2c_dev, xfer_len))
+ return -EINVAL;
+
+ err = readl_relaxed_poll_timeout(shiftstat, val, val & 1, 0, 1000);
+ if (err) {
+ dev_err(i2c_dev->dev, "wait transmit SHIFTBUF empty timeout\n");
+ return err;
+ }
+
+ if (!msg_read)
+ err = i2c_master_write(i2c_dev, msg);
+ else
+ err = i2c_master_read(i2c_dev, msg);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int imx_flexio_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct imx_flexio_i2c_master_dev *i2c_dev = i2c_get_adapdata(adap);
+ void __iomem *base = i2c_dev->base;
+ u32 ctl;
+ int i, err;
+
+ if (i2c_dev->nack) {
+ ctl = flexio_readl(base, SHIFTCTL_0);
+ ctl &= ~SHIFTCTL_PINCFG_MASK;
+ ctl |= SHIFTCTL_PINCFG(SHIFTER_PIN_OPEN_DRAIN_OUTPUT);
+ ctl &= ~SHIFTCTL_SMOD_MASK;
+ ctl |= SHIFTCTL_SMOD(SHIFTER_TRANSMIT);
+ flexio_writel(base, ctl, SHIFTCTL_0);
+
+ ctl = flexio_readl(base, TIMCTL_0);
+ ctl &= ~TIMCTL_TIMOD_MASK;
+ ctl |= TIMCTL_TIMOD(DUAL_8BIT_COUNTERS_BAUD);
+ flexio_writel(base, ctl, TIMCTL_0);
+
+ ctl = flexio_readl(base, TIMCTL_1);
+ ctl &= ~TIMCTL_TIMOD_MASK;
+ ctl |= TIMCTL_TIMOD(SINGLE_16BIT_COUNTER);
+ flexio_writel(base, ctl, TIMCTL_1);
+ }
+ i2c_dev->nack = false;
+
+ i2c_dev->repeated_start = false;
+ if (num > 1)
+ i2c_dev->repeated_start = true;
+
+ clear_shifter_flags(i2c_dev, RX_FULL_F | RX_NAK_F);
+
+ err = i2c_bus_busy(i2c_dev);
+ if (err) {
+ dev_dbg(i2c_dev->dev, "SDA/SCL is continuously pulled down\n");
+
+ imx_flexio_init_hardware(i2c_dev);
+ clear_shifter_flags(i2c_dev, RX_FULL_F | RX_NAK_F);
+ }
+
+ for (i = 0; i < num; i++) {
+ err = xfer_msg(i2c_dev, &msgs[i]);
+ if (err)
+ return err;
+ }
+
+ return (err < 0) ? err : num;
+}
+
+static int imx_flexio_i2c_master_xfer_atomic(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ return 0;
+}
+
+static u32 imx_flexio_i2c_master_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm imx_flexio_i2c_master_algo = {
+ .master_xfer = imx_flexio_i2c_master_xfer,
+ .master_xfer_atomic = imx_flexio_i2c_master_xfer_atomic,
+ .functionality = imx_flexio_i2c_master_func,
+};
+
+static int imx_flexio_i2c_issue_bus_clear(struct i2c_adapter *adap)
+{
+ return 0;
+}
+
+static struct i2c_bus_recovery_info imx_flexio_i2c_recovery_info = {
+ .recover_bus = imx_flexio_i2c_issue_bus_clear,
+};
+
+static int imx_flexio_i2c_master_probe(struct platform_device *pdev)
+{
+ struct imx_flexio_i2c_master_dev *i2c_dev;
+ struct resource *res;
+ int err;
+
+ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, i2c_dev);
+
+ i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(i2c_dev->base))
+ return PTR_ERR(i2c_dev->base);
+
+ i2c_dev->dev = &pdev->dev;
+
+ err = platform_get_irq(pdev, 0);
+ if (err < 0)
+ return err;
+ i2c_dev->irq = err;
+
+ err = devm_request_irq(i2c_dev->dev, i2c_dev->irq, imx_flexio_i2c_isr,
+ IRQF_NO_SUSPEND, dev_name(i2c_dev->dev),
+ i2c_dev);
+ if (err)
+ return err;
+
+ i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c_dev->clk)) {
+ dev_err(&pdev->dev, "missing imx flexio i2c master clock\n");
+ return PTR_ERR(i2c_dev->clk);
+ }
+
+ err = clk_prepare_enable(i2c_dev->clk);
+ if (err) {
+ dev_err(i2c_dev->dev, "failed to enable imx flexio i2c master clock: %d\n", err);
+ return err;
+ }
+
+ /* hardware configuration */
+ err = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &i2c_dev->baudrate);
+ if (err < 0) {
+ dev_err(i2c_dev->dev, "no clock-frequency found\n");
+ return err;
+ }
+ i2c_dev->src_clock = clk_get_rate(i2c_dev->clk);
+ i2c_dev->shifters[0] = 0;
+ i2c_dev->shifters[1] = 1;
+ i2c_dev->timers[0] = 0;
+ i2c_dev->timers[1] = 1;
+ err = of_property_read_u8(pdev->dev.of_node, "sda", &i2c_dev->sda_pin);
+ if (err < 0) {
+ dev_err(i2c_dev->dev, "no sda property found\n");
+ return err;
+ }
+ err = of_property_read_u8(pdev->dev.of_node, "scl", &i2c_dev->scl_pin);
+ if (err < 0) {
+ dev_err(i2c_dev->dev, "no scl property found\n");
+ return err;
+ }
+ imx_flexio_init_hardware(i2c_dev);
+
+ i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
+ i2c_dev->adapter.owner = THIS_MODULE;
+ i2c_dev->adapter.class = I2C_CLASS_DEPRECATED;
+ i2c_dev->adapter.algo = &imx_flexio_i2c_master_algo;
+ i2c_dev->adapter.timeout = 600;
+ i2c_dev->adapter.retries = 1;
+ i2c_dev->adapter.dev.parent = i2c_dev->dev;
+ i2c_dev->adapter.dev.of_node = i2c_dev->dev->of_node;
+ i2c_dev->adapter.nr = pdev->id;
+ i2c_dev->adapter.bus_recovery_info = &imx_flexio_i2c_recovery_info;
+
+ strlcpy(i2c_dev->adapter.name, dev_name(i2c_dev->dev),
+ sizeof(i2c_dev->adapter.name));
+
+ init_completion(&i2c_dev->complete);
+ spin_lock_init(&i2c_dev->lock);
+
+ err = i2c_add_adapter(&i2c_dev->adapter);
+ if (err)
+ goto release_clock;
+
+ return 0;
+
+release_clock:
+ clk_disable(i2c_dev->clk);
+
+ return err;
+}
+
+static int imx_flexio_i2c_master_remove(struct platform_device *pdev)
+{
+ struct imx_flexio_i2c_master_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c_dev->adapter);
+ clk_disable(i2c_dev->clk);
+ return 0;
+}
+
+static const struct of_device_id imx_flexio_i2c_master_of_match[] = {
+ { .compatible = "imx,flexio_i2c_master", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, imx_flexio_i2c_master_of_match);
+
+static struct platform_driver imx_flexio_i2c_master_driver = {
+ .probe = imx_flexio_i2c_master_probe,
+ .remove = imx_flexio_i2c_master_remove,
+ .driver = {
+ .name = "imx-flexio-i2c-master",
+ .of_match_table = imx_flexio_i2c_master_of_match,
+ },
+};
+module_platform_driver(imx_flexio_i2c_master_driver);
+
+MODULE_DESCRIPTION("NXP I.MX FlexIO I2C Master driver");
+MODULE_AUTHOR("Alice Guo <alice.guo@nxp.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
index 8b9ba055c418..4e8d6a2f8a80 100644
--- a/drivers/i2c/busses/i2c-imx-lpi2c.c
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -8,6 +8,8 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
@@ -18,6 +20,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -30,6 +33,7 @@
#define LPI2C_MCR 0x10 /* i2c contrl register */
#define LPI2C_MSR 0x14 /* i2c status register */
#define LPI2C_MIER 0x18 /* i2c interrupt enable */
+#define LPI2C_MDER 0x1C /* i2c DMA enable */
#define LPI2C_MCFGR0 0x20 /* i2c master configuration */
#define LPI2C_MCFGR1 0x24 /* i2c master configuration */
#define LPI2C_MCFGR2 0x28 /* i2c master configuration */
@@ -71,18 +75,22 @@
#define MCFGR1_AUTOSTOP BIT(8)
#define MCFGR1_IGNACK BIT(9)
#define MRDR_RXEMPTY BIT(14)
+#define MDER_TDDE BIT(0)
+#define MDER_RDDE BIT(1)
-#define I2C_CLK_RATIO 2
+#define I2C_CLK_RATIO 24 / 59
#define CHUNK_DATA 256
-#define I2C_PM_TIMEOUT 10 /* ms */
+#define I2C_PM_TIMEOUT 1000 /* ms */
+#define I2C_DMA_THRESHOLD 16 /* bytes */
+#define I2C_USE_PIO (-150)
enum lpi2c_imx_mode {
- STANDARD, /* 100+Kbps */
- FAST, /* 400+Kbps */
- FAST_PLUS, /* 1.0+Mbps */
- HS, /* 3.4+Mbps */
- ULTRA_FAST, /* 5.0+Mbps */
+ STANDARD, /* <=100Kbps */
+ FAST, /* <=400Kbps */
+ FAST_PLUS, /* <=1.0Mbps */
+ HS, /* <=3.4Mbps */
+ ULTRA_FAST, /* <=5.0Mbps */
};
enum lpi2c_imx_pincfg {
@@ -94,7 +102,10 @@ enum lpi2c_imx_pincfg {
struct lpi2c_imx_struct {
struct i2c_adapter adapter;
- struct clk *clk;
+ resource_size_t phy_addr;
+ int irq;
+ struct clk *clk_per;
+ struct clk *clk_ipg;
void __iomem *base;
__u8 *rx_buf;
__u8 *tx_buf;
@@ -106,6 +117,22 @@ struct lpi2c_imx_struct {
unsigned int txfifosize;
unsigned int rxfifosize;
enum lpi2c_imx_mode mode;
+
+ struct i2c_bus_recovery_info rinfo;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pinctrl_pins_default;
+ struct pinctrl_state *pinctrl_pins_gpio;
+
+ bool can_use_dma;
+ bool using_dma;
+ bool xferred;
+ struct i2c_msg *msg;
+ dma_addr_t dma_addr;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
+ enum dma_data_direction dma_direction;
+ u8 *dma_buf;
+ unsigned int dma_len;
};
static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
@@ -133,6 +160,8 @@ static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
+ if (lpi2c_imx->adapter.bus_recovery_info)
+ i2c_recover_bus(&lpi2c_imx->adapter);
return -ETIMEDOUT;
}
schedule();
@@ -146,13 +175,13 @@ 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 < I2C_MAX_FAST_MODE_FREQ)
+ if (bitrate <= I2C_MAX_STANDARD_MODE_FREQ)
mode = STANDARD;
- else if (bitrate < I2C_MAX_FAST_MODE_PLUS_FREQ)
+ else if (bitrate <= I2C_MAX_FAST_MODE_FREQ)
mode = FAST;
- else if (bitrate < I2C_MAX_HIGH_SPEED_MODE_FREQ)
+ else if (bitrate <= I2C_MAX_FAST_MODE_PLUS_FREQ)
mode = FAST_PLUS;
- else if (bitrate < I2C_MAX_ULTRA_FAST_MODE_FREQ)
+ else if (bitrate <= I2C_MAX_HIGH_SPEED_MODE_FREQ)
mode = HS;
else
mode = ULTRA_FAST;
@@ -190,6 +219,8 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
+ if (lpi2c_imx->adapter.bus_recovery_info)
+ i2c_recover_bus(&lpi2c_imx->adapter);
break;
}
schedule();
@@ -197,17 +228,23 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
} while (1);
}
-/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */
+/* CLKLO = (1 - I2C_CLK_RATIO) * clk_cycle, SETHOLD = CLKHI, DATAVD = CLKHI/2
+ CLKHI = I2C_CLK_RATIO * clk_cycle */
static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
{
- u8 prescale, filt, sethold, clkhi, clklo, datavd;
- unsigned int clk_rate, clk_cycle;
+ u8 prescale, filt, sethold, datavd;
+ unsigned int clk_rate, clk_cycle, clkhi, clklo;
enum lpi2c_imx_pincfg pincfg;
unsigned int temp;
lpi2c_imx_set_mode(lpi2c_imx);
- clk_rate = clk_get_rate(lpi2c_imx->clk);
+ clk_rate = clk_get_rate(lpi2c_imx->clk_per);
+ if (!clk_rate) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "clk_per rate is 0\n");
+ return -EINVAL;
+ }
+
if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
filt = 0;
else
@@ -215,8 +252,8 @@ static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
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);
+ - (2 + filt) / (1 << prescale);
+ clkhi = clk_cycle * I2C_CLK_RATIO;
clklo = clk_cycle - clkhi;
if (clklo < 64)
break;
@@ -271,6 +308,9 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
if (ret)
goto rpm_put;
+ if (lpi2c_imx->can_use_dma)
+ writel(MDER_TDDE | MDER_RDDE, lpi2c_imx->base + LPI2C_MDER);
+
temp = readl(lpi2c_imx->base + LPI2C_MCR);
temp |= MCR_MEN;
writel(temp, lpi2c_imx->base + LPI2C_MCR);
@@ -322,6 +362,8 @@ static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
+ if (lpi2c_imx->adapter.bus_recovery_info)
+ i2c_recover_bus(&lpi2c_imx->adapter);
return -ETIMEDOUT;
}
schedule();
@@ -442,6 +484,154 @@ static void lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
}
+static void lpi2c_dma_unmap(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ struct dma_chan *chan = lpi2c_imx->dma_direction == DMA_FROM_DEVICE
+ ? lpi2c_imx->dma_rx : lpi2c_imx->dma_tx;
+
+ dma_unmap_single(chan->device->dev, lpi2c_imx->dma_addr,
+ lpi2c_imx->dma_len, lpi2c_imx->dma_direction);
+
+ lpi2c_imx->dma_direction = DMA_NONE;
+}
+
+static void lpi2c_cleanup_dma(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ if (lpi2c_imx->dma_direction == DMA_NONE)
+ return;
+ else if (lpi2c_imx->dma_direction == DMA_FROM_DEVICE)
+ dmaengine_terminate_all(lpi2c_imx->dma_rx);
+ else if (lpi2c_imx->dma_direction == DMA_TO_DEVICE)
+ dmaengine_terminate_all(lpi2c_imx->dma_tx);
+
+ lpi2c_dma_unmap(lpi2c_imx);
+}
+
+static void lpi2c_dma_callback(void *data)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = (struct lpi2c_imx_struct *)data;
+
+ lpi2c_dma_unmap(lpi2c_imx);
+ writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
+ lpi2c_imx->xferred = true;
+
+ complete(&lpi2c_imx->complete);
+}
+
+static int lpi2c_dma_submit(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msg)
+{
+ bool read = msg->flags & I2C_M_RD;
+ enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ struct dma_chan *chan = read ? lpi2c_imx->dma_rx : lpi2c_imx->dma_tx;
+ struct dma_async_tx_descriptor *txdesc;
+ dma_cookie_t cookie;
+
+ lpi2c_imx->dma_len = read ? msg->len - 1 : msg->len;
+ lpi2c_imx->msg = msg;
+ lpi2c_imx->dma_direction = dir;
+
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ lpi2c_imx->dma_addr = dma_map_single(chan->device->dev,
+ lpi2c_imx->dma_buf,
+ lpi2c_imx->dma_len, dir);
+ if (dma_mapping_error(chan->device->dev, lpi2c_imx->dma_addr)) {
+ dev_err(&lpi2c_imx->adapter.dev, "dma map failed, use pio\n");
+ return -EINVAL;
+ }
+
+ txdesc = dmaengine_prep_slave_single(chan, lpi2c_imx->dma_addr,
+ lpi2c_imx->dma_len, read ?
+ DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!txdesc) {
+ dev_err(&lpi2c_imx->adapter.dev, "dma prep slave sg failed, use pio\n");
+ lpi2c_cleanup_dma(lpi2c_imx);
+ return -EINVAL;
+ }
+
+ reinit_completion(&lpi2c_imx->complete);
+ txdesc->callback = lpi2c_dma_callback;
+ txdesc->callback_param = (void *)lpi2c_imx;
+
+ cookie = dmaengine_submit(txdesc);
+ if (dma_submit_error(cookie)) {
+ dev_err(&lpi2c_imx->adapter.dev, "submitting dma failed, use pio\n");
+ lpi2c_cleanup_dma(lpi2c_imx);
+ return -EINVAL;
+ }
+
+ lpi2c_imx_intctrl(lpi2c_imx, MIER_NDIE);
+
+ dma_async_issue_pending(chan);
+
+ return 0;
+}
+
+static bool is_use_dma(struct lpi2c_imx_struct *lpi2c_imx, struct i2c_msg *msg)
+{
+ if (!lpi2c_imx->can_use_dma)
+ return false;
+
+ if (msg->len < I2C_DMA_THRESHOLD)
+ return false;
+
+ return true;
+}
+
+static int lpi2c_imx_push_rx_cmd(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msg)
+{
+ unsigned int temp, rx_remain;
+ unsigned long orig_jiffies = jiffies;
+
+ if ((msg->flags & I2C_M_RD)) {
+ rx_remain = msg->len;
+ do {
+ temp = rx_remain > CHUNK_DATA ?
+ CHUNK_DATA - 1 : rx_remain - 1;
+ temp |= (RECV_DATA << 8);
+ while ((readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff) > 2) {
+ if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(1000))) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
+ if (lpi2c_imx->adapter.bus_recovery_info)
+ i2c_recover_bus(&lpi2c_imx->adapter);
+ return -ETIMEDOUT;
+ }
+ schedule();
+ }
+ writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+ rx_remain = rx_remain - (temp & 0xff) - 1;
+ } while (rx_remain > 0);
+ }
+
+ return 0;
+}
+
+static int lpi2c_dma_xfer(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msg)
+{
+ int result;
+
+ result = lpi2c_dma_submit(lpi2c_imx, msg);
+ if (!result) {
+ result = lpi2c_imx_push_rx_cmd(lpi2c_imx, msg);
+ if (result)
+ return result;
+ result = lpi2c_imx_msg_complete(lpi2c_imx);
+ return result;
+ }
+
+ /* DMA xfer failed, try to use PIO, clean up dma things */
+ i2c_put_dma_safe_msg_buf(lpi2c_imx->dma_buf, lpi2c_imx->msg,
+ lpi2c_imx->xferred);
+ lpi2c_cleanup_dma(lpi2c_imx);
+
+ return I2C_USE_PIO;
+}
+
static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
@@ -454,6 +644,9 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
return result;
for (i = 0; i < num; i++) {
+ lpi2c_imx->xferred = false;
+ lpi2c_imx->using_dma = false;
+
result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
if (result)
goto disable;
@@ -462,9 +655,24 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
if (num == 1 && msgs[0].len == 0)
goto stop;
+ if (is_use_dma(lpi2c_imx, &msgs[i])) {
+ lpi2c_imx->using_dma = true;
+
+ writel(0x1, lpi2c_imx->base + LPI2C_MFCR);
+
+ lpi2c_imx->dma_buf = i2c_get_dma_safe_msg_buf(&msgs[i],
+ I2C_DMA_THRESHOLD);
+ if (lpi2c_imx->dma_buf) {
+ result = lpi2c_dma_xfer(lpi2c_imx, &msgs[i]);
+ if (result != I2C_USE_PIO)
+ goto stop;
+ }
+ }
+
+ lpi2c_imx->using_dma = false;
lpi2c_imx->delivered = 0;
lpi2c_imx->msglen = msgs[i].len;
- init_completion(&lpi2c_imx->complete);
+ reinit_completion(&lpi2c_imx->complete);
if (msgs[i].flags & I2C_M_RD)
lpi2c_imx_read(lpi2c_imx, &msgs[i]);
@@ -483,7 +691,16 @@ static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
}
stop:
- lpi2c_imx_stop(lpi2c_imx);
+ if (!lpi2c_imx->using_dma)
+ lpi2c_imx_stop(lpi2c_imx);
+ else {
+ i2c_put_dma_safe_msg_buf(lpi2c_imx->dma_buf, lpi2c_imx->msg,
+ lpi2c_imx->xferred);
+ if (result) {
+ lpi2c_cleanup_dma(lpi2c_imx);
+ writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
+ }
+ }
temp = readl(lpi2c_imx->base + LPI2C_MSR);
if ((temp & MSR_NDF) && !result)
@@ -507,18 +724,89 @@ static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)
lpi2c_imx_intctrl(lpi2c_imx, 0);
temp = readl(lpi2c_imx->base + LPI2C_MSR);
+ if (temp & MSR_NDF) {
+ if (lpi2c_imx->using_dma) {
+ lpi2c_cleanup_dma(lpi2c_imx);
+ writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
+ }
+ complete(&lpi2c_imx->complete);
+ goto ret;
+ }
+
if (temp & MSR_RDF)
lpi2c_imx_read_rxfifo(lpi2c_imx);
-
- if (temp & MSR_TDF)
+ else if (temp & MSR_TDF)
lpi2c_imx_write_txfifo(lpi2c_imx);
- if (temp & MSR_NDF)
- complete(&lpi2c_imx->complete);
-
+ret:
return IRQ_HANDLED;
}
+static void lpi2c_imx_prepare_recovery(struct i2c_adapter *adap)
+{
+ struct lpi2c_imx_struct *lpi2c_imx;
+
+ lpi2c_imx = container_of(adap, struct lpi2c_imx_struct, adapter);
+
+ pinctrl_select_state(lpi2c_imx->pinctrl, lpi2c_imx->pinctrl_pins_gpio);
+}
+
+static void lpi2c_imx_unprepare_recovery(struct i2c_adapter *adap)
+{
+ struct lpi2c_imx_struct *lpi2c_imx;
+
+ lpi2c_imx = container_of(adap, struct lpi2c_imx_struct, adapter);
+
+ pinctrl_select_state(lpi2c_imx->pinctrl, lpi2c_imx->pinctrl_pins_default);
+}
+
+/*
+ * We switch SCL and SDA to their GPIO function and do some bitbanging
+ * for bus recovery. These alternative pinmux settings can be
+ * described in the device tree by a separate pinctrl state "gpio". If
+ * this is missing this is not a big problem, the only implication is
+ * that we can't do bus recovery.
+ */
+static int lpi2c_imx_init_recovery_info(struct lpi2c_imx_struct *lpi2c_imx,
+ struct platform_device *pdev)
+{
+ struct i2c_bus_recovery_info *rinfo = &lpi2c_imx->rinfo;
+
+ lpi2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (!lpi2c_imx->pinctrl || IS_ERR(lpi2c_imx->pinctrl)) {
+ dev_info(&pdev->dev, "can't get pinctrl, bus recovery not supported\n");
+ return PTR_ERR(lpi2c_imx->pinctrl);
+ }
+
+ lpi2c_imx->pinctrl_pins_default = pinctrl_lookup_state(lpi2c_imx->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ lpi2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(lpi2c_imx->pinctrl,
+ "gpio");
+ rinfo->sda_gpiod = devm_gpiod_get(&pdev->dev, "sda", GPIOD_IN);
+ rinfo->scl_gpiod = devm_gpiod_get(&pdev->dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN);
+
+ if (PTR_ERR(rinfo->sda_gpiod) == -EPROBE_DEFER ||
+ PTR_ERR(rinfo->scl_gpiod) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(rinfo->sda_gpiod) ||
+ IS_ERR(rinfo->scl_gpiod) ||
+ IS_ERR(lpi2c_imx->pinctrl_pins_default) ||
+ IS_ERR(lpi2c_imx->pinctrl_pins_gpio)) {
+ dev_dbg(&pdev->dev, "recovery information incomplete\n");
+ return 0;
+ }
+
+ dev_info(&pdev->dev, "using scl%s for recovery\n",
+ rinfo->sda_gpiod ? ",sda" : "");
+
+ rinfo->prepare_recovery = lpi2c_imx_prepare_recovery;
+ rinfo->unprepare_recovery = lpi2c_imx_unprepare_recovery;
+ rinfo->recover_bus = i2c_generic_scl_recovery;
+ lpi2c_imx->adapter.bus_recovery_info = rinfo;
+
+ return 0;
+}
+
static u32 lpi2c_imx_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
@@ -536,23 +824,97 @@ static const struct of_device_id lpi2c_imx_of_match[] = {
};
MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
+static void lpi2c_dma_exit(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ if (lpi2c_imx->dma_rx) {
+ dma_release_channel(lpi2c_imx->dma_rx);
+ lpi2c_imx->dma_rx = NULL;
+ }
+
+ if (lpi2c_imx->dma_tx) {
+ dma_release_channel(lpi2c_imx->dma_tx);
+ lpi2c_imx->dma_tx = NULL;
+ }
+}
+
+static int lpi2c_dma_init(struct device *dev,
+ struct lpi2c_imx_struct *lpi2c_imx)
+{
+ int ret;
+ struct dma_slave_config dma_sconfig;
+
+ /* Prepare for TX DMA: */
+ lpi2c_imx->dma_tx = dma_request_chan(dev, "tx");
+ if (IS_ERR(lpi2c_imx->dma_tx)) {
+ ret = PTR_ERR(lpi2c_imx->dma_tx);
+ dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret);
+ lpi2c_imx->dma_tx = NULL;
+ goto err;
+ }
+
+ dma_sconfig.dst_addr = lpi2c_imx->phy_addr + LPI2C_MTDR;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.dst_maxburst = 1;
+ dma_sconfig.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(lpi2c_imx->dma_tx, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(dev, "can't configure tx channel (%d)\n", ret);
+ goto fail_tx;
+ }
+
+ /* Prepare for RX DMA: */
+ lpi2c_imx->dma_rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(lpi2c_imx->dma_rx)) {
+ ret = PTR_ERR(lpi2c_imx->dma_rx);
+ dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret);
+ lpi2c_imx->dma_rx = NULL;
+ goto fail_tx;
+ }
+
+ dma_sconfig.src_addr = lpi2c_imx->phy_addr + LPI2C_MRDR;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_sconfig.src_maxburst = 1;
+ dma_sconfig.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(lpi2c_imx->dma_rx, &dma_sconfig);
+ if (ret < 0) {
+ dev_err(dev, "can't configure rx channel (%d)\n", ret);
+ goto fail_rx;
+ }
+
+ lpi2c_imx->can_use_dma = true;
+ lpi2c_imx->using_dma = false;
+
+ return 0;
+fail_rx:
+ dma_release_channel(lpi2c_imx->dma_rx);
+fail_tx:
+ dma_release_channel(lpi2c_imx->dma_tx);
+err:
+ lpi2c_dma_exit(lpi2c_imx);
+ lpi2c_imx->can_use_dma = false;
+ return ret;
+}
+
static int lpi2c_imx_probe(struct platform_device *pdev)
{
struct lpi2c_imx_struct *lpi2c_imx;
unsigned int temp;
- int irq, ret;
+ int ret;
+ struct resource *res;
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_platform_ioremap_resource(pdev, 0);
if (IS_ERR(lpi2c_imx->base))
return PTR_ERR(lpi2c_imx->base);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ lpi2c_imx->phy_addr = (dma_addr_t)res->start;
+ lpi2c_imx->irq = platform_get_irq(pdev, 0);
+ if (lpi2c_imx->irq < 0)
+ return lpi2c_imx->irq;
lpi2c_imx->adapter.owner = THIS_MODULE;
lpi2c_imx->adapter.algo = &lpi2c_imx_algo;
@@ -561,10 +923,16 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
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)) {
+ lpi2c_imx->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(lpi2c_imx->clk_per)) {
dev_err(&pdev->dev, "can't get I2C peripheral clock\n");
- return PTR_ERR(lpi2c_imx->clk);
+ return PTR_ERR(lpi2c_imx->clk_per);
+ }
+
+ lpi2c_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(lpi2c_imx->clk_ipg)) {
+ dev_err(&pdev->dev, "can't get I2C ipg clock\n");
+ return PTR_ERR(lpi2c_imx->clk_ipg);
}
ret = of_property_read_u32(pdev->dev.of_node,
@@ -572,32 +940,36 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
if (ret)
lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
- 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;
- }
-
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_get_noresume(&pdev->dev);
- pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
temp = readl(lpi2c_imx->base + LPI2C_PARAM);
lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
+ /* Init optional bus recovery function */
+ ret = lpi2c_imx_init_recovery_info(lpi2c_imx, pdev);
+ /* Give it another chance if pinctrl used is not ready yet */
+ if (ret == -EPROBE_DEFER)
+ goto rpm_disable;
+
+ /* Init DMA */
+ lpi2c_imx->dma_direction = DMA_NONE;
+ lpi2c_imx->dma_rx = lpi2c_imx->dma_tx = NULL;
+ ret = lpi2c_dma_init(&pdev->dev, lpi2c_imx);
+ if (ret) {
+ if (ret == -EPROBE_DEFER)
+ goto rpm_disable;
+ dev_info(&pdev->dev, "use pio mode\n");
+ }
+
+ init_completion(&lpi2c_imx->complete);
+
ret = i2c_add_adapter(&lpi2c_imx->adapter);
if (ret)
goto rpm_disable;
@@ -610,9 +982,9 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
return 0;
rpm_disable:
- pm_runtime_put(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -633,8 +1005,10 @@ static int __maybe_unused lpi2c_runtime_suspend(struct device *dev)
{
struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);
- clk_disable_unprepare(lpi2c_imx->clk);
- pinctrl_pm_select_sleep_state(dev);
+ devm_free_irq(dev, lpi2c_imx->irq, lpi2c_imx);
+ clk_disable_unprepare(lpi2c_imx->clk_ipg);
+ clk_disable_unprepare(lpi2c_imx->clk_per);
+ pinctrl_pm_select_idle_state(dev);
return 0;
}
@@ -645,18 +1019,50 @@ static int __maybe_unused lpi2c_runtime_resume(struct device *dev)
int ret;
pinctrl_pm_select_default_state(dev);
- ret = clk_prepare_enable(lpi2c_imx->clk);
+ ret = clk_prepare_enable(lpi2c_imx->clk_per);
if (ret) {
- dev_err(dev, "failed to enable I2C clock, ret=%d\n", ret);
+ dev_err(dev, "can't enable I2C per clock, ret=%d\n", ret);
return ret;
}
+ ret = clk_prepare_enable(lpi2c_imx->clk_ipg);
+ if (ret) {
+ clk_disable_unprepare(lpi2c_imx->clk_per);
+ dev_err(dev, "can't enable I2C ipg clock, ret=%d\n", ret);
+ }
+
+ ret = devm_request_irq(dev, lpi2c_imx->irq, lpi2c_imx_isr,
+ IRQF_NO_SUSPEND,
+ dev_name(dev), lpi2c_imx);
+ if (ret) {
+ dev_err(dev, "can't claim irq %d\n", lpi2c_imx->irq);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int lpi2c_suspend_noirq(struct device *dev)
+{
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
+static int lpi2c_resume_noirq(struct device *dev)
+{
+ return pm_runtime_force_resume(dev);
+}
+
static const struct dev_pm_ops lpi2c_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(lpi2c_suspend_noirq,
+ lpi2c_resume_noirq)
SET_RUNTIME_PM_OPS(lpi2c_runtime_suspend,
lpi2c_runtime_resume, NULL)
};
@@ -671,7 +1077,17 @@ static struct platform_driver lpi2c_imx_driver = {
},
};
-module_platform_driver(lpi2c_imx_driver);
+static int __init lpi2c_imx_init(void)
+{
+ return platform_driver_register(&lpi2c_imx_driver);
+}
+subsys_initcall(lpi2c_imx_init);
+
+static void __exit lpi2c_imx_exit(void)
+{
+ platform_driver_unregister(&lpi2c_imx_driver);
+}
+module_exit(lpi2c_imx_exit);
MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
MODULE_DESCRIPTION("I2C adapter driver for LPI2C bus");
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 3576b63a6c03..0190f287b183 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -41,16 +41,24 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
+#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/i2c-imx.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/libata.h>
/* This will be the driver name the kernel reports */
#define DRIVER_NAME "imx-i2c"
+#define IMX_I2C_MAX_E_BIT_RATE 384000 /* 384kHz from e7805 errata*/
+
/*
* Enable DMA if transfer byte size is bigger than this threshold.
* As the hardware request, it must bigger than 4 bytes.\
@@ -112,7 +120,56 @@
#define I2CR_IEN_OPCODE_0 0x0
#define I2CR_IEN_OPCODE_1 I2CR_IEN
-#define I2C_PM_TIMEOUT 10 /* ms */
+#define I2C_PM_TIMEOUT 1000 /* ms */
+
+enum pinmux_endian_type {
+ BIG_ENDIAN,
+ LITTLE_ENDIAN,
+};
+
+struct pinmux_cfg {
+ enum pinmux_endian_type endian; /* endian of RCWPMUXCR0 */
+ u32 pmuxcr_offset;
+ u32 pmuxcr_set_bit; /* pin mux of RCWPMUXCR0 */
+};
+
+static struct pinmux_cfg ls1012a_pinmux_cfg = {
+ .endian = BIG_ENDIAN,
+ .pmuxcr_offset = 0x430,
+ .pmuxcr_set_bit = 0x10,
+};
+
+static struct pinmux_cfg ls1043a_pinmux_cfg = {
+ .endian = BIG_ENDIAN,
+ .pmuxcr_offset = 0x40C,
+ .pmuxcr_set_bit = 0x10,
+};
+
+static struct pinmux_cfg ls1046a_pinmux_cfg = {
+ .endian = BIG_ENDIAN,
+ .pmuxcr_offset = 0x40C,
+ .pmuxcr_set_bit = 0x80000000,
+};
+
+static const struct of_device_id pinmux_of_match[] = {
+ { .compatible = "fsl,ls1012a-vf610-i2c", .data = &ls1012a_pinmux_cfg},
+ { .compatible = "fsl,ls1043a-vf610-i2c", .data = &ls1043a_pinmux_cfg},
+ { .compatible = "fsl,ls1046a-vf610-i2c", .data = &ls1046a_pinmux_cfg},
+ {},
+};
+MODULE_DEVICE_TABLE(of, pinmux_of_match);
+
+/* The SCFG, Supplemental Configuration Unit, provides SoC specific
+ * configuration and status registers for the device. There is a
+ * SDHC IO VSEL control register on SCFG for some platforms. It's
+ * used to support SDHC IO voltage switching.
+ */
+static const struct of_device_id scfg_device_ids[] = {
+ { .compatible = "fsl,ls1012a-scfg", },
+ { .compatible = "fsl,ls1043a-scfg", },
+ { .compatible = "fsl,ls1046a-scfg", },
+ {}
+};
/*
* sorted list of clock divider, register value pairs
@@ -166,6 +223,7 @@ enum imx_i2c_type {
IMX1_I2C,
IMX21_I2C,
VF610_I2C,
+ IMX7D_I2C,
};
struct imx_i2c_hwdata {
@@ -208,6 +266,13 @@ struct imx_i2c_struct {
struct pinctrl_state *pinctrl_pins_gpio;
struct imx_i2c_dma *dma;
+ int layerscape_bus_recover;
+ int gpio;
+ int need_set_pmuxcr;
+ int pmuxcr_set;
+ int pmuxcr_endian;
+ void __iomem *pmuxcr_addr;
+
struct i2c_client *slave;
enum i2c_slave_event last_slave_event;
};
@@ -255,10 +320,21 @@ static const struct platform_device_id imx_i2c_devtype[] = {
};
MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);
+static const struct imx_i2c_hwdata imx7d_i2c_hwdata = {
+ .devtype = IMX7D_I2C,
+ .regshift = IMX_I2C_REGSHIFT,
+ .clk_div = imx_i2c_clk_div,
+ .ndivs = ARRAY_SIZE(imx_i2c_clk_div),
+ .i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C,
+ .i2cr_ien_opcode = I2CR_IEN_OPCODE_1,
+
+};
+
static const struct of_device_id i2c_imx_dt_ids[] = {
{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
+ { .compatible = "fsl,imx7d-i2c", .data = &imx7d_i2c_hwdata, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
@@ -279,6 +355,11 @@ static inline int is_vf610_i2c(struct imx_i2c_struct *i2c_imx)
return i2c_imx->hwdata->devtype == VF610_I2C;
}
+static inline int is_imx7d_i2c(struct imx_i2c_struct *i2c_imx)
+{
+ return i2c_imx->hwdata->devtype == IMX7D_I2C;
+}
+
static inline void imx_i2c_write_reg(unsigned int val,
struct imx_i2c_struct *i2c_imx, unsigned int reg)
{
@@ -313,17 +394,17 @@ static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx)
}
/* Functions for DMA support */
-static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
- dma_addr_t phy_addr)
+static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
+ struct device *dev,
+ dma_addr_t phy_addr)
{
struct imx_i2c_dma *dma;
struct dma_slave_config dma_sconfig;
- struct device *dev = &i2c_imx->adapter.dev;
int ret;
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
- return;
+ return -ENOMEM;
dma->chan_tx = dma_request_chan(dev, "tx");
if (IS_ERR(dma->chan_tx)) {
@@ -368,7 +449,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
- return;
+ return 0;
fail_rx:
dma_release_channel(dma->chan_rx);
@@ -376,6 +457,8 @@ fail_tx:
dma_release_channel(dma->chan_tx);
fail_al:
devm_kfree(dev, dma);
+
+ return ret;
}
static void i2c_imx_dma_callback(void *arg)
@@ -511,15 +594,6 @@ static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic)
return -ETIMEDOUT;
}
- /* check for arbitration lost */
- if (i2c_imx->i2csr & I2SR_IAL) {
- dev_dbg(&i2c_imx->adapter.dev, "<%s> Arbitration lost\n", __func__);
- i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
-
- i2c_imx->i2csr = 0;
- return -EAGAIN;
- }
-
dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__);
i2c_imx->i2csr = 0;
return 0;
@@ -536,16 +610,24 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
return 0;
}
-static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
+static int i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
unsigned int i2c_clk_rate)
{
struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
unsigned int div;
int i;
- /* Divider value calculation */
if (i2c_imx->cur_clk == i2c_clk_rate)
- return;
+ return 0;
+
+ /*
+ * Keep the denominator of the following program
+ * always NOT equal to 0.
+ */
+
+ /* Divider value calculation */
+ if (!(i2c_clk_rate / 2))
+ return -EINVAL;
i2c_imx->cur_clk = i2c_clk_rate;
@@ -576,20 +658,23 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
dev_dbg(&i2c_imx->adapter.dev, "IFDR[IC]=0x%x, REAL DIV=%d\n",
i2c_clk_div[i].val, i2c_clk_div[i].div);
#endif
+
+ return 0;
}
static int i2c_imx_clk_notifier_call(struct notifier_block *nb,
unsigned long action, void *data)
{
+ int ret = 0;
struct clk_notifier_data *ndata = data;
struct imx_i2c_struct *i2c_imx = container_of(nb,
struct imx_i2c_struct,
clk_change_nb);
if (action & POST_RATE_CHANGE)
- i2c_imx_set_clk(i2c_imx, ndata->new_rate);
+ ret = i2c_imx_set_clk(i2c_imx, ndata->new_rate);
- return NOTIFY_OK;
+ return notifier_from_errno(ret);
}
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic)
@@ -597,6 +682,10 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic)
unsigned int temp = 0;
int result;
+ result = i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
+ if (result)
+ return result;
+
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
/* Enable I2C controller */
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
@@ -1240,21 +1329,122 @@ fail0:
return (result < 0) ? result : num;
}
+/*
+ * Based on the I2C specification, if the data line (SDA) is
+ * stuck low, the master should send nine * clock pulses.
+ * The I2C slave device that held the bus low should release it
+ * sometime within * those nine clocks. Due to this erratum,
+ * the I2C controller cannot generate nine clock pulses.
+ */
+static int i2c_imx_recovery_for_layerscape(struct imx_i2c_struct *i2c_imx)
+{
+ u32 pmuxcr = 0;
+ int ret;
+ unsigned int i, temp;
+
+ /* configure IICx_SCL/GPIO pin as a GPIO */
+ if (i2c_imx->need_set_pmuxcr == 1) {
+ pmuxcr = ioread32be(i2c_imx->pmuxcr_addr);
+ if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
+ iowrite32be(i2c_imx->pmuxcr_set|pmuxcr,
+ i2c_imx->pmuxcr_addr);
+ else
+ iowrite32(i2c_imx->pmuxcr_set|pmuxcr,
+ i2c_imx->pmuxcr_addr);
+ }
+
+ ret = gpio_request(i2c_imx->gpio, i2c_imx->adapter.name);
+ if (ret) {
+ dev_err(&i2c_imx->adapter.dev,
+ "can't get gpio: %d\n", ret);
+ return ret;
+ }
+
+ /* Configure GPIO pin as an output and open drain. */
+ gpio_direction_output(i2c_imx->gpio, 1);
+ udelay(10);
+
+ /* Write data to generate 9 pulses */
+ for (i = 0; i < 9; i++) {
+ gpio_set_value(i2c_imx->gpio, 1);
+ udelay(10);
+ gpio_set_value(i2c_imx->gpio, 0);
+ udelay(10);
+ }
+ /* ensure that the last level sent is always high */
+ gpio_set_value(i2c_imx->gpio, 1);
+
+ /*
+ * Set I2Cx_IBCR = 0h00 to generate a STOP
+ */
+ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR);
+
+ /*
+ * Set I2Cx_IBCR = 0h80 to reset the I2Cx controller
+ */
+ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode | I2CR_IEN, i2c_imx, IMX_I2C_I2CR);
+
+ /* Restore the saved value of the register SCFG_RCWPMUXCR0 */
+ if (i2c_imx->need_set_pmuxcr == 1) {
+ if (i2c_imx->pmuxcr_endian == BIG_ENDIAN)
+ iowrite32be(pmuxcr, i2c_imx->pmuxcr_addr);
+ else
+ iowrite32(pmuxcr, i2c_imx->pmuxcr_addr);
+ }
+ /*
+ * Set I2C_IBSR[IBAL] to clear the IBAL bit if-
+ * I2C_IBSR[IBAL] = 1
+ */
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ if (temp & I2SR_IAL)
+ i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
+
+ return 0;
+}
+
static int i2c_imx_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
+ bool enable_runtime_pm = false;
int result;
+ if (!pm_runtime_enabled(i2c_imx->adapter.dev.parent)) {
+ pm_runtime_enable(i2c_imx->adapter.dev.parent);
+ enable_runtime_pm = true;
+ }
+
result = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
- if (result < 0)
+ if (result < 0) {
+ if (enable_runtime_pm)
+ pm_runtime_disable(i2c_imx->adapter.dev.parent);
return result;
+ }
+
+ /*
+ * workaround for ERR010027: ensure that the I2C BUS is idle
+ * before switching to master mode and attempting a Start cycle
+ */
+ result = i2c_imx_bus_busy(i2c_imx, 0, false);
+ if (result) {
+ /* timeout */
+ if ((result == -ETIMEDOUT) && (i2c_imx->layerscape_bus_recover == 1))
+ i2c_imx_recovery_for_layerscape(i2c_imx);
+ else {
+ if (enable_runtime_pm)
+ pm_runtime_disable(i2c_imx->adapter.dev.parent);
+ return result;
+ }
+ }
result = i2c_imx_xfer_common(adapter, msgs, num, false);
pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
+ if (enable_runtime_pm)
+ pm_runtime_disable(i2c_imx->adapter.dev.parent);
+
return result;
}
@@ -1340,6 +1530,50 @@ static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
return 0;
}
+/*
+ * switch SCL and SDA to their GPIO function and do some bitbanging
+ * for bus recovery.
+ * There are platforms such as Layerscape that don't support pinctrl, so add
+ * workaround for layerscape, it has no effect for other platforms.
+ */
+static int i2c_imx_init_recovery_for_layerscape(
+ struct imx_i2c_struct *i2c_imx,
+ struct platform_device *pdev)
+{
+ const struct of_device_id *of_id;
+ struct device_node *np = pdev->dev.of_node;
+ struct pinmux_cfg *pinmux_cfg;
+ struct device_node *scfg_node;
+ void __iomem *scfg_base = NULL;
+
+ i2c_imx->gpio = of_get_named_gpio(np, "scl-gpios", 0);
+ if (!gpio_is_valid(i2c_imx->gpio)) {
+ dev_info(&pdev->dev, "scl-gpios not found\n");
+ return 0;
+ }
+ pinmux_cfg = devm_kzalloc(&pdev->dev, sizeof(*pinmux_cfg), GFP_KERNEL);
+ if (!pinmux_cfg)
+ return -ENOMEM;
+
+ i2c_imx->need_set_pmuxcr = 0;
+ of_id = of_match_node(pinmux_of_match, np);
+ if (of_id) {
+ pinmux_cfg = (struct pinmux_cfg *)of_id->data;
+ i2c_imx->pmuxcr_endian = pinmux_cfg->endian;
+ i2c_imx->pmuxcr_set = pinmux_cfg->pmuxcr_set_bit;
+ scfg_node = of_find_matching_node(NULL, scfg_device_ids);
+ if (scfg_node) {
+ scfg_base = of_iomap(scfg_node, 0);
+ if (scfg_base) {
+ i2c_imx->pmuxcr_addr = scfg_base + pinmux_cfg->pmuxcr_offset;
+ i2c_imx->need_set_pmuxcr = 1;
+ }
+ }
+ }
+ i2c_imx->layerscape_bus_recover = 1;
+ return 0;
+}
+
static u32 i2c_imx_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
@@ -1426,7 +1660,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
goto rpm_disable;
/* Request IRQ */
- ret = request_threaded_irq(irq, i2c_imx_isr, NULL, IRQF_SHARED,
+ ret = request_threaded_irq(irq, i2c_imx_isr, NULL,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
pdev->name, i2c_imx);
if (ret) {
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
@@ -1441,16 +1676,39 @@ static int i2c_imx_probe(struct platform_device *pdev)
i2c_imx->bitrate = pdata->bitrate;
i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call;
clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
- i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
+ ret = i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get I2C clock\n");
+ goto clk_notifier_unregister;
+ }
+
+ /*
+ * This limit caused by an i.MX7D hardware issue(e7805 in Errata).
+ * If there is no limit, when the bitrate set up to 400KHz, it will
+ * cause the SCK low level period less than 1.3us.
+ */
+ if (is_imx7d_i2c(i2c_imx) && i2c_imx->bitrate > IMX_I2C_MAX_E_BIT_RATE)
+ i2c_imx->bitrate = IMX_I2C_MAX_E_BIT_RATE;
i2c_imx_reset_regs(i2c_imx);
- /* Init optional bus recovery function */
- ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
+ /* Init optional bus recovery */
+ if (of_match_node(pinmux_of_match, pdev->dev.of_node))
+ ret = i2c_imx_init_recovery_for_layerscape(i2c_imx, pdev);
+ else
+ ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
+
/* Give it another chance if pinctrl used is not ready yet */
if (ret == -EPROBE_DEFER)
goto clk_notifier_unregister;
+ /* Init DMA config if supported */
+ ret = i2c_imx_dma_request(i2c_imx, &pdev->dev, phy_addr);
+ if (ret == -EPROBE_DEFER) {
+ dev_err(&pdev->dev, "DMA not ready, go defer probe!\n");
+ goto clk_notifier_unregister;
+ }
+
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0)
@@ -1465,9 +1723,6 @@ static int i2c_imx_probe(struct platform_device *pdev)
i2c_imx->adapter.name);
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
- /* Init DMA config if supported */
- i2c_imx_dma_request(i2c_imx, phy_addr);
-
return 0; /* Return OK */
clk_notifier_unregister:
@@ -1520,7 +1775,8 @@ static int __maybe_unused i2c_imx_runtime_suspend(struct device *dev)
{
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
- clk_disable(i2c_imx->clk);
+ clk_disable_unprepare(i2c_imx->clk);
+ pinctrl_pm_select_sleep_state(dev);
return 0;
}
@@ -1530,14 +1786,28 @@ static int __maybe_unused i2c_imx_runtime_resume(struct device *dev)
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
int ret;
- ret = clk_enable(i2c_imx->clk);
+ pinctrl_pm_select_default_state(dev);
+ ret = clk_prepare_enable(i2c_imx->clk);
if (ret)
dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
return ret;
}
+static int i2c_imx_suspend(struct device *dev)
+{
+ pinctrl_pm_select_sleep_state(dev);
+ return 0;
+}
+
+static int i2c_imx_resume(struct device *dev)
+{
+ pinctrl_pm_select_default_state(dev);
+ return 0;
+}
+
static const struct dev_pm_ops i2c_imx_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
i2c_imx_runtime_resume, NULL)
};
diff --git a/drivers/i2c/busses/i2c-rpmsg-imx.c b/drivers/i2c/busses/i2c-rpmsg-imx.c
new file mode 100644
index 000000000000..06e705271172
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rpmsg-imx.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2019 NXP
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* The i2c-rpmsg transfer protocol:
+ *
+ * +---------------+-------------------------------+
+ * | Byte Offset | Content |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 0 | Category |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 1 ~ 2 | Version |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 3 | Type |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 4 | Command |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 5 | Priority |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 6 | Reserved1 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 7 | Reserved2 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 8 | Reserved3 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 9 | Reserved4 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 10 | BUS ID |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 11 | Return Value |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 12 ~ 13 | BUS ID |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 14 ~ 15 | Address |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 16 ~ 17 | Data Len |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 18 ~ 33 | 16 Bytes Data |
+ * +---------------+---+---+---+---+---+---+---+---+
+ *
+ * The definition of Return Value:
+ * 0x00 = Success
+ * 0x01 = Failed
+ * 0x02 = Invalid parameter
+ * 0x03 = Invalid message
+ * 0x04 = Operate in invalid state
+ * 0x05 = Memory allocation failed
+ * 0x06 = Timeout when waiting for an event
+ * 0x07 = Cannot add to list as node already in another list
+ * 0x08 = Cannot remove from list as node not in list
+ * 0x09 = Transfer timeout
+ * 0x0A = Transfer failed due to peer core not ready
+ * 0x0B = Transfer failed due to communication failure
+ * 0x0C = Cannot find service for a request/notification
+ * 0x0D = Service version cannot support the request/notification
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/imx_rpmsg.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/rpmsg.h>
+
+#define I2C_RPMSG_MAX_BUF_SIZE 16
+#define I2C_RPMSG_TIMEOUT 500 /* unit: ms */
+
+#define I2C_RPMSG_CATEGORY 0x09
+#define I2C_RPMSG_VERSION 0x0001
+#define I2C_RPMSG_TYPE_REQUEST 0x00
+#define I2C_RPMSG_TYPE_RESPONSE 0x01
+#define I2C_RPMSG_COMMAND_READ 0x00
+#define I2C_RPMSG_COMMAND_WRITE 0x01
+#define I2C_RPMSG_PRIORITY 0x01
+
+#define I2C_RPMSG_M_STOP 0x0200
+
+struct i2c_rpmsg_msg {
+ struct imx_rpmsg_head header;
+
+ /* Payload Start*/
+ u8 bus_id;
+ u8 ret_val;
+ u16 addr;
+ u16 flags;
+ u16 len;
+ u8 buf[I2C_RPMSG_MAX_BUF_SIZE];
+} __packed __aligned(1);
+
+struct i2c_rpmsg_info {
+ struct rpmsg_device *rpdev;
+ struct device *dev;
+ struct i2c_rpmsg_msg *msg;
+ struct completion cmd_complete;
+ struct mutex lock;
+
+ u8 bus_id;
+ u16 addr;
+};
+
+static struct i2c_rpmsg_info i2c_rpmsg;
+
+struct imx_rpmsg_i2c_data {
+ struct i2c_adapter adapter;
+};
+
+static int i2c_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ struct i2c_rpmsg_msg *msg = (struct i2c_rpmsg_msg *)data;
+
+ if (msg->header.type != I2C_RPMSG_TYPE_RESPONSE)
+ return -EINVAL;
+
+ if (msg->bus_id != i2c_rpmsg.bus_id || msg->addr != i2c_rpmsg.addr) {
+ dev_err(&rpdev->dev,
+ "expected bus_id:%d, addr:%2x, received bus_id:%d, addr:%2x\n",
+ i2c_rpmsg.bus_id, i2c_rpmsg.addr, msg->bus_id, msg->addr);
+ return -EINVAL;
+ }
+
+ if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) {
+ dev_err(&rpdev->dev,
+ "%s failed: data length greater than %d, len=%d\n",
+ __func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len);
+ return -EINVAL;
+ }
+
+ /* Receive Success */
+ i2c_rpmsg.msg = msg;
+
+ complete(&i2c_rpmsg.cmd_complete);
+
+ return 0;
+}
+
+static int rpmsg_xfer(struct i2c_rpmsg_msg *rmsg, struct i2c_rpmsg_info *info)
+{
+ int ret = 0;
+
+ ret = rpmsg_send(info->rpdev->ept, (void *)rmsg,
+ sizeof(struct i2c_rpmsg_msg));
+ if (ret < 0) {
+ dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&info->cmd_complete,
+ msecs_to_jiffies(I2C_RPMSG_TIMEOUT));
+ if (!ret) {
+ dev_err(&info->rpdev->dev, "%s failed: timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ if (info->msg->ret_val) {
+ dev_dbg(&info->rpdev->dev,
+ "%s failed: %d\n", __func__, info->msg->ret_val);
+ return -(info->msg->ret_val);
+ }
+
+ return 0;
+}
+
+static int i2c_rpmsg_read(struct i2c_msg *msg, struct i2c_rpmsg_info *info,
+ int bus_id, bool is_last)
+{
+ int ret;
+ struct i2c_rpmsg_msg rmsg;
+
+ if (!info->rpdev)
+ return -EINVAL;
+
+ if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) {
+ dev_err(&info->rpdev->dev,
+ "%s failed: data length greater than %d, len=%d\n",
+ __func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len);
+ return -EINVAL;
+ }
+
+ memset(&rmsg, 0, sizeof(struct i2c_rpmsg_msg));
+ rmsg.header.cate = I2C_RPMSG_CATEGORY;
+ rmsg.header.major = I2C_RPMSG_VERSION;
+ rmsg.header.minor = I2C_RPMSG_VERSION >> 8;
+ rmsg.header.type = I2C_RPMSG_TYPE_REQUEST;
+ rmsg.header.cmd = I2C_RPMSG_COMMAND_READ;
+ rmsg.header.reserved[0] = I2C_RPMSG_PRIORITY;
+ rmsg.bus_id = bus_id;
+ rmsg.ret_val = 0;
+ rmsg.addr = msg->addr;
+ if (is_last)
+ rmsg.flags = msg->flags | I2C_RPMSG_M_STOP;
+ else
+ rmsg.flags = msg->flags;
+ rmsg.len = (msg->len);
+
+ reinit_completion(&info->cmd_complete);
+
+ ret = rpmsg_xfer(&rmsg, info);
+ if (ret)
+ return ret;
+
+ if (!info->msg ||
+ (info->msg->len != msg->len)) {
+ dev_err(&info->rpdev->dev,
+ "%s failed: %d\n", __func__, -EPROTO);
+ return -EPROTO;
+ }
+
+ memcpy(msg->buf, info->msg->buf, info->msg->len);
+
+ return msg->len;
+}
+
+int i2c_rpmsg_write(struct i2c_msg *msg, struct i2c_rpmsg_info *info,
+ int bus_id, bool is_last)
+{
+ int i, ret;
+ struct i2c_rpmsg_msg rmsg;
+
+ if (!info || !info->rpdev)
+ return -EINVAL;
+
+ if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) {
+ dev_err(&info->rpdev->dev,
+ "%s failed: data length greater than %d, len=%d\n",
+ __func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len);
+ return -EINVAL;
+ }
+
+ memset(&rmsg, 0, sizeof(struct i2c_rpmsg_msg));
+ rmsg.header.cate = I2C_RPMSG_CATEGORY;
+ rmsg.header.major = I2C_RPMSG_VERSION;
+ rmsg.header.minor = I2C_RPMSG_VERSION >> 8;
+ rmsg.header.type = I2C_RPMSG_TYPE_REQUEST;
+ rmsg.header.cmd = I2C_RPMSG_COMMAND_WRITE;
+ rmsg.header.reserved[0] = I2C_RPMSG_PRIORITY;
+ rmsg.bus_id = bus_id;
+ rmsg.ret_val = 0;
+ rmsg.addr = msg->addr;
+ if (is_last)
+ rmsg.flags = msg->flags | I2C_RPMSG_M_STOP;
+ else
+ rmsg.flags = msg->flags;
+ rmsg.len = msg->len;
+
+ for (i = 0; i < rmsg.len; i++)
+ rmsg.buf[i] = msg->buf[i];
+
+ reinit_completion(&info->cmd_complete);
+
+ ret = rpmsg_xfer(&rmsg, info);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int i2c_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+ int ret = 0;
+
+ if (!rpdev) {
+ dev_info(&rpdev->dev, "%s failed, rpdev=NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ i2c_rpmsg.rpdev = rpdev;
+
+ mutex_init(&i2c_rpmsg.lock);
+ init_completion(&i2c_rpmsg.cmd_complete);
+
+ dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+ rpdev->src, rpdev->dst);
+
+ return ret;
+}
+
+static void i2c_rpmsg_remove(struct rpmsg_device *rpdev)
+{
+ i2c_rpmsg.rpdev = NULL;
+ dev_info(&rpdev->dev, "i2c rpmsg driver is removed\n");
+}
+
+static struct rpmsg_device_id i2c_rpmsg_id_table[] = {
+ { .name = "rpmsg-i2c-channel" },
+ { },
+};
+
+static struct rpmsg_driver i2c_rpmsg_driver = {
+ .drv.name = "i2c-rpmsg",
+ .drv.owner = THIS_MODULE,
+ .id_table = i2c_rpmsg_id_table,
+ .probe = i2c_rpmsg_probe,
+ .remove = i2c_rpmsg_remove,
+ .callback = i2c_rpmsg_cb,
+};
+
+
+static int i2c_rpbus_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct imx_rpmsg_i2c_data *rdata =
+ container_of(adapter, struct imx_rpmsg_i2c_data, adapter);
+ struct i2c_msg *pmsg;
+ int i, ret;
+ bool is_last = false;
+
+ mutex_lock(&i2c_rpmsg.lock);
+
+ for (i = 0; i < num; i++) {
+ if (i == num - 1)
+ is_last = true;
+
+ pmsg = &msgs[i];
+
+ i2c_rpmsg.bus_id = rdata->adapter.nr;
+ i2c_rpmsg.addr = pmsg->addr;
+
+ if (pmsg->flags & I2C_M_RD) {
+ ret = i2c_rpmsg_read(pmsg, &i2c_rpmsg,
+ rdata->adapter.nr, is_last);
+ if (ret < 0) {
+ mutex_unlock(&i2c_rpmsg.lock);
+ return ret;
+ }
+
+ pmsg->len = ret;
+ } else {
+ ret = i2c_rpmsg_write(pmsg, &i2c_rpmsg,
+ rdata->adapter.nr, is_last);
+ if (ret < 0) {
+ mutex_unlock(&i2c_rpmsg.lock);
+ return ret;
+ }
+ }
+ }
+
+ mutex_unlock(&i2c_rpmsg.lock);
+ return num;
+}
+
+static u32 i2c_rpbus_func(struct i2c_adapter *a)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm i2c_rpbus_algorithm = {
+ .master_xfer = i2c_rpbus_xfer,
+ .functionality = i2c_rpbus_func,
+};
+
+static const struct i2c_adapter_quirks i2c_rpbus_quirks = {
+ .max_write_len = I2C_RPMSG_MAX_BUF_SIZE,
+ .max_read_len = I2C_RPMSG_MAX_BUF_SIZE,
+};
+
+static int i2c_rpbus_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct imx_rpmsg_i2c_data *rdata;
+ struct i2c_adapter *adapter;
+ int ret;
+
+ if (!i2c_rpmsg.rpdev)
+ return -EPROBE_DEFER;
+
+ rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata), GFP_KERNEL);
+ if (!rdata)
+ return -ENOMEM;
+
+ adapter = &rdata->adapter;
+ /* setup i2c adapter description */
+ adapter->owner = THIS_MODULE;
+ adapter->class = I2C_CLASS_HWMON;
+ adapter->algo = &i2c_rpbus_algorithm;
+ adapter->dev.parent = dev;
+ adapter->dev.of_node = np;
+ adapter->nr = of_alias_get_id(np, "i2c");
+ /*
+ * The driver will send the adapter->nr as BUS ID to the other
+ * side, and the other side will check the BUS ID to see whether
+ * the BUS has been registered. If there is alias id for this
+ * virtual adapter, linux kernel will automatically allocate one
+ * id which might be not the same number used in the other side,
+ * cause i2c slave probe failure under this virtual I2C bus.
+ * So let's add a BUG_ON to catch this issue earlier.
+ */
+ BUG_ON(adapter->nr < 0);
+ adapter->quirks = &i2c_rpbus_quirks;
+ snprintf(rdata->adapter.name, sizeof(rdata->adapter.name), "%s",
+ "i2c-rpmsg-adapter");
+ platform_set_drvdata(pdev, rdata);
+
+ ret = i2c_add_adapter(&rdata->adapter);
+ if (ret < 0) {
+ dev_err(dev, "failed to add I2C adapter: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(dev, "add I2C adapter %s successfully\n", rdata->adapter.name);
+
+ return 0;
+}
+
+static int i2c_rpbus_remove(struct platform_device *pdev)
+{
+ struct imx_rpmsg_i2c_data *rdata = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&rdata->adapter);
+
+ return 0;
+}
+
+static const struct of_device_id imx_rpmsg_i2c_dt_ids[] = {
+ { .compatible = "fsl,i2c-rpbus", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_rpmsg_i2c_dt_ids);
+
+static struct platform_driver imx_rpmsg_i2c_driver = {
+ .driver = {
+ .name = "imx_rpmsg_i2c",
+ .of_match_table = imx_rpmsg_i2c_dt_ids,
+ },
+ .probe = i2c_rpbus_probe,
+ .remove = i2c_rpbus_remove
+};
+
+static int __init imx_rpmsg_i2c_driver_init(void)
+{
+ int ret = 0;
+
+ ret = register_rpmsg_driver(&i2c_rpmsg_driver);
+ if (ret < 0)
+ return ret;
+
+ return platform_driver_register(&(imx_rpmsg_i2c_driver));
+}
+subsys_initcall(imx_rpmsg_i2c_driver_init);
+
+MODULE_AUTHOR("Clark Wang<xiaoning.wang@nxp.com>");
+MODULE_DESCRIPTION("Driver for i2c over rpmsg");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c-rpbus");
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 4ad665757dd8..e9d6b12ec2db 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -59,6 +59,7 @@ enum pca_type {
pca_9546,
pca_9547,
pca_9548,
+ pca_9646,
pca_9846,
pca_9847,
pca_9848,
@@ -140,6 +141,11 @@ static const struct chip_desc chips[] = {
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
+ [pca_9646] = {
+ .nchans = 4,
+ .muxtype = pca954x_isswi,
+ .id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
+ },
[pca_9846] = {
.nchans = 4,
.muxtype = pca954x_isswi,
@@ -185,6 +191,7 @@ static const struct i2c_device_id pca954x_id[] = {
{ "pca9546", pca_9546 },
{ "pca9547", pca_9547 },
{ "pca9548", pca_9548 },
+ { "pca9646", pca_9646 },
{ "pca9846", pca_9846 },
{ "pca9847", pca_9847 },
{ "pca9848", pca_9848 },
@@ -202,6 +209,7 @@ static const struct of_device_id pca954x_of_match[] = {
{ .compatible = "nxp,pca9546", .data = &chips[pca_9546] },
{ .compatible = "nxp,pca9547", .data = &chips[pca_9547] },
{ .compatible = "nxp,pca9548", .data = &chips[pca_9548] },
+ { .compatible = "nxp,pca9646", .data = &chips[pca_9646] },
{ .compatible = "nxp,pca9846", .data = &chips[pca_9846] },
{ .compatible = "nxp,pca9847", .data = &chips[pca_9847] },
{ .compatible = "nxp,pca9848", .data = &chips[pca_9848] },