* [v7] i2c: imx: support slave mode for imx I2C driver
@ 2020-10-16 8:24 ` Biwen Li
0 siblings, 0 replies; 4+ messages in thread
From: Biwen Li @ 2020-10-16 8:24 UTC (permalink / raw)
To: leoyang.li, linux, kernel, wsa, shawnguo, s.hauer, festevam,
aisheng.dong, xiaoning.wang, o.rempel
Cc: linux-i2c, linux-kernel, jiafei.pan, xiaobo.xie, linux-arm-kernel,
Biwen Li
From: Biwen Li <biwen.li@nxp.com>
The patch supports slave mode for imx I2C driver
Signed-off-by: Biwen Li <biwen.li@nxp.com>
---
Change in v7:
- support auto switch mode between master and slave
- enable interrupt when idle in slave mode
- remove #ifdef
Change in v6:
- delete robust logs and comments
- not read status register again in master isr.
Change in v5:
- fix a bug that cannot determine in what mode(master mode or
slave mode)
Change in v4:
- add MACRO CONFIG_I2C_SLAVE to fix compilation issue
Change in v3:
- support layerscape and i.mx platform
Change in v2:
- remove MACRO CONFIG_I2C_SLAVE
drivers/i2c/busses/i2c-imx.c | 213 ++++++++++++++++++++++++++++++++---
1 file changed, 199 insertions(+), 14 deletions(-)
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 0ab5381aa012..0d62b09ee967 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -17,6 +17,7 @@
* Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
*
* Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2020 NXP
*
*/
@@ -72,6 +73,7 @@
#define IMX_I2C_I2CR 0x02 /* i2c control */
#define IMX_I2C_I2SR 0x03 /* i2c status */
#define IMX_I2C_I2DR 0x04 /* i2c transfer data */
+#define IMX_I2C_IBIC 0x05 /* i2c transfer data */
#define IMX_I2C_REGSHIFT 2
#define VF610_I2C_REGSHIFT 0
@@ -91,6 +93,7 @@
#define I2CR_MSTA 0x20
#define I2CR_IIEN 0x40
#define I2CR_IEN 0x80
+#define IBIC_BIIE 0x80 // Bus idle interrupt enable
/* register bits different operating codes definition:
* 1) I2SR: Interrupt flags clear operation differ between SoCs:
@@ -201,6 +204,7 @@ struct imx_i2c_struct {
struct pinctrl_state *pinctrl_pins_gpio;
struct imx_i2c_dma *dma;
+ struct i2c_client *slave;
};
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
@@ -277,6 +281,14 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
}
+/* Set up i2c controller register and i2c status register to default value. */
+static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx)
+{
+ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
+}
+
/* Functions for DMA support */
static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dma_addr_t phy_addr)
@@ -614,20 +626,110 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic)
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
}
+/*
+ * Enable bus idle interrupts
+ * Note: IBIC register will be cleared after disabled i2c module.
+ */
+static void i2c_imx_enable_bus_idle(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int temp;
+
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_IBIC);
+ temp |= IBIC_BIIE;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_IBIC);
+}
+
+static void i2c_imx_clr_if_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
+{
+ status &= ~I2SR_IIF;
+ status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
+ imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
+}
+
+/* Clear arbitration lost bit */
+static void i2c_imx_clr_al_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
+{
+ status &= ~I2SR_IAL;
+ status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IAL);
+ imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
+}
+
+static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
+ unsigned int status, unsigned int ctl)
+{
+ u8 value;
+
+ if (status & I2SR_IAL) { /* Arbitration lost */
+ i2c_imx_clr_al_bit(status | I2SR_IIF, i2c_imx);
+ } else if (status & I2SR_IAAS) { /* Addressed as a slave */
+ if (status & I2SR_SRW) { /* Master wants to read from us*/
+ dev_dbg(&i2c_imx->adapter.dev, "read requested");
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
+
+ /* Slave transmit */
+ ctl |= I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+
+ /* Send data */
+ imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
+ } else { /* Master wants to write to us */
+ dev_dbg(&i2c_imx->adapter.dev, "write requested");
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ /* Slave receive */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+ /* Dummy read */
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ }
+ } else if (!(ctl & I2CR_MTX)) { /* Receive mode */
+ if (status & I2SR_IBB) { /* No STOP signal detected */
+ value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+ } else { /* STOP signal is detected */
+ dev_dbg(&i2c_imx->adapter.dev,
+ "STOP signal detected");
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
+ }
+ } else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
+ ctl |= I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
+
+ imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
+ } else { /* Transmit mode received NAK */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx, unsigned int status)
+{
+ /* Save status register */
+ i2c_imx->i2csr = status | I2SR_IIF;
+ wake_up(&i2c_imx->queue);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
{
struct imx_i2c_struct *i2c_imx = dev_id;
- unsigned int temp;
+ unsigned int ctl, status;
+
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+
+ if (status & I2SR_IIF) {
+ i2c_imx_clr_if_bit(status, i2c_imx);
+ if (IS_ENABLED(CONFIG_I2C_SLAVE) && i2c_imx->slave && !(ctl & I2CR_MSTA))
+ return i2c_imx_slave_isr(i2c_imx, status, ctl);
- temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
- if (temp & I2SR_IIF) {
- /* save status register */
- i2c_imx->i2csr = temp;
- temp &= ~I2SR_IIF;
- temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
- imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
- wake_up(&i2c_imx->queue);
- return IRQ_HANDLED;
+ return i2c_imx_master_isr(i2c_imx, status);
}
return IRQ_NONE;
@@ -918,6 +1020,38 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
return 0;
}
+static int i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
+{
+ int temp;
+
+ /* Resume */
+ temp = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
+ if (temp < 0) {
+ dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
+ return temp;
+ }
+
+ /* Set slave addr. */
+ imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
+
+ i2c_imx_reset_regs(i2c_imx);
+
+ /* Enable module */
+ temp = i2c_imx->hwdata->i2cr_ien_opcode;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /* Enable interrupt from i2c module */
+ temp |= I2CR_IIEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ i2c_imx_enable_bus_idle(i2c_imx);
+
+ /* Wait controller to be stable */
+ usleep_range(50, 150);
+
+ return 0;
+}
+
static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num, bool atomic)
{
@@ -999,6 +1133,12 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
(result < 0) ? "error" : "success msg",
(result < 0) ? result : num);
+ /* After data is transferred, switch to slave mode(as a receiver) */
+ if (IS_ENABLED(CONFIG_I2C_SLAVE) && i2c_imx->slave) {
+ if (i2c_imx_slave_init(i2c_imx) < 0)
+ dev_err(&i2c_imx->adapter.dev, "failed to switch to slave mode");
+ }
+
return (result < 0) ? result : num;
}
@@ -1108,10 +1248,58 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}
+static int i2c_imx_reg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_I2C_SLAVE))
+ return -EINVAL;
+
+ if (i2c_imx->slave)
+ return -EBUSY;
+
+ i2c_imx->slave = client;
+
+ ret = i2c_imx_slave_init(i2c_imx);
+ if (ret < 0)
+ dev_err(&i2c_imx->adapter.dev, "failed to switch to slave mode");
+
+ return ret;
+}
+
+static int i2c_imx_unreg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_I2C_SLAVE))
+ return -EINVAL;
+
+ if (!i2c_imx->slave)
+ return -EINVAL;
+
+ /* Reset slave address. */
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
+
+ i2c_imx_reset_regs(i2c_imx);
+
+ i2c_imx->slave = NULL;
+
+ /* Suspend */
+ ret = pm_runtime_put_sync(i2c_imx->adapter.dev.parent);
+ if (ret < 0)
+ dev_err(&i2c_imx->adapter.dev, "failed to suspend i2c controller");
+
+ return ret;
+}
+
static const struct i2c_algorithm i2c_imx_algo = {
.master_xfer = i2c_imx_xfer,
.master_xfer_atomic = i2c_imx_xfer_atomic,
.functionality = i2c_imx_func,
+ .reg_slave = i2c_imx_reg_slave,
+ .unreg_slave = i2c_imx_unreg_slave,
};
static int i2c_imx_probe(struct platform_device *pdev)
@@ -1207,10 +1395,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
- /* Set up chip registers to defaults */
- imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
- i2c_imx, IMX_I2C_I2CR);
- imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
+ i2c_imx_reset_regs(i2c_imx);
/* Init optional bus recovery function */
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
--
2.17.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* [v7] i2c: imx: support slave mode for imx I2C driver
@ 2020-10-16 8:24 ` Biwen Li
0 siblings, 0 replies; 4+ messages in thread
From: Biwen Li @ 2020-10-16 8:24 UTC (permalink / raw)
To: leoyang.li, linux, kernel, wsa, shawnguo, s.hauer, festevam,
aisheng.dong, xiaoning.wang, o.rempel
Cc: Biwen Li, xiaobo.xie, linux-kernel, linux-i2c, jiafei.pan,
linux-arm-kernel
From: Biwen Li <biwen.li@nxp.com>
The patch supports slave mode for imx I2C driver
Signed-off-by: Biwen Li <biwen.li@nxp.com>
---
Change in v7:
- support auto switch mode between master and slave
- enable interrupt when idle in slave mode
- remove #ifdef
Change in v6:
- delete robust logs and comments
- not read status register again in master isr.
Change in v5:
- fix a bug that cannot determine in what mode(master mode or
slave mode)
Change in v4:
- add MACRO CONFIG_I2C_SLAVE to fix compilation issue
Change in v3:
- support layerscape and i.mx platform
Change in v2:
- remove MACRO CONFIG_I2C_SLAVE
drivers/i2c/busses/i2c-imx.c | 213 ++++++++++++++++++++++++++++++++---
1 file changed, 199 insertions(+), 14 deletions(-)
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 0ab5381aa012..0d62b09ee967 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -17,6 +17,7 @@
* Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
*
* Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2020 NXP
*
*/
@@ -72,6 +73,7 @@
#define IMX_I2C_I2CR 0x02 /* i2c control */
#define IMX_I2C_I2SR 0x03 /* i2c status */
#define IMX_I2C_I2DR 0x04 /* i2c transfer data */
+#define IMX_I2C_IBIC 0x05 /* i2c transfer data */
#define IMX_I2C_REGSHIFT 2
#define VF610_I2C_REGSHIFT 0
@@ -91,6 +93,7 @@
#define I2CR_MSTA 0x20
#define I2CR_IIEN 0x40
#define I2CR_IEN 0x80
+#define IBIC_BIIE 0x80 // Bus idle interrupt enable
/* register bits different operating codes definition:
* 1) I2SR: Interrupt flags clear operation differ between SoCs:
@@ -201,6 +204,7 @@ struct imx_i2c_struct {
struct pinctrl_state *pinctrl_pins_gpio;
struct imx_i2c_dma *dma;
+ struct i2c_client *slave;
};
static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
@@ -277,6 +281,14 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
}
+/* Set up i2c controller register and i2c status register to default value. */
+static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx)
+{
+ imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
+}
+
/* Functions for DMA support */
static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
dma_addr_t phy_addr)
@@ -614,20 +626,110 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic)
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
}
+/*
+ * Enable bus idle interrupts
+ * Note: IBIC register will be cleared after disabled i2c module.
+ */
+static void i2c_imx_enable_bus_idle(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int temp;
+
+ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_IBIC);
+ temp |= IBIC_BIIE;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_IBIC);
+}
+
+static void i2c_imx_clr_if_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
+{
+ status &= ~I2SR_IIF;
+ status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
+ imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
+}
+
+/* Clear arbitration lost bit */
+static void i2c_imx_clr_al_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
+{
+ status &= ~I2SR_IAL;
+ status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IAL);
+ imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
+}
+
+static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
+ unsigned int status, unsigned int ctl)
+{
+ u8 value;
+
+ if (status & I2SR_IAL) { /* Arbitration lost */
+ i2c_imx_clr_al_bit(status | I2SR_IIF, i2c_imx);
+ } else if (status & I2SR_IAAS) { /* Addressed as a slave */
+ if (status & I2SR_SRW) { /* Master wants to read from us*/
+ dev_dbg(&i2c_imx->adapter.dev, "read requested");
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
+
+ /* Slave transmit */
+ ctl |= I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+
+ /* Send data */
+ imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
+ } else { /* Master wants to write to us */
+ dev_dbg(&i2c_imx->adapter.dev, "write requested");
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ /* Slave receive */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+ /* Dummy read */
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ }
+ } else if (!(ctl & I2CR_MTX)) { /* Receive mode */
+ if (status & I2SR_IBB) { /* No STOP signal detected */
+ value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
+ } else { /* STOP signal is detected */
+ dev_dbg(&i2c_imx->adapter.dev,
+ "STOP signal detected");
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
+ }
+ } else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
+ ctl |= I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+
+ i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
+
+ imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
+ } else { /* Transmit mode received NAK */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+ imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx, unsigned int status)
+{
+ /* Save status register */
+ i2c_imx->i2csr = status | I2SR_IIF;
+ wake_up(&i2c_imx->queue);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
{
struct imx_i2c_struct *i2c_imx = dev_id;
- unsigned int temp;
+ unsigned int ctl, status;
+
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+
+ if (status & I2SR_IIF) {
+ i2c_imx_clr_if_bit(status, i2c_imx);
+ if (IS_ENABLED(CONFIG_I2C_SLAVE) && i2c_imx->slave && !(ctl & I2CR_MSTA))
+ return i2c_imx_slave_isr(i2c_imx, status, ctl);
- temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
- if (temp & I2SR_IIF) {
- /* save status register */
- i2c_imx->i2csr = temp;
- temp &= ~I2SR_IIF;
- temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
- imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR);
- wake_up(&i2c_imx->queue);
- return IRQ_HANDLED;
+ return i2c_imx_master_isr(i2c_imx, status);
}
return IRQ_NONE;
@@ -918,6 +1020,38 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs,
return 0;
}
+static int i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
+{
+ int temp;
+
+ /* Resume */
+ temp = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
+ if (temp < 0) {
+ dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
+ return temp;
+ }
+
+ /* Set slave addr. */
+ imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
+
+ i2c_imx_reset_regs(i2c_imx);
+
+ /* Enable module */
+ temp = i2c_imx->hwdata->i2cr_ien_opcode;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /* Enable interrupt from i2c module */
+ temp |= I2CR_IIEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ i2c_imx_enable_bus_idle(i2c_imx);
+
+ /* Wait controller to be stable */
+ usleep_range(50, 150);
+
+ return 0;
+}
+
static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num, bool atomic)
{
@@ -999,6 +1133,12 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter,
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
(result < 0) ? "error" : "success msg",
(result < 0) ? result : num);
+ /* After data is transferred, switch to slave mode(as a receiver) */
+ if (IS_ENABLED(CONFIG_I2C_SLAVE) && i2c_imx->slave) {
+ if (i2c_imx_slave_init(i2c_imx) < 0)
+ dev_err(&i2c_imx->adapter.dev, "failed to switch to slave mode");
+ }
+
return (result < 0) ? result : num;
}
@@ -1108,10 +1248,58 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}
+static int i2c_imx_reg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_I2C_SLAVE))
+ return -EINVAL;
+
+ if (i2c_imx->slave)
+ return -EBUSY;
+
+ i2c_imx->slave = client;
+
+ ret = i2c_imx_slave_init(i2c_imx);
+ if (ret < 0)
+ dev_err(&i2c_imx->adapter.dev, "failed to switch to slave mode");
+
+ return ret;
+}
+
+static int i2c_imx_unreg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_I2C_SLAVE))
+ return -EINVAL;
+
+ if (!i2c_imx->slave)
+ return -EINVAL;
+
+ /* Reset slave address. */
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
+
+ i2c_imx_reset_regs(i2c_imx);
+
+ i2c_imx->slave = NULL;
+
+ /* Suspend */
+ ret = pm_runtime_put_sync(i2c_imx->adapter.dev.parent);
+ if (ret < 0)
+ dev_err(&i2c_imx->adapter.dev, "failed to suspend i2c controller");
+
+ return ret;
+}
+
static const struct i2c_algorithm i2c_imx_algo = {
.master_xfer = i2c_imx_xfer,
.master_xfer_atomic = i2c_imx_xfer_atomic,
.functionality = i2c_imx_func,
+ .reg_slave = i2c_imx_reg_slave,
+ .unreg_slave = i2c_imx_unreg_slave,
};
static int i2c_imx_probe(struct platform_device *pdev)
@@ -1207,10 +1395,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
- /* Set up chip registers to defaults */
- imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
- i2c_imx, IMX_I2C_I2CR);
- imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
+ i2c_imx_reset_regs(i2c_imx);
/* Init optional bus recovery function */
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [v7] i2c: imx: support slave mode for imx I2C driver
2020-10-16 8:24 ` Biwen Li
(?)
@ 2020-10-19 15:43 ` kernel test robot
-1 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2020-10-19 15:43 UTC (permalink / raw)
To: kbuild-all
[-- Attachment #1: Type: text/plain, Size: 7047 bytes --]
Hi Biwen,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on v5.9]
[cannot apply to wsa/i2c/for-next next-20201016]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Biwen-Li/i2c-imx-support-slave-mode-for-imx-I2C-driver/20201016-163639
base: bbf5c979011a099af5dc76498918ed7df445635b
config: arm-randconfig-r004-20201019 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project 094e9f4779eb9b5c6a49014f2f80b8cbb833572f)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm cross compiling tool for clang build
# apt-get install binutils-arm-linux-gnueabi
# https://github.com/0day-ci/linux/commit/afe1305a21d6e88b2c015cf62f84893cd9c462d5
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Biwen-Li/i2c-imx-support-slave-mode-for-imx-I2C-driver/20201016-163639
git checkout afe1305a21d6e88b2c015cf62f84893cd9c462d5
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
>> drivers/i2c/busses/i2c-imx.c:667:4: error: implicit declaration of function 'i2c_slave_event' [-Werror,-Wimplicit-function-declaration]
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
^
>> drivers/i2c/busses/i2c-imx.c:667:36: error: use of undeclared identifier 'I2C_SLAVE_READ_REQUESTED'
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
^
drivers/i2c/busses/i2c-imx.c:677:4: error: implicit declaration of function 'i2c_slave_event' [-Werror,-Wimplicit-function-declaration]
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
^
>> drivers/i2c/busses/i2c-imx.c:677:36: error: use of undeclared identifier 'I2C_SLAVE_WRITE_REQUESTED'
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
^
drivers/i2c/busses/i2c-imx.c:688:4: error: implicit declaration of function 'i2c_slave_event' [-Werror,-Wimplicit-function-declaration]
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
^
>> drivers/i2c/busses/i2c-imx.c:688:36: error: use of undeclared identifier 'I2C_SLAVE_WRITE_RECEIVED'
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
^
drivers/i2c/busses/i2c-imx.c:692:4: error: implicit declaration of function 'i2c_slave_event' [-Werror,-Wimplicit-function-declaration]
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
^
>> drivers/i2c/busses/i2c-imx.c:692:36: error: use of undeclared identifier 'I2C_SLAVE_STOP'
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
^
drivers/i2c/busses/i2c-imx.c:698:3: error: implicit declaration of function 'i2c_slave_event' [-Werror,-Wimplicit-function-declaration]
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
^
>> drivers/i2c/busses/i2c-imx.c:698:35: error: use of undeclared identifier 'I2C_SLAVE_READ_PROCESSED'
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
^
>> drivers/i2c/busses/i2c-imx.c:1301:3: error: field designator 'reg_slave' does not refer to any field in type 'const struct i2c_algorithm'
.reg_slave = i2c_imx_reg_slave,
^
>> drivers/i2c/busses/i2c-imx.c:1302:3: error: field designator 'unreg_slave' does not refer to any field in type 'const struct i2c_algorithm'
.unreg_slave = i2c_imx_unreg_slave,
^
12 errors generated.
vim +/i2c_slave_event +667 drivers/i2c/busses/i2c-imx.c
656
657 static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
658 unsigned int status, unsigned int ctl)
659 {
660 u8 value;
661
662 if (status & I2SR_IAL) { /* Arbitration lost */
663 i2c_imx_clr_al_bit(status | I2SR_IIF, i2c_imx);
664 } else if (status & I2SR_IAAS) { /* Addressed as a slave */
665 if (status & I2SR_SRW) { /* Master wants to read from us*/
666 dev_dbg(&i2c_imx->adapter.dev, "read requested");
> 667 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
668
669 /* Slave transmit */
670 ctl |= I2CR_MTX;
671 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
672
673 /* Send data */
674 imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
675 } else { /* Master wants to write to us */
676 dev_dbg(&i2c_imx->adapter.dev, "write requested");
> 677 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
678
679 /* Slave receive */
680 ctl &= ~I2CR_MTX;
681 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
682 /* Dummy read */
683 imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
684 }
685 } else if (!(ctl & I2CR_MTX)) { /* Receive mode */
686 if (status & I2SR_IBB) { /* No STOP signal detected */
687 value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
> 688 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
689 } else { /* STOP signal is detected */
690 dev_dbg(&i2c_imx->adapter.dev,
691 "STOP signal detected");
> 692 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
693 }
694 } else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
695 ctl |= I2CR_MTX;
696 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
697
> 698 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
699
700 imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
701 } else { /* Transmit mode received NAK */
702 ctl &= ~I2CR_MTX;
703 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
704 imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
705 }
706
707 return IRQ_HANDLED;
708 }
709
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 24936 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [v7] i2c: imx: support slave mode for imx I2C driver
2020-10-16 8:24 ` Biwen Li
(?)
(?)
@ 2020-10-19 15:46 ` kernel test robot
-1 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2020-10-19 15:46 UTC (permalink / raw)
To: kbuild-all
[-- Attachment #1: Type: text/plain, Size: 6710 bytes --]
Hi Biwen,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on v5.9]
[cannot apply to wsa/i2c/for-next next-20201016]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Biwen-Li/i2c-imx-support-slave-mode-for-imx-I2C-driver/20201016-163639
base: bbf5c979011a099af5dc76498918ed7df445635b
config: arm-imx_v6_v7_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/afe1305a21d6e88b2c015cf62f84893cd9c462d5
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Biwen-Li/i2c-imx-support-slave-mode-for-imx-I2C-driver/20201016-163639
git checkout afe1305a21d6e88b2c015cf62f84893cd9c462d5
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All error/warnings (new ones prefixed by >>):
drivers/i2c/busses/i2c-imx.c: In function 'i2c_imx_slave_isr':
>> drivers/i2c/busses/i2c-imx.c:667:4: error: implicit declaration of function 'i2c_slave_event' [-Werror=implicit-function-declaration]
667 | i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
| ^~~~~~~~~~~~~~~
>> drivers/i2c/busses/i2c-imx.c:667:36: error: 'I2C_SLAVE_READ_REQUESTED' undeclared (first use in this function)
667 | i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c:667:36: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/i2c/busses/i2c-imx.c:677:36: error: 'I2C_SLAVE_WRITE_REQUESTED' undeclared (first use in this function)
677 | i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/i2c/busses/i2c-imx.c:688:36: error: 'I2C_SLAVE_WRITE_RECEIVED' undeclared (first use in this function)
688 | i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
| ^~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/i2c/busses/i2c-imx.c:692:36: error: 'I2C_SLAVE_STOP' undeclared (first use in this function); did you mean 'I2C_M_STOP'?
692 | i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
| ^~~~~~~~~~~~~~
| I2C_M_STOP
>> drivers/i2c/busses/i2c-imx.c:698:35: error: 'I2C_SLAVE_READ_PROCESSED' undeclared (first use in this function)
698 | i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c: At top level:
>> drivers/i2c/busses/i2c-imx.c:1301:3: error: 'const struct i2c_algorithm' has no member named 'reg_slave'
1301 | .reg_slave = i2c_imx_reg_slave,
| ^~~~~~~~~
>> drivers/i2c/busses/i2c-imx.c:1301:15: warning: excess elements in struct initializer
1301 | .reg_slave = i2c_imx_reg_slave,
| ^~~~~~~~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c:1301:15: note: (near initialization for 'i2c_imx_algo')
>> drivers/i2c/busses/i2c-imx.c:1302:3: error: 'const struct i2c_algorithm' has no member named 'unreg_slave'
1302 | .unreg_slave = i2c_imx_unreg_slave,
| ^~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c:1302:17: warning: excess elements in struct initializer
1302 | .unreg_slave = i2c_imx_unreg_slave,
| ^~~~~~~~~~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c:1302:17: note: (near initialization for 'i2c_imx_algo')
cc1: some warnings being treated as errors
vim +/i2c_slave_event +667 drivers/i2c/busses/i2c-imx.c
656
657 static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx,
658 unsigned int status, unsigned int ctl)
659 {
660 u8 value;
661
662 if (status & I2SR_IAL) { /* Arbitration lost */
663 i2c_imx_clr_al_bit(status | I2SR_IIF, i2c_imx);
664 } else if (status & I2SR_IAAS) { /* Addressed as a slave */
665 if (status & I2SR_SRW) { /* Master wants to read from us*/
666 dev_dbg(&i2c_imx->adapter.dev, "read requested");
> 667 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
668
669 /* Slave transmit */
670 ctl |= I2CR_MTX;
671 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
672
673 /* Send data */
674 imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
675 } else { /* Master wants to write to us */
676 dev_dbg(&i2c_imx->adapter.dev, "write requested");
> 677 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
678
679 /* Slave receive */
680 ctl &= ~I2CR_MTX;
681 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
682 /* Dummy read */
683 imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
684 }
685 } else if (!(ctl & I2CR_MTX)) { /* Receive mode */
686 if (status & I2SR_IBB) { /* No STOP signal detected */
687 value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
> 688 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
689 } else { /* STOP signal is detected */
690 dev_dbg(&i2c_imx->adapter.dev,
691 "STOP signal detected");
> 692 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
693 }
694 } else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
695 ctl |= I2CR_MTX;
696 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
697
> 698 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
699
700 imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
701 } else { /* Transmit mode received NAK */
702 ctl &= ~I2CR_MTX;
703 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
704 imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
705 }
706
707 return IRQ_HANDLED;
708 }
709
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 39787 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2020-10-19 15:46 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-10-16 8:24 [v7] i2c: imx: support slave mode for imx I2C driver Biwen Li
2020-10-16 8:24 ` Biwen Li
2020-10-19 15:43 ` kernel test robot
2020-10-19 15:46 ` kernel test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.