2019-10-25 19:36:07

by Biwen Li

[permalink] [raw]
Subject: [v3] 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]>
---
Change in v3:
- support layerscape and i.mx platform

Change in v2:
- remove MACRO CONFIG_I2C_SLAVE

drivers/i2c/busses/i2c-imx.c | 209 ++++++++++++++++++++++++++++++++---
1 file changed, 191 insertions(+), 18 deletions(-)

diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index a3b61336fe55..a91bf122f66c 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -203,6 +203,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 = {
@@ -279,6 +280,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)
@@ -588,23 +597,33 @@ 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(unsigned int status, struct imx_i2c_struct *i2c_imx)
{
- struct imx_i2c_struct *i2c_imx = dev_id;
- unsigned int temp;
+ status &= ~I2SR_IIF;
+ status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
+ imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
+}

- 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;
- }
+/* 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_master_isr(struct imx_i2c_struct *i2c_imx)
+{
+ unsigned int status;
+
+ /* Save status register */
+ status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ i2c_imx->i2csr = status | I2SR_IIF;

- return IRQ_NONE;
+ wake_up(&i2c_imx->queue);
+
+ return IRQ_HANDLED;
}

static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
@@ -900,6 +919,11 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,

dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

+ if (i2c_imx->slave) {
+ dev_err(&i2c_imx->adapter.dev, "Please not do operations of master mode in slave mode");
+ return -EBUSY;
+ }
+
result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
if (result < 0)
goto out;
@@ -1048,11 +1072,163 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}

+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);
+
+ /* Wait controller to be stable */
+ usleep_range(50, 150);
+ return 0;
+}
+
+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(status, 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 */
+ 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 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 int i2c_imx_reg_slave(struct i2c_client *client)
+{
+ struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
+ int ret;
+ 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 (!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,
.functionality = i2c_imx_func,
+ .reg_slave = i2c_imx_reg_slave,
+ .unreg_slave = i2c_imx_unreg_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(status, i2c_imx);
+ if (ctl & I2CR_MSTA)
+ irq_status = i2c_imx_master_isr(i2c_imx);
+ else
+ irq_status = i2c_imx_slave_isr(i2c_imx);
+ }
+
+ return irq_status;
+}
+
static int i2c_imx_probe(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx;
@@ -1148,10 +1324,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


2019-10-28 16:31:14

by kernel test robot

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

Hi Biwen,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on wsa/i2c/for-next]
[also build test ERROR on v5.4-rc5 next-20191025]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url: https://github.com/0day-ci/linux/commits/Biwen-Li/i2c-imx-support-slave-mode-for-imx-I2C-driver/20191028-135008
base: https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
config: arm-imx_v6_v7_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 7.4.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.4.0 make.cross ARCH=arm

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>

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:1121:4: error: implicit declaration of function 'i2c_slave_event'; did you mean 'io_wait_event'? [-Werror=implicit-function-declaration]
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
^~~~~~~~~~~~~~~
io_wait_event
>> drivers/i2c/busses/i2c-imx.c:1121:36: error: 'I2C_SLAVE_READ_REQUESTED' undeclared (first use in this function); did you mean 'I2C_CLASS_DEPRECATED'?
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
^~~~~~~~~~~~~~~~~~~~~~~~
I2C_CLASS_DEPRECATED
drivers/i2c/busses/i2c-imx.c:1121:36: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/i2c/busses/i2c-imx.c:1131:36: error: 'I2C_SLAVE_WRITE_REQUESTED' undeclared (first use in this function); did you mean 'I2C_SLAVE_READ_REQUESTED'?
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
^~~~~~~~~~~~~~~~~~~~~~~~~
I2C_SLAVE_READ_REQUESTED
>> drivers/i2c/busses/i2c-imx.c:1145:37: error: 'I2C_SLAVE_WRITE_RECEIVED' undeclared (first use in this function); did you mean 'I2C_SLAVE_WRITE_REQUESTED'?
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
^~~~~~~~~~~~~~~~~~~~~~~~
I2C_SLAVE_WRITE_REQUESTED
>> drivers/i2c/busses/i2c-imx.c:1149:37: error: 'I2C_SLAVE_STOP' undeclared (first use in this function); did you mean 'I2C_M_STOP'?
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
^~~~~~~~~~~~~~
I2C_M_STOP
>> drivers/i2c/busses/i2c-imx.c:1155:35: error: 'I2C_SLAVE_READ_PROCESSED' undeclared (first use in this function); did you mean 'I2C_SLAVE_READ_REQUESTED'?
i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
^~~~~~~~~~~~~~~~~~~~~~~~
I2C_SLAVE_READ_REQUESTED
drivers/i2c/busses/i2c-imx.c: At top level:
>> drivers/i2c/busses/i2c-imx.c:1208:3: error: 'const struct i2c_algorithm' has no member named 'reg_slave'
.reg_slave = i2c_imx_reg_slave,
^~~~~~~~~
>> drivers/i2c/busses/i2c-imx.c:1208:15: warning: excess elements in struct initializer
.reg_slave = i2c_imx_reg_slave,
^~~~~~~~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c:1208:15: note: (near initialization for 'i2c_imx_algo')
>> drivers/i2c/busses/i2c-imx.c:1209:3: error: 'const struct i2c_algorithm' has no member named 'unreg_slave'
.unreg_slave = i2c_imx_unreg_slave,
^~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c:1209:17: warning: excess elements in struct initializer
.unreg_slave = i2c_imx_unreg_slave,
^~~~~~~~~~~~~~~~~~~
drivers/i2c/busses/i2c-imx.c:1209:17: note: (near initialization for 'i2c_imx_algo')
cc1: some warnings being treated as errors

vim +1121 drivers/i2c/busses/i2c-imx.c

1103
1104 static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
1105 {
1106 unsigned int status, ctl;
1107 u8 value;
1108
1109 if (!i2c_imx->slave) {
1110 dev_err(&i2c_imx->adapter.dev, "cannot deal with slave irq,i2c_imx->slave is null");
1111 return IRQ_NONE;
1112 }
1113
1114 status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
1115 ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
1116 if (status & I2SR_IAL) { /* Arbitration lost */
1117 i2c_imx_clr_al_bit(status, i2c_imx);
1118 } else if (status & I2SR_IAAS) { /* Addressed as a slave */
1119 if (status & I2SR_SRW) { /* Master wants to read from us*/
1120 dev_dbg(&i2c_imx->adapter.dev, "read requested");
> 1121 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
1122
1123 /* Slave transmit */
1124 ctl |= I2CR_MTX;
1125 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
1126
1127 /* Send data */
1128 imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
1129 } else { /* Master wants to write to us */
1130 dev_dbg(&i2c_imx->adapter.dev, "write requested");
> 1131 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
1132
1133 /* Slave receive */
1134 ctl &= ~I2CR_MTX;
1135 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
1136 /* Dummy read */
1137 imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
1138 }
1139 } else if (!(ctl & I2CR_MTX)) { /* Receive mode */
1140 if (status & I2SR_IBB) { /* No STOP signal detected */
1141 ctl &= ~I2CR_MTX;
1142 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
1143
1144 value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
> 1145 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
1146 } else { /* STOP signal is detected */
1147 dev_dbg(&i2c_imx->adapter.dev,
1148 "STOP signal detected");
> 1149 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
1150 }
1151 } else if (!(status & I2SR_RXAK)) { /* Transmit mode received ACK */
1152 ctl |= I2CR_MTX;
1153 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
1154
> 1155 i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_PROCESSED, &value);
1156
1157 imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
1158 } else { /* Transmit mode received NAK */
1159 ctl &= ~I2CR_MTX;
1160 imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
1161 imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
1162 }
1163 return IRQ_HANDLED;
1164 }
1165
1166 static int i2c_imx_reg_slave(struct i2c_client *client)
1167 {
1168 struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
1169 int ret;
1170 if (i2c_imx->slave)
1171 return -EBUSY;
1172
1173 i2c_imx->slave = client;
1174
1175 ret = i2c_imx_slave_init(i2c_imx);
1176 if (ret < 0)
1177 dev_err(&i2c_imx->adapter.dev, "failed to switch to slave mode");
1178
1179 return ret;
1180 }
1181
1182 static int i2c_imx_unreg_slave(struct i2c_client *client)
1183 {
1184 struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
1185 int ret;
1186
1187 if (!i2c_imx->slave)
1188 return -EINVAL;
1189
1190 /* Reset slave address. */
1191 imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
1192
1193 i2c_imx_reset_regs(i2c_imx);
1194
1195 i2c_imx->slave = NULL;
1196
1197 /* Suspend */
1198 ret = pm_runtime_put_sync(i2c_imx->adapter.dev.parent);
1199 if (ret < 0)
1200 dev_err(&i2c_imx->adapter.dev, "failed to suspend i2c controller");
1201
1202 return ret;
1203 }
1204
1205 static const struct i2c_algorithm i2c_imx_algo = {
1206 .master_xfer = i2c_imx_xfer,
1207 .functionality = i2c_imx_func,
> 1208 .reg_slave = i2c_imx_reg_slave,
> 1209 .unreg_slave = i2c_imx_unreg_slave,
1210 };
1211

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (8.30 kB)
.config.gz (35.72 kB)
Download all attachments