diff options
-rw-r--r-- | Documentation/devicetree/bindings/i2c/i2c-rpmsg-imx.txt | 29 | ||||
-rw-r--r-- | drivers/i2c/busses/Kconfig | 14 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 2 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-flexio.c | 994 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx-lpi2c.c | 528 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 330 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-rpmsg-imx.c | 462 | ||||
-rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca954x.c | 8 |
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] }, |