2019-08-08 04:04:27

by Biwen Li

[permalink] [raw]
Subject: i2c: imx: support slave mode for imx I2C driver

The patch supports slave mode for imx I2C driver

Signed-off-by: Biwen Li <[email protected]>
---
drivers/i2c/busses/i2c-imx.c | 199 ++++++++++++++++++++++++++++++++---
1 file changed, 185 insertions(+), 14 deletions(-)

diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index b1b8b938d7f4..f7583a9fa56f 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -202,6 +202,9 @@ struct imx_i2c_struct {
struct pinctrl_state *pinctrl_pins_gpio;

struct imx_i2c_dma *dma;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ struct i2c_client *slave;
+#endif /* CONFIG_I2C_SLAVE */
};

static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
@@ -583,23 +586,40 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
}

-static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
+/* Clear interrupt flag bit */
+static void i2c_imx_clr_if_bit(struct imx_i2c_struct *i2c_imx)
{
- struct imx_i2c_struct *i2c_imx = dev_id;
- unsigned int temp;
+ unsigned int status;

- 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;
- }
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ 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(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int status;
+
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ status &= ~I2SR_IAL;
+ imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
+}

- return IRQ_NONE;
+static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int status;
+
+ dev_dbg(&i2c_imx->adapter.dev, "<%s>: master interrupt\n", __func__);
+
+ /* Save status register */
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ i2c_imx->i2csr = status | I2SR_IIF;
+
+ wake_up(&i2c_imx->queue);
+
+ return IRQ_HANDLED;
}

static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
@@ -1043,11 +1063,162 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}

+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int temp;
+
+ dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
+
+ /* Set slave addr. */
+ imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
+
+ /* Disable i2c module */
+ temp = i2c_imx->hwdata->i2cr_ien_opcode
+ ^ I2CR_IEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /* Reset status register */
+ imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
+ IMX_I2C_I2SR);
+
+ /* Enable module and enable interrupt from i2c module */
+ temp = i2c_imx->hwdata->i2cr_ien_opcode
+ | I2CR_IIEN;
+ imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
+
+ /* Wait controller to be stable */
+ usleep_range(50, 150);
+}
+
+static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int status, ctl;
+ u8 value;
+
+ if (!i2c_imx->slave) {
+ dev_err(&i2c_imx->adapter.dev, "cannot deal with slave irq,i2c_imx->slave is null");
+ return IRQ_NONE;
+ }
+
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
+ if (status & I2SR_IAL) { /* Arbitration lost */
+ i2c_imx_clr_al_bit(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 transimt */
+ 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 */
+ value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ }
+ } else {
+ if (!(ctl & I2CR_MTX)) { /* Receive mode */
+ if (status & I2SR_IBB) { /* No STOP signal detected */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+
+ 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 { /* Transmit mode */
+ if (!(status & I2SR_RXAK)) { /* 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 { /* Received NAK */
+ ctl &= ~I2CR_MTX;
+ imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
+ value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
+ }
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static int i2c_imx_reg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+
+ if (i2c_imx->slave)
+ return -EINVAL;
+
+ dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
+ i2c_imx->slave = client;
+
+ i2c_imx_slave_init(i2c_imx);
+
+ return 0;
+}
+
+static int i2c_imx_unreg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+
+ if (!i2c_imx->slave)
+ return -EINVAL;
+
+ i2c_imx->slave = NULL;
+
+ return 0;
+}
+#endif /* CONFIG_I2C_SLAVE */
+
static const struct i2c_algorithm i2c_imx_algo = {
.master_xfer = i2c_imx_xfer,
.functionality = i2c_imx_func,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ .reg_slave = i2c_imx_reg_slave,
+ .unreg_slave = i2c_imx_unreg_slave,
+#endif /* CONFIG_I2C_SLAVE */
};

+static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
+{
+ struct imx_i2c_struct *i2c_imx = dev_id;
+ unsigned int status, ctl;
+ irqreturn_t irq_status = IRQ_NONE;
+
+ 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(i2c_imx);
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+ if (ctl & I2CR_MSTA)
+ irq_status = i2c_imx_master_isr(i2c_imx);
+ else
+ irq_status = i2c_imx_slave_isr(i2c_imx);
+#else
+ irq_status = i2c_imx_master_isr(i2c_imx);
+
+#endif /* CONFIG_I2C_SLAVE */
+ }
+
+ return irq_status;
+}
+
static int i2c_imx_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
--
2.17.1


2019-08-08 15:00:13

by Sascha Hauer

[permalink] [raw]
Subject: Re: i2c: imx: support slave mode for imx I2C driver

Hi,

On Thu, Aug 08, 2019 at 11:53:43AM +0800, Biwen Li wrote:
> The patch supports slave mode for imx I2C driver
>
> Signed-off-by: Biwen Li <[email protected]>
> ---
> drivers/i2c/busses/i2c-imx.c | 199 ++++++++++++++++++++++++++++++++---
> 1 file changed, 185 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
> index b1b8b938d7f4..f7583a9fa56f 100644
> --- a/drivers/i2c/busses/i2c-imx.c
> +++ b/drivers/i2c/busses/i2c-imx.c
> @@ -202,6 +202,9 @@ struct imx_i2c_struct {
> struct pinctrl_state *pinctrl_pins_gpio;
>
> struct imx_i2c_dma *dma;
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + struct i2c_client *slave;
> +#endif /* CONFIG_I2C_SLAVE */

Other drivers just do a "select I2C_SLAVE" in Kconfig to get rid of
these #ifs. We should do the same.

> };
>
> static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
> @@ -583,23 +586,40 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
> imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> }
>
> -static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
> +/* Clear interrupt flag bit */
> +static void i2c_imx_clr_if_bit(struct imx_i2c_struct *i2c_imx)
> {
> - struct imx_i2c_struct *i2c_imx = dev_id;
> - unsigned int temp;
> + unsigned int status;
>
> - 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;
> - }
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + 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(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int status;
> +
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + status &= ~I2SR_IAL;
> + imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
> +}
>
> - return IRQ_NONE;
> +static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int status;
> +
> + dev_dbg(&i2c_imx->adapter.dev, "<%s>: master interrupt\n", __func__);

Generally this driver has way too many dev_dbg spread around in hot
pathes already. IMO adding more doesn't make the output more useful.

> +
> + /* Save status register */
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + i2c_imx->i2csr = status | I2SR_IIF;
> +
> + wake_up(&i2c_imx->queue);
> +
> + return IRQ_HANDLED;
> }
>
> static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
> @@ -1043,11 +1063,162 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
> | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
> }
>
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int temp;
> +
> + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
> +
> + /* Set slave addr. */
> + imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
> +
> + /* Disable i2c module */
> + temp = i2c_imx->hwdata->i2cr_ien_opcode
> + ^ I2CR_IEN;

unnecessary line break.

> + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> +
> + /* Reset status register */
> + imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
> + IMX_I2C_I2SR);
> +
> + /* Enable module and enable interrupt from i2c module */
> + temp = i2c_imx->hwdata->i2cr_ien_opcode
> + | I2CR_IIEN;

ditto.

> + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> +
> + /* Wait controller to be stable */
> + usleep_range(50, 150);
> +}
> +
> +static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int status, ctl;
> + u8 value;
> +
> + if (!i2c_imx->slave) {
> + dev_err(&i2c_imx->adapter.dev, "cannot deal with slave irq,i2c_imx->slave is null");
> + return IRQ_NONE;
> + }
> +
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
> + if (status & I2SR_IAL) { /* Arbitration lost */
> + i2c_imx_clr_al_bit(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 transimt */

s/transimt/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 */
> + value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);

value is unused, no need to assign a value to it.

> + }
> + } else {
> + if (!(ctl & I2CR_MTX)) { /* Receive mode */

Since you have an 'else' path please convert this to positive logic.
This makes it easier to read.

> + if (status & I2SR_IBB) { /* No STOP signal detected */
> + ctl &= ~I2CR_MTX;
> + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
> +
> + 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 { /* Transmit mode */
> + if (!(status & I2SR_RXAK)) { /* Received ACK */

Same here.

> + 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 { /* Received NAK */
> + ctl &= ~I2CR_MTX;
> + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
> + value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);

Also value unused.

> + }
> + }
> + }
> + return IRQ_HANDLED;
> +}
> +
> +static int i2c_imx_reg_slave(struct i2c_client *client)
> +{
> + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
> +
> + if (i2c_imx->slave)
> + return -EINVAL;

Better -EBUSY?

Sascha

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2019-08-08 20:03:41

by Wolfram Sang

[permalink] [raw]
Subject: Re: i2c: imx: support slave mode for imx I2C driver

On Thu, Aug 08, 2019 at 11:53:43AM +0800, Biwen Li wrote:
> The patch supports slave mode for imx I2C driver
>
> Signed-off-by: Biwen Li <[email protected]>

Wow, this is much simpler than the other approach flying around:

http://patchwork.ozlabs.org/patch/1124048/

Can this one be master and slave on the same bus, too?

CCing the author of the other patch.

> ---
> drivers/i2c/busses/i2c-imx.c | 199 ++++++++++++++++++++++++++++++++---
> 1 file changed, 185 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
> index b1b8b938d7f4..f7583a9fa56f 100644
> --- a/drivers/i2c/busses/i2c-imx.c
> +++ b/drivers/i2c/busses/i2c-imx.c
> @@ -202,6 +202,9 @@ struct imx_i2c_struct {
> struct pinctrl_state *pinctrl_pins_gpio;
>
> struct imx_i2c_dma *dma;
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + struct i2c_client *slave;
> +#endif /* CONFIG_I2C_SLAVE */
> };
>
> static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
> @@ -583,23 +586,40 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
> imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> }
>
> -static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
> +/* Clear interrupt flag bit */
> +static void i2c_imx_clr_if_bit(struct imx_i2c_struct *i2c_imx)
> {
> - struct imx_i2c_struct *i2c_imx = dev_id;
> - unsigned int temp;
> + unsigned int status;
>
> - 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;
> - }
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + 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(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int status;
> +
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + status &= ~I2SR_IAL;
> + imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
> +}
>
> - return IRQ_NONE;
> +static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int status;
> +
> + dev_dbg(&i2c_imx->adapter.dev, "<%s>: master interrupt\n", __func__);
> +
> + /* Save status register */
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + i2c_imx->i2csr = status | I2SR_IIF;
> +
> + wake_up(&i2c_imx->queue);
> +
> + return IRQ_HANDLED;
> }
>
> static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
> @@ -1043,11 +1063,162 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
> | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
> }
>
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int temp;
> +
> + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
> +
> + /* Set slave addr. */
> + imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
> +
> + /* Disable i2c module */
> + temp = i2c_imx->hwdata->i2cr_ien_opcode
> + ^ I2CR_IEN;
> + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> +
> + /* Reset status register */
> + imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
> + IMX_I2C_I2SR);
> +
> + /* Enable module and enable interrupt from i2c module */
> + temp = i2c_imx->hwdata->i2cr_ien_opcode
> + | I2CR_IIEN;
> + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> +
> + /* Wait controller to be stable */
> + usleep_range(50, 150);
> +}
> +
> +static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
> +{
> + unsigned int status, ctl;
> + u8 value;
> +
> + if (!i2c_imx->slave) {
> + dev_err(&i2c_imx->adapter.dev, "cannot deal with slave irq,i2c_imx->slave is null");
> + return IRQ_NONE;
> + }
> +
> + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
> + if (status & I2SR_IAL) { /* Arbitration lost */
> + i2c_imx_clr_al_bit(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 transimt */
> + 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 */
> + value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
> + }
> + } else {
> + if (!(ctl & I2CR_MTX)) { /* Receive mode */
> + if (status & I2SR_IBB) { /* No STOP signal detected */
> + ctl &= ~I2CR_MTX;
> + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
> +
> + 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 { /* Transmit mode */
> + if (!(status & I2SR_RXAK)) { /* 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 { /* Received NAK */
> + ctl &= ~I2CR_MTX;
> + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
> + value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
> + }
> + }
> + }
> + return IRQ_HANDLED;
> +}
> +
> +static int i2c_imx_reg_slave(struct i2c_client *client)
> +{
> + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
> +
> + if (i2c_imx->slave)
> + return -EINVAL;
> +
> + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
> + i2c_imx->slave = client;
> +
> + i2c_imx_slave_init(i2c_imx);
> +
> + return 0;
> +}
> +
> +static int i2c_imx_unreg_slave(struct i2c_client *client)
> +{
> + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
> +
> + if (!i2c_imx->slave)
> + return -EINVAL;
> +
> + i2c_imx->slave = NULL;
> +
> + return 0;
> +}
> +#endif /* CONFIG_I2C_SLAVE */
> +
> static const struct i2c_algorithm i2c_imx_algo = {
> .master_xfer = i2c_imx_xfer,
> .functionality = i2c_imx_func,
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + .reg_slave = i2c_imx_reg_slave,
> + .unreg_slave = i2c_imx_unreg_slave,
> +#endif /* CONFIG_I2C_SLAVE */
> };
>
> +static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
> +{
> + struct imx_i2c_struct *i2c_imx = dev_id;
> + unsigned int status, ctl;
> + irqreturn_t irq_status = IRQ_NONE;
> +
> + 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(i2c_imx);
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> + if (ctl & I2CR_MSTA)
> + irq_status = i2c_imx_master_isr(i2c_imx);
> + else
> + irq_status = i2c_imx_slave_isr(i2c_imx);
> +#else
> + irq_status = i2c_imx_master_isr(i2c_imx);
> +
> +#endif /* CONFIG_I2C_SLAVE */
> + }
> +
> + return irq_status;
> +}
> +
> static int i2c_imx_probe(struct platform_device *pdev)
> {
> const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
> --
> 2.17.1
>


Attachments:
(No filename) (7.72 kB)
signature.asc (849.00 B)
Download all attachments

2019-08-09 03:20:57

by Biwen Li

[permalink] [raw]
Subject: RE: [EXT] Re: i2c: imx: support slave mode for imx I2C driver

> > The patch supports slave mode for imx I2C driver
> >
> > Signed-off-by: Biwen Li <[email protected]>
>
> Wow, this is much simpler than the other approach flying around:
>
> http://patchwork.ozlabs.org/patch/1124048/
>
> Can this one be master and slave on the same bus, too?
At the same time, the same bus is in master mode or slave mode.
>
> CCing the author of the other patch.
>
> > ---
> > drivers/i2c/busses/i2c-imx.c | 199
> > ++++++++++++++++++++++++++++++++---
> > 1 file changed, 185 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/i2c/busses/i2c-imx.c
> > b/drivers/i2c/busses/i2c-imx.c index b1b8b938d7f4..f7583a9fa56f 100644
> > --- a/drivers/i2c/busses/i2c-imx.c
> > +++ b/drivers/i2c/busses/i2c-imx.c
> > @@ -202,6 +202,9 @@ struct imx_i2c_struct {
> > struct pinctrl_state *pinctrl_pins_gpio;
> >
> > struct imx_i2c_dma *dma;
> > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > + struct i2c_client *slave;
> > +#endif /* CONFIG_I2C_SLAVE */
> > };
> >
> > static const struct imx_i2c_hwdata imx1_i2c_hwdata = { @@ -583,23
> > +586,40 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
> > imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); }
> >
> > -static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
> > +/* Clear interrupt flag bit */
> > +static void i2c_imx_clr_if_bit(struct imx_i2c_struct *i2c_imx)
> > {
> > - struct imx_i2c_struct *i2c_imx = dev_id;
> > - unsigned int temp;
> > + unsigned int status;
> >
> > - 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;
> > - }
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + 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(struct imx_i2c_struct *i2c_imx) {
> > + unsigned int status;
> > +
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + status &= ~I2SR_IAL;
> > + imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR); }
> >
> > - return IRQ_NONE;
> > +static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx)
> > +{
> > + unsigned int status;
> > +
> > + dev_dbg(&i2c_imx->adapter.dev, "<%s>: master interrupt\n",
> > +__func__);
> > +
> > + /* Save status register */
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + i2c_imx->i2csr = status | I2SR_IIF;
> > +
> > + wake_up(&i2c_imx->queue);
> > +
> > + return IRQ_HANDLED;
> > }
> >
> > static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, @@
> > -1043,11 +1063,162 @@ static u32 i2c_imx_func(struct i2c_adapter
> *adapter)
> > | I2C_FUNC_SMBUS_READ_BLOCK_DATA;
> > }
> >
> > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > +static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx) {
> > + unsigned int temp;
> > +
> > + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
> > +
> > + /* Set slave addr. */
> > + imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx,
> > +IMX_I2C_IADR);
> > +
> > + /* Disable i2c module */
> > + temp = i2c_imx->hwdata->i2cr_ien_opcode
> > + ^ I2CR_IEN;
> > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> > +
> > + /* Reset status register */
> > + imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
> > + IMX_I2C_I2SR);
> > +
> > + /* Enable module and enable interrupt from i2c module */
> > + temp = i2c_imx->hwdata->i2cr_ien_opcode
> > + | I2CR_IIEN;
> > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> > +
> > + /* Wait controller to be stable */
> > + usleep_range(50, 150);
> > +}
> > +
> > +static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
> > +{
> > + unsigned int status, ctl;
> > + u8 value;
> > +
> > + if (!i2c_imx->slave) {
> > + dev_err(&i2c_imx->adapter.dev, "cannot deal with slave
> irq,i2c_imx->slave is null");
> > + return IRQ_NONE;
> > + }
> > +
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
> > + if (status & I2SR_IAL) { /* Arbitration lost */
> > + i2c_imx_clr_al_bit(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 transimt */
> > + 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 */
> > + value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
> > + }
> > + } else {
> > + if (!(ctl & I2CR_MTX)) { /* Receive mode */
> > + if (status & I2SR_IBB) { /* No STOP signal detected */
> > + ctl &= ~I2CR_MTX;
> > + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
> > +
> > + 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 { /* Transmit mode */
> > + if (!(status & I2SR_RXAK)) { /* 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 { /* Received NAK */
> > + ctl &= ~I2CR_MTX;
> > + imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
> > + value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
> > + }
> > + }
> > + }
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int i2c_imx_reg_slave(struct i2c_client *client) {
> > + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
> > +
> > + if (i2c_imx->slave)
> > + return -EINVAL;
> > +
> > + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
> > + i2c_imx->slave = client;
> > +
> > + i2c_imx_slave_init(i2c_imx);
> > +
> > + return 0;
> > +}
> > +
> > +static int i2c_imx_unreg_slave(struct i2c_client *client) {
> > + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
> > +
> > + if (!i2c_imx->slave)
> > + return -EINVAL;
> > +
> > + i2c_imx->slave = NULL;
> > +
> > + return 0;
> > +}
> > +#endif /* CONFIG_I2C_SLAVE */
> > +
> > static const struct i2c_algorithm i2c_imx_algo = {
> > .master_xfer = i2c_imx_xfer,
> > .functionality = i2c_imx_func,
> > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > + .reg_slave = i2c_imx_reg_slave,
> > + .unreg_slave = i2c_imx_unreg_slave,
> > +#endif /* CONFIG_I2C_SLAVE */
> > };
> >
> > +static irqreturn_t i2c_imx_isr(int irq, void *dev_id) {
> > + struct imx_i2c_struct *i2c_imx = dev_id;
> > + unsigned int status, ctl;
> > + irqreturn_t irq_status = IRQ_NONE;
> > +
> > + 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(i2c_imx);
> > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > + if (ctl & I2CR_MSTA)
> > + irq_status = i2c_imx_master_isr(i2c_imx);
> > + else
> > + irq_status = i2c_imx_slave_isr(i2c_imx); #else
> > + irq_status = i2c_imx_master_isr(i2c_imx);
> > +
> > +#endif /* CONFIG_I2C_SLAVE */
> > + }
> > +
> > + return irq_status;
> > +}
> > +
> > static int i2c_imx_probe(struct platform_device *pdev) {
> > const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
> > --
> > 2.17.1
> >

2019-08-09 04:07:28

by Biwen Li

[permalink] [raw]
Subject: RE: [EXT] Re: i2c: imx: support slave mode for imx I2C driver

>
> Hi,
>
> On Thu, Aug 08, 2019 at 11:53:43AM +0800, Biwen Li wrote:
> > The patch supports slave mode for imx I2C driver
> >
> > Signed-off-by: Biwen Li <[email protected]>
> > ---
> > drivers/i2c/busses/i2c-imx.c | 199
> > ++++++++++++++++++++++++++++++++---
> > 1 file changed, 185 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/i2c/busses/i2c-imx.c
> > b/drivers/i2c/busses/i2c-imx.c index b1b8b938d7f4..f7583a9fa56f 100644
> > --- a/drivers/i2c/busses/i2c-imx.c
> > +++ b/drivers/i2c/busses/i2c-imx.c
> > @@ -202,6 +202,9 @@ struct imx_i2c_struct {
> > struct pinctrl_state *pinctrl_pins_gpio;
> >
> > struct imx_i2c_dma *dma;
> > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > + struct i2c_client *slave;
> > +#endif /* CONFIG_I2C_SLAVE */
>
> Other drivers just do a "select I2C_SLAVE" in Kconfig to get rid of these #ifs. We
> should do the same.
Hi sascha, I don't know your meaning, could you let it clearer?
>
> > };
> >
> > static const struct imx_i2c_hwdata imx1_i2c_hwdata = { @@ -583,23
> > +586,40 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
> > imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); }
> >
> > -static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
> > +/* Clear interrupt flag bit */
> > +static void i2c_imx_clr_if_bit(struct imx_i2c_struct *i2c_imx)
> > {
> > - struct imx_i2c_struct *i2c_imx = dev_id;
> > - unsigned int temp;
> > + unsigned int status;
> >
> > - 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;
> > - }
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + 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(struct imx_i2c_struct *i2c_imx) {
> > + unsigned int status;
> > +
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + status &= ~I2SR_IAL;
> > + imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR); }
> >
> > - return IRQ_NONE;
> > +static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx)
> > +{
> > + unsigned int status;
> > +
> > + dev_dbg(&i2c_imx->adapter.dev, "<%s>: master interrupt\n",
> > + __func__);
>
> Generally this driver has way too many dev_dbg spread around in hot pathes
> already. IMO adding more doesn't make the output more useful.
Ok, got it. I will delete it in v2.
>
> > +
> > + /* Save status register */
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + i2c_imx->i2csr = status | I2SR_IIF;
> > +
> > + wake_up(&i2c_imx->queue);
> > +
> > + return IRQ_HANDLED;
> > }
> >
> > static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, @@
> > -1043,11 +1063,162 @@ static u32 i2c_imx_func(struct i2c_adapter
> *adapter)
> > | I2C_FUNC_SMBUS_READ_BLOCK_DATA; }
> >
> > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > +static void i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx) {
> > + unsigned int temp;
> > +
> > + dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
> > +
> > + /* Set slave addr. */
> > + imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx,
> > + IMX_I2C_IADR);
> > +
> > + /* Disable i2c module */
> > + temp = i2c_imx->hwdata->i2cr_ien_opcode
> > + ^ I2CR_IEN;
>
> unnecessary line break.
Ok, no problem. I will remove the line break in v2.
>
> > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> > +
> > + /* Reset status register */
> > + imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
> > + IMX_I2C_I2SR);
> > +
> > + /* Enable module and enable interrupt from i2c module */
> > + temp = i2c_imx->hwdata->i2cr_ien_opcode
> > + | I2CR_IIEN;
>
> ditto.
Ok. I will remove the line break in v2.
>
> > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> > +
> > + /* Wait controller to be stable */
> > + usleep_range(50, 150);
> > +}
> > +
> > +static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
> > +{
> > + unsigned int status, ctl;
> > + u8 value;
> > +
> > + if (!i2c_imx->slave) {
> > + dev_err(&i2c_imx->adapter.dev, "cannot deal with slave
> irq,i2c_imx->slave is null");
> > + return IRQ_NONE;
> > + }
> > +
> > + status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
> > + ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
> > + if (status & I2SR_IAL) { /* Arbitration lost */
> > + i2c_imx_clr_al_bit(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 transimt */
>
> s/transimt/transmit/
You are right. I will correct it in v2.
>
> > + 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 */
> > + value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
>
> value is unused, no need to assign a value to it.
Ok, I will correct this in v2.
>
> > + }
> > + } else {
> > + if (!(ctl & I2CR_MTX)) { /* Receive mode */
>
> Since you have an 'else' path please convert this to positive logic.
> This makes it easier to read.
Ok,got it, I will convert this to positive logic in v2.
>
> > + if (status & I2SR_IBB) { /* No STOP signal detected
> */
> > + ctl &= ~I2CR_MTX;
> > + imx_i2c_write_reg(ctl, i2c_imx,
> > + IMX_I2C_I2CR);
> > +
> > + 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 { /* Transmit mode */
> > + if (!(status & I2SR_RXAK)) { /* Received ACK */
>
> Same here.
Ok,got it, I will convert this to positive logic in v2.
>
> > + 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 { /* Received NAK */
> > + ctl &= ~I2CR_MTX;
> > + imx_i2c_write_reg(ctl, i2c_imx,
> IMX_I2C_I2CR);
> > + value = imx_i2c_read_reg(i2c_imx,
> > + IMX_I2C_I2DR);
>
> Also value unused.
Ok,got it, I will correct this in v2.
>
> > + }
> > + }
> > + }
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int i2c_imx_reg_slave(struct i2c_client *client) {
> > + struct imx_i2c_struct *i2c_imx =
> > +i2c_get_adapdata(client->adapter);
> > +
> > + if (i2c_imx->slave)
> > + return -EINVAL;
>
> Better -EBUSY?
Yes, you are right, I will correct it in v2.
>
> Sascha
>
> --
> Pengutronix e.K. |
> |
> Industrial Linux Solutions |
> https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.pe
> ngutronix.de%2F&amp;data=02%7C01%7Cbiwen.li%40nxp.com%7C6741b4f5
> b14646209b6d08d71c0b46cf%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C
> 0%7C0%7C637008707067019414&amp;sdata=b95HQ8KRhoLS8R%2BMWyaF
> FTo%2BOr9qqVM45nTn8OoboPg%3D&amp;reserved=0 |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0
> |
> Amtsgericht Hildesheim, HRA 2686 | Fax:
> +49-5121-206917-5555 |

2019-08-09 15:12:08

by Wolfram Sang

[permalink] [raw]
Subject: Re: [EXT] Re: i2c: imx: support slave mode for imx I2C driver

On Fri, Aug 09, 2019 at 03:18:01AM +0000, Biwen Li wrote:
> > > The patch supports slave mode for imx I2C driver
> > >
> > > Signed-off-by: Biwen Li <[email protected]>
> >
> > Wow, this is much simpler than the other approach flying around:
> >
> > http://patchwork.ozlabs.org/patch/1124048/
> >
> > Can this one be master and slave on the same bus, too?
> At the same time, the same bus is in master mode or slave mode.

So, can someone kindly point out the key differences to me?


Attachments:
(No filename) (500.00 B)
signature.asc (849.00 B)
Download all attachments

2019-08-12 04:23:03

by Biwen Li

[permalink] [raw]
Subject: RE: [EXT] Re: i2c: imx: support slave mode for imx I2C driver

> On Fri, Aug 09, 2019 at 03:18:01AM +0000, Biwen Li wrote:
> > > > The patch supports slave mode for imx I2C driver
> > > >
> > > > Signed-off-by: Biwen Li <[email protected]>
> > >
> > > Wow, this is much simpler than the other approach flying around:
> > >
> > > http://patchwork.ozlabs.org/patch/1124048/
> > >
> > > Can this one be master and slave on the same bus, too?
> > At the same time, the same bus is in master mode or slave mode.
>
> So, can someone kindly point out the key differences to me?
The I2C module cannot be a master and a slave simultaneously.
Such as: if the i2c module is used in slave mode with command
'echo slave-24c02 0x64 > /sys/bus/i2c/devices/i2c-0/new_device',
At the mean time, you cannot do master operations(i2cset/i2cget).
You can switch mode from slave to master with command 'echo 0x64
> /sys/bus/i2c/devices/i2c-0/delete_device', then you can do master
operations.

Adding Leo to this version too.

2019-08-12 06:39:50

by Sascha Hauer

[permalink] [raw]
Subject: Re: [EXT] Re: i2c: imx: support slave mode for imx I2C driver

On Fri, Aug 09, 2019 at 04:04:45AM +0000, Biwen Li wrote:
> >
> > Hi,
> >
> > On Thu, Aug 08, 2019 at 11:53:43AM +0800, Biwen Li wrote:
> > > The patch supports slave mode for imx I2C driver
> > >
> > > Signed-off-by: Biwen Li <[email protected]>
> > > ---
> > > drivers/i2c/busses/i2c-imx.c | 199
> > > ++++++++++++++++++++++++++++++++---
> > > 1 file changed, 185 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/drivers/i2c/busses/i2c-imx.c
> > > b/drivers/i2c/busses/i2c-imx.c index b1b8b938d7f4..f7583a9fa56f 100644
> > > --- a/drivers/i2c/busses/i2c-imx.c
> > > +++ b/drivers/i2c/busses/i2c-imx.c
> > > @@ -202,6 +202,9 @@ struct imx_i2c_struct {
> > > struct pinctrl_state *pinctrl_pins_gpio;
> > >
> > > struct imx_i2c_dma *dma;
> > > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > > + struct i2c_client *slave;
> > > +#endif /* CONFIG_I2C_SLAVE */
> >
> > Other drivers just do a "select I2C_SLAVE" in Kconfig to get rid of these #ifs. We
> > should do the same.
> Hi sascha, I don't know your meaning, could you let it clearer?

Other drivers have "select I2C_SLAVE" in Kconfig, so they do not need
any #ifdef CONFIG_I2C_SLAVE as this is always true.

Sascha

--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |

2019-08-12 07:12:14

by Biwen Li

[permalink] [raw]
Subject: RE: [EXT] Re: i2c: imx: support slave mode for imx I2C driver

> Subject: Re: [EXT] Re: i2c: imx: support slave mode for imx I2C driver
>
> Caution: EXT Email
>
> On Fri, Aug 09, 2019 at 04:04:45AM +0000, Biwen Li wrote:
> > >
> > > Hi,
> > >
> > > On Thu, Aug 08, 2019 at 11:53:43AM +0800, Biwen Li wrote:
> > > > The patch supports slave mode for imx I2C driver
> > > >
> > > > Signed-off-by: Biwen Li <[email protected]>
> > > > ---
> > > > drivers/i2c/busses/i2c-imx.c | 199
> > > > ++++++++++++++++++++++++++++++++---
> > > > 1 file changed, 185 insertions(+), 14 deletions(-)
> > > >
> > > > diff --git a/drivers/i2c/busses/i2c-imx.c
> > > > b/drivers/i2c/busses/i2c-imx.c index b1b8b938d7f4..f7583a9fa56f
> > > > 100644
> > > > --- a/drivers/i2c/busses/i2c-imx.c
> > > > +++ b/drivers/i2c/busses/i2c-imx.c
> > > > @@ -202,6 +202,9 @@ struct imx_i2c_struct {
> > > > struct pinctrl_state *pinctrl_pins_gpio;
> > > >
> > > > struct imx_i2c_dma *dma;
> > > > +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> > > > + struct i2c_client *slave;
> > > > +#endif /* CONFIG_I2C_SLAVE */
> > >
> > > Other drivers just do a "select I2C_SLAVE" in Kconfig to get rid of
> > > these #ifs. We should do the same.
> > Hi sascha, I don't know your meaning, could you let it clearer?
>
> Other drivers have "select I2C_SLAVE" in Kconfig, so they do not need any #ifdef
> CONFIG_I2C_SLAVE as this is always true.
Okay, sascha, got it, I will remove the option in v2, thanks.
>
> Sascha
>
> --
> Pengutronix e.K. |
> |
> Industrial Linux Solutions |
> https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.pe
> ngutronix.de%2F&amp;data=02%7C01%7Cbiwen.li%40nxp.com%7C2ad7a8ef
> 04c84f224a9808d71eefaf43%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0
> %7C0%7C637011887098895560&amp;sdata=Dde7Hi6eMWcg7SM8T63eBtgY
> XsQPu3HFJaZMt6z8Vck%3D&amp;reserved=0 |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0
> |
> Amtsgericht Hildesheim, HRA 2686 | Fax:
> +49-5121-206917-5555 |