From e28b124564e648d933337a0708dae1323c518af7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:44 +0200 Subject: i2c: guarantee that I2C_M_RD will be 0x0001 forever There is code out there in user space and kernel space which relies on I2C_M_RD being bit 0 to simplify their bit operations. Add a comment to make sure this will never break. Do proper sorting of the defines while we are here. Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- include/uapi/linux/i2c.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h index b0a7dd61eb35..adcbef4bff61 100644 --- a/include/uapi/linux/i2c.h +++ b/include/uapi/linux/i2c.h @@ -68,14 +68,15 @@ struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; -#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ -#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ -#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ -#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ -#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ -#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ + /* I2C_M_RD is guaranteed to be 0x0001! */ +#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ +#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ +#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ }; -- cgit From a16d6ebca6efb73f6402f36e5aebf84f61721856 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:45 +0200 Subject: i2c: introduce helper function to get 8 bit address from a message Drivers do this in various ways, let's use one standard way of doing it. Note: I2C_M_RD is bit 0, so the code could be simplified. To be extremly robust and to advertise good coding practices, I still use the ternary operator and let the compilers do the optimizing job. Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 200cf13b00f6..c30833b7b073 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -654,6 +654,11 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap) return adap->nr; } +static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) +{ + return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); +} + /** * module_i2c_driver() - Helper macro for registering a modular I2C driver * @__i2c_driver: i2c_driver struct -- cgit From 041edda8757b5567b4d417e0cc9ade504dd4292f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:46 +0200 Subject: i2c: core: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index e584d88ee337..cc3c143b1c6a 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -2646,7 +2646,7 @@ static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) { /* The address will be sent first */ - u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD); + u8 addr = i2c_8bit_addr_from_msg(msg); pec = i2c_smbus_pec(pec, &addr, 1); /* The data buffer follows */ -- cgit From 32822bff880bd9c3b55145e258d03dc671f0d2ea Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:47 +0200 Subject: i2c: bcm-iproc: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-bcm-iproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index b9f0fff4e723..19c843828fe2 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -267,7 +267,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, iproc_i2c->msg = msg; /* format and load slave address into the TX FIFO */ - addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0); + addr = i2c_8bit_addr_from_msg(msg); writel(addr, iproc_i2c->base + M_TX_OFFSET); /* -- cgit From 191d738d847e63b24da7cacbdfb179e5b5a9145f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:48 +0200 Subject: i2c: bcm-kona: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-bcm-kona.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c index 2c9d9b1c8e64..ac9f47679c3a 100644 --- a/drivers/i2c/busses/i2c-bcm-kona.c +++ b/drivers/i2c/busses/i2c-bcm-kona.c @@ -501,10 +501,7 @@ static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev, return -EREMOTEIO; } } else { - addr = msg->addr << 1; - - if (msg->flags & I2C_M_RD) - addr |= 1; + addr = i2c_8bit_addr_from_msg(msg); if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) return -EREMOTEIO; -- cgit From fa5ce47a994867e79bd595ec2eef5178834c2570 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:49 +0200 Subject: i2c: brcmstb: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-brcmstb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index 4a45408dd820..6a8cfc1344b2 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -446,9 +446,7 @@ static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev, } } else { - addr = msg->addr << 1; - if (msg->flags & I2C_M_RD) - addr |= 1; + addr = i2c_8bit_addr_from_msg(msg); bsc_writel(dev, addr, chip_address); } -- cgit From 949d0c5b72b94fb58a1d2c48c223658af24f58ee Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:50 +0200 Subject: i2c: cpm: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cpm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 714bdc837769..b6d2821aaabd 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -197,9 +197,7 @@ static void cpm_i2c_parse_message(struct i2c_adapter *adap, tbdf = cpm->tbase + tx; rbdf = cpm->rbase + rx; - addr = pmsg->addr << 1; - if (pmsg->flags & I2C_M_RD) - addr |= 1; + addr = i2c_8bit_addr_from_msg(pmsg); tb = cpm->txbuf[tx]; rb = cpm->rxbuf[rx]; -- cgit From 73fef2196dd654ffda44486842d336c851d9294d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:51 +0200 Subject: i2c: ibm_iic: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ibm_iic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index b6c080334297..cdaa7be2cd1b 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -269,7 +269,7 @@ static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p) ndelay(t->hd_sta); /* Send address */ - v = (u8)((p->addr << 1) | ((p->flags & I2C_M_RD) ? 1 : 0)); + v = i2c_8bit_addr_from_msg(p); for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){ out_8(&iic->directcntl, sda); ndelay(t->low / 2); -- cgit From 9c01cae8dd841a3d7b2f7763affd4e3fd16cebcf Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:52 +0200 Subject: i2c: img-scb: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-img-scb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index 379ef9c31664..ea20425b6972 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -751,9 +751,7 @@ static unsigned int img_i2c_atomic(struct img_i2c *i2c, switch (i2c->at_cur_cmd) { case CMD_GEN_START: next_cmd = CMD_GEN_DATA; - next_data = (i2c->msg.addr << 1); - if (i2c->msg.flags & I2C_M_RD) - next_data |= 0x1; + next_data = i2c_8bit_addr_from_msg(&i2c->msg); break; case CMD_GEN_DATA: if (i2c->line_status & LINESTAT_INPUT_HELD_V) -- cgit From 515e240bb54c53195fe0c7a9bad92ffb23e8bc54 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:53 +0200 Subject: i2c: iop3xx: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-iop3xx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 72d6161cf77c..85cbe4b55578 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -50,10 +50,7 @@ iic_cook_addr(struct i2c_msg *msg) { unsigned char addr; - addr = (msg->addr << 1); - - if (msg->flags & I2C_M_RD) - addr |= 1; + addr = i2c_8bit_addr_from_msg(msg); return addr; } -- cgit From 043f47f49b3646beab84019eefb2c336be87db5f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:54 +0200 Subject: i2c: lpc2k: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-lpc2k.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-lpc2k.c b/drivers/i2c/busses/i2c-lpc2k.c index 8560a13bf1b3..586a15205e61 100644 --- a/drivers/i2c/busses/i2c-lpc2k.c +++ b/drivers/i2c/busses/i2c-lpc2k.c @@ -133,9 +133,7 @@ static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c) case M_START: case M_REPSTART: /* Start bit was just sent out, send out addr and dir */ - data = i2c->msg->addr << 1; - if (i2c->msg->flags & I2C_M_RD) - data |= 1; + data = i2c_8bit_addr_from_msg(i2c->msg); writel(data, i2c->base + LPC24XX_I2DAT); writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR); -- cgit From 0d47ce210a5d6733d7bc8cb07f158908bb224629 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:55 +0200 Subject: i2c: mt65xx: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 453358b4d9ca..d9373e60be8a 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -413,10 +413,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, else writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF); - addr_reg = msgs->addr << 1; - if (i2c->op == I2C_MASTER_RD) - addr_reg |= 0x1; - + addr_reg = i2c_8bit_addr_from_msg(msgs); writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR); /* Clear interrupt status */ -- cgit From 380a295c87a6423390898b98e388e1b0a399bb08 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:56 +0200 Subject: i2c: ocores: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ocores.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 11b7b87311ed..dfa7a4b4a91d 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -178,10 +178,7 @@ static void ocores_process(struct ocores_i2c *i2c) if (i2c->nmsgs) { /* end? */ /* send start? */ if (!(msg->flags & I2C_M_NOSTART)) { - u8 addr = (msg->addr << 1); - - if (msg->flags & I2C_M_RD) - addr |= 1; + u8 addr = i2c_8bit_addr_from_msg(msg); i2c->state = STATE_START; -- cgit From 7756e1ee78b37ddb98bfcc815002cb885a0449c8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:57 +0200 Subject: i2c: powermac: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-powermac.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 6abcf696e359..b0d9dee14a7e 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -150,13 +150,11 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap, { struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); int rc = 0; - int read; int addrdir; if (msgs->flags & I2C_M_TEN) return -EINVAL; - read = (msgs->flags & I2C_M_RD) != 0; - addrdir = (msgs->addr << 1) | read; + addrdir = i2c_8bit_addr_from_msg(msgs); rc = pmac_i2c_open(bus, 0); if (rc) { -- cgit From e3c60f3d2d8bc803806262ecb4ec5b2723bcd606 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:58 +0200 Subject: i2c: qup: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 23eaabb19f96..cc6439ab3f71 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -515,7 +515,7 @@ static int qup_i2c_get_data_len(struct qup_i2c_dev *qup) static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup, struct i2c_msg *msg, int is_dma) { - u16 addr = (msg->addr << 1) | ((msg->flags & I2C_M_RD) == I2C_M_RD); + u16 addr = i2c_8bit_addr_from_msg(msg); int len = 0; int data_len; -- cgit From 3f8a57bb6d5fb37c0b02d091184b01eab9dcf43a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:44:59 +0200 Subject: i2c: sh_mobile: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 7d2bd3ec2d2d..6fb3e2645992 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -398,8 +398,7 @@ static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd, { switch (pd->pos) { case -1: - *buf = (pd->msg->addr & 0x7f) << 1; - *buf |= (pd->msg->flags & I2C_M_RD) ? 1 : 0; + *buf = i2c_8bit_addr_from_msg(pd->msg); break; default: *buf = pd->msg->buf[pd->pos]; -- cgit From 5c1274fab5c10aa193f5740dd76780010bda77d7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:45:00 +0200 Subject: i2c: sirf: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sirf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c index 13e51ef6af73..792a42bdd335 100644 --- a/drivers/i2c/busses/i2c-sirf.c +++ b/drivers/i2c/busses/i2c-sirf.c @@ -190,9 +190,7 @@ static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic, writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++)); - addr = msg->addr << 1; /* Generate address */ - if (msg->flags & I2C_M_RD) - addr |= 1; + addr = i2c_8bit_addr_from_msg(msg); /* Reverse direction bit */ if (msg->flags & I2C_M_REV_DIR_ADDR) -- cgit From a51a79d50ebc21d47f73f1e7f695ef2f86ef3417 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 3 Apr 2016 20:45:01 +0200 Subject: i2c: st: use new 8 bit address helper function Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-st.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 6ee77159ac57..b3c182fea165 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -614,8 +614,7 @@ static int st_i2c_xfer_msg(struct st_i2c_dev *i2c_dev, struct i2c_msg *msg, unsigned long timeout; int ret; - c->addr = (u8)(msg->addr << 1); - c->addr |= (msg->flags & I2C_M_RD); + c->addr = i2c_8bit_addr_from_msg(msg); c->buf = msg->buf; c->count = msg->len; c->xfered = 0; -- cgit From 04f59143b571161d25315dd52d7a2ecc022cb71a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 12 Apr 2016 09:57:35 +0200 Subject: i2c: let I2C masters ignore their children for PM When using a certain I2C device with runtime PM enabled on a certain I2C bus adaper the following happens: struct amba_device *foo \ struct i2c_adapter *bar \ struct i2c_client *baz The AMBA device foo has its device PM struct set to ignore children with pm_suspend_ignore_children(&foo->dev, true). This makes runtime PM work just fine locally in the driver: the fact that devices on the bus are suspended or resumed individually does not affect its operation, and the hardware does not power up unless transferring messages. However this child ignorance property is not inherited into the struct i2c_adapter *bar. On system suspend things will work fine. On system resume the following annoying phenomenon occurs: - In the pm_runtime_force_resume() path of struct i2c_client *baz, pm_runtime_set_active(&baz->dev); is eventually called. - This becomes __pm_runtime_set_status(&baz->dev, RPM_ACTIVE); - __pm_runtime_set_status() detects that RPM state is changed, and checks whether the parent is: not active (RPM_ACTIVE) and not ignoring its children If this happens it concludes something is wrong, because a parent that is not ignoring its children must be active before any children activate. - Since the struct i2c_adapter *bar does not ignore its children, the PM core thinks that it must indeed go online before its children, the check bails out with -EBUSY, i.e. the i2c_client *baz thinks it can't work because it's parent is not online, and it respects its parent. - In the driver the .resume() callback returns -EBUSY from the runtime_force_resume() call as per above. This leaves the device in a suspended state, leading to bad behaviour later when the device is used. The following debug print is made with an extra printg patch but illustrates the problem: [ 17.040832] bh1780 2-0029: parent (i2c-2) is not active parent->power.ignore_children = 0 [ 17.040832] bh1780 2-0029: pm_runtime_force_resume: pm_runtime_set_active() failed (-16) [ 17.040863] dpm_run_callback(): pm_runtime_force_resume+0x0/0x88 returns -16 [ 17.040863] PM: Device 2-0029 failed to resume: error -16 Fix this by letting all struct i2c_adapter:s ignore their children: i2c children have no business doing keeping their parents awake: they are completely autonomous devices that just use their parent to talk, a usecase which must be power managed in the host on a per-message basis. Signed-off-by: Linus Walleij Reviewed-by: Ulf Hansson Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index cc3c143b1c6a..4979728f7fb2 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1559,6 +1559,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); pm_runtime_no_callbacks(&adap->dev); + pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); #ifdef CONFIG_I2C_COMPAT -- cgit From 2ee73c484dc4b8831b745a5feed56382b8741a72 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 10 Mar 2016 14:12:21 +0200 Subject: i2c: i801: Convert to struct dev_pm_ops for suspend/resume Stop using legacy PCI PM support and convert to standard dev_pm_ops. This provides more straightforward path to add runtime PM. While at it remove explicit PCI power state control and configuration space save/restore as the PCI core does it. Signed-off-by: Jarkko Nikula Tested-by: Reinette Chatre Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-i801.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 585a3b7915bd..7a29f1436ddb 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1433,34 +1433,32 @@ static void i801_remove(struct pci_dev *dev) } #ifdef CONFIG_PM -static int i801_suspend(struct pci_dev *dev, pm_message_t mesg) +static int i801_suspend(struct device *dev) { - struct i801_priv *priv = pci_get_drvdata(dev); + struct pci_dev *pci_dev = to_pci_dev(dev); + struct i801_priv *priv = pci_get_drvdata(pci_dev); - pci_save_state(dev); - pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); - pci_set_power_state(dev, pci_choose_state(dev, mesg)); + pci_write_config_byte(pci_dev, SMBHSTCFG, priv->original_hstcfg); return 0; } -static int i801_resume(struct pci_dev *dev) +static int i801_resume(struct device *dev) { - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); return 0; } -#else -#define i801_suspend NULL -#define i801_resume NULL #endif +static UNIVERSAL_DEV_PM_OPS(i801_pm_ops, i801_suspend, + i801_resume, NULL); + static struct pci_driver i801_driver = { .name = "i801_smbus", .id_table = i801_ids, .probe = i801_probe, .remove = i801_remove, - .suspend = i801_suspend, - .resume = i801_resume, + .driver = { + .pm = &i801_pm_ops, + }, }; static int __init i2c_i801_init(void) -- cgit From a7401ca5596e246f17b087b84fe55a429b666132 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 10 Mar 2016 14:12:22 +0200 Subject: i2c: i801: Add runtime PM support with autosuspend Allow runtime PM so that PM and PCI core can put the device into low-power state when idle and resume it back when needed in those platforms that support PM for i801 device. Enable also autosuspend with 1 second delay in order to not needlessly toggle power state on and off if there are multiple transactions during short time. Device is resumed at the beginning of bus access and marked idle ready for autosuspend at the end of it. Signed-off-by: Jarkko Nikula Tested-by: Reinette Chatre Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-i801.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 7a29f1436ddb..64b1208bca5e 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -94,6 +94,7 @@ #include #include #include +#include #if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \ defined CONFIG_DMI @@ -714,9 +715,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, { int hwpec; int block = 0; - int ret, xact = 0; + int ret = 0, xact = 0; struct i801_priv *priv = i2c_get_adapdata(adap); + pm_runtime_get_sync(&priv->pci_dev->dev); + hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA; @@ -773,7 +776,8 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, default: dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n", size); - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + goto out; } if (hwpec) /* enable/disable hardware PEC */ @@ -796,11 +800,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); if (block) - return ret; + goto out; if (ret) - return ret; + goto out; if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) - return 0; + goto out; switch (xact & 0x7f) { case I801_BYTE: /* Result put in SMBHSTDAT0 */ @@ -812,7 +816,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, (inb_p(SMBHSTDAT1(priv)) << 8); break; } - return 0; + +out: + pm_runtime_mark_last_busy(&priv->pci_dev->dev); + pm_runtime_put_autosuspend(&priv->pci_dev->dev); + return ret; } @@ -1413,6 +1421,11 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) pci_set_drvdata(dev, priv); + pm_runtime_set_autosuspend_delay(&dev->dev, 1000); + pm_runtime_use_autosuspend(&dev->dev); + pm_runtime_put_autosuspend(&dev->dev); + pm_runtime_allow(&dev->dev); + return 0; } @@ -1420,6 +1433,9 @@ static void i801_remove(struct pci_dev *dev) { struct i801_priv *priv = pci_get_drvdata(dev); + pm_runtime_forbid(&dev->dev); + pm_runtime_get_noresume(&dev->dev); + i801_del_mux(priv); i2c_del_adapter(&priv->adapter); pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); -- cgit From 497fbe24987bd24ee271c67c212ec681995188b6 Mon Sep 17 00:00:00 2001 From: Shardar Shariff Md Date: Mon, 14 Mar 2016 18:52:18 +0530 Subject: i2c: tegra: enable multi master mode for tegra210 Enable multi-master mode in I2C_CNFG reg based on hw features. Using single/multi-master mode bit introduced for Tegra210, whereas multi-master mode is enabled by default in HW for T124 and earlier Tegra SOC. Enabling this bit doesn't explicitly start treating the bus has having multiple masters, but will start checking for arbitration lost and reporting when it occurs. The Tegra210 I2C controller supports single/multi master mode. Add chipdata for Tegra210 and its compatibility string so that Tegra210 will select data that enables multi master mode correctly. Do below prerequisites for multi-master bus if "multi-master" dt property entry is added. 1. Enable 1st level clock always set. 2. Disable 2nd level clock gating (slcg which is supported from T124 SOC and later chips) Signed-off-by: Shardar Shariff Md Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 74 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 929185a7296c..d764d64e9d2c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -38,6 +38,7 @@ #define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 #define I2C_CNFG_PACKET_MODE_EN (1<<10) #define I2C_CNFG_NEW_MASTER_FSM (1<<11) +#define I2C_CNFG_MULTI_MASTER_MODE (1<<17) #define I2C_STATUS 0x01C #define I2C_SL_CNFG 0x020 #define I2C_SL_CNFG_NACK (1<<1) @@ -106,6 +107,9 @@ #define I2C_SLV_CONFIG_LOAD (1 << 1) #define I2C_TIMEOUT_CONFIG_LOAD (1 << 2) +#define I2C_CLKEN_OVERRIDE 0x090 +#define I2C_MST_CORE_CLKEN_OVR (1 << 0) + /* * msg_end_type: The bus control which need to be send at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer. @@ -143,6 +147,8 @@ struct tegra_i2c_hw_feature { int clk_divisor_hs_mode; int clk_divisor_std_fast_mode; u16 clk_divisor_fast_plus_mode; + bool has_multi_master_mode; + bool has_slcg_override_reg; }; /** @@ -184,6 +190,7 @@ struct tegra_i2c_dev { u32 bus_clk_rate; u16 clk_divisor_non_hs_mode; bool is_suspended; + bool is_multimaster_mode; }; static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg) @@ -438,6 +445,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN | (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); + + if (i2c_dev->hw->has_multi_master_mode) + val |= I2C_CNFG_MULTI_MASTER_MODE; + i2c_writel(i2c_dev, val, I2C_CNFG); i2c_writel(i2c_dev, 0, I2C_INT_MASK); @@ -463,6 +474,9 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) if (tegra_i2c_flush_fifos(i2c_dev)) err = -ETIMEDOUT; + if (i2c_dev->is_multimaster_mode && i2c_dev->hw->has_slcg_override_reg) + i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE); + if (i2c_dev->hw->has_config_load_reg) { i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD); while (i2c_readl(i2c_dev, I2C_CONFIG_LOAD) != 0) { @@ -688,6 +702,20 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap) return ret; } +static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev) +{ + struct device_node *np = i2c_dev->dev->of_node; + int ret; + + ret = of_property_read_u32(np, "clock-frequency", + &i2c_dev->bus_clk_rate); + if (ret) + i2c_dev->bus_clk_rate = 100000; /* default clock rate */ + + i2c_dev->is_multimaster_mode = of_property_read_bool(np, + "multi-master"); +} + static const struct i2c_algorithm tegra_i2c_algo = { .master_xfer = tegra_i2c_xfer, .functionality = tegra_i2c_func, @@ -707,6 +735,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .clk_divisor_std_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, + .has_multi_master_mode = false, + .has_slcg_override_reg = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -717,6 +747,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .clk_divisor_std_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, + .has_multi_master_mode = false, + .has_slcg_override_reg = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -727,6 +759,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .clk_divisor_std_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = false, + .has_multi_master_mode = false, + .has_slcg_override_reg = false, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -737,10 +771,25 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .clk_divisor_std_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, + .has_multi_master_mode = false, + .has_slcg_override_reg = true, +}; + +static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .has_single_clk_source = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_fast_plus_mode = 0x10, + .has_config_load_reg = true, + .has_multi_master_mode = true, + .has_slcg_override_reg = true, }; /* Match table for of_platform binding */ static const struct of_device_id tegra_i2c_of_match[] = { + { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, }, { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, }, @@ -797,10 +846,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) return PTR_ERR(i2c_dev->rst); } - ret = of_property_read_u32(i2c_dev->dev->of_node, "clock-frequency", - &i2c_dev->bus_clk_rate); - if (ret) - i2c_dev->bus_clk_rate = 100000; /* default clock rate */ + tegra_i2c_parse_dt(i2c_dev); i2c_dev->hw = &tegra20_i2c_hw; @@ -853,6 +899,15 @@ static int tegra_i2c_probe(struct platform_device *pdev) goto unprepare_fast_clk; } + if (i2c_dev->is_multimaster_mode) { + ret = clk_enable(i2c_dev->div_clk); + if (ret < 0) { + dev_err(i2c_dev->dev, "div_clk enable failed %d\n", + ret); + goto unprepare_div_clk; + } + } + ret = tegra_i2c_init(i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller"); @@ -863,7 +918,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq); - goto unprepare_div_clk; + goto disable_div_clk; } i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); @@ -878,11 +933,15 @@ static int tegra_i2c_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&i2c_dev->adapter); if (ret) { dev_err(&pdev->dev, "Failed to add I2C adapter\n"); - goto unprepare_div_clk; + goto disable_div_clk; } return 0; +disable_div_clk: + if (i2c_dev->is_multimaster_mode) + clk_disable(i2c_dev->div_clk); + unprepare_div_clk: clk_unprepare(i2c_dev->div_clk); @@ -898,6 +957,9 @@ static int tegra_i2c_remove(struct platform_device *pdev) struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev); i2c_del_adapter(&i2c_dev->adapter); + if (i2c_dev->is_multimaster_mode) + clk_disable(i2c_dev->div_clk); + clk_unprepare(i2c_dev->div_clk); if (!i2c_dev->hw->has_single_clk_source) clk_unprepare(i2c_dev->fast_clk); -- cgit From a035d71b1205f880d85752869053b14813366108 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 11 Apr 2016 17:28:32 +0200 Subject: i2c: octeon: Increase retry default and use fixed timeout value Convert the adapter timeout to 2 ms independently of depending on CONFIG_HZ. CONFIG_HZ is 100 for MIPS Cavium-Octeon so the timeout value is not changed. Also set retries to 5 to improve robustness. Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 46fb6c42934f..c4abf16468ca 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -426,7 +426,6 @@ static struct i2c_adapter octeon_i2c_ops = { .owner = THIS_MODULE, .name = "OCTEON adapter", .algo = &octeon_i2c_algo, - .timeout = HZ / 50, }; /* calculate and set clock divisors */ @@ -553,6 +552,8 @@ static int octeon_i2c_probe(struct platform_device *pdev) octeon_i2c_set_clock(i2c); i2c->adap = octeon_i2c_ops; + i2c->adap.timeout = msecs_to_jiffies(2); + i2c->adap.retries = 5; i2c->adap.dev.parent = &pdev->dev; i2c->adap.dev.of_node = node; i2c_set_adapdata(&i2c->adap, i2c); -- cgit From f541bb382fd6b4cd60c9f1fcc2afca1d1d8ab84c Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 11 Apr 2016 17:28:33 +0200 Subject: i2c: octeon: Move set-clock and init-lowlevel upward No functional change, just moving the functions upward in preparation of improving the recovery. Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 126 ++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index c4abf16468ca..f647667a3a47 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -214,6 +214,69 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } +/* calculate and set clock divisors */ +static void octeon_i2c_set_clock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; + + for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { + /* + * An mdiv value of less than 2 seems to not work well + * with ds1337 RTCs, so we constrain it to larger values. + */ + for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { + /* + * For given ndiv and mdiv values check the + * two closest thp values. + */ + tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; + tclk *= (1 << ndiv_idx); + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + + for (inc = 0; inc <= 1; inc++) { + thp_idx = thp_base + inc; + if (thp_idx < 5 || thp_idx > 0xff) + continue; + + foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + foscl = foscl / (1 << ndiv_idx); + foscl = foscl / (mdiv_idx + 1) / 10; + diff = abs(foscl - i2c->twsi_freq); + if (diff < delta_hz) { + delta_hz = diff; + thp = thp_idx; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); +} + +static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) +{ + u8 status; + int tries; + + /* disable high level controller, enable bus access */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + /* reset controller */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries; tries--) { + udelay(1); + status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if (status == STAT_IDLE) + return 0; + } + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); + return -EIO; +} + /** * octeon_i2c_start - send START to the bus * @i2c: The struct octeon_i2c @@ -428,69 +491,6 @@ static struct i2c_adapter octeon_i2c_ops = { .algo = &octeon_i2c_algo, }; -/* calculate and set clock divisors */ -static void octeon_i2c_set_clock(struct octeon_i2c *i2c) -{ - int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; - int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; - - for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { - /* - * An mdiv value of less than 2 seems to not work well - * with ds1337 RTCs, so we constrain it to larger values. - */ - for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { - /* - * For given ndiv and mdiv values check the - * two closest thp values. - */ - tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; - tclk *= (1 << ndiv_idx); - thp_base = (i2c->sys_freq / (tclk * 2)) - 1; - - for (inc = 0; inc <= 1; inc++) { - thp_idx = thp_base + inc; - if (thp_idx < 5 || thp_idx > 0xff) - continue; - - foscl = i2c->sys_freq / (2 * (thp_idx + 1)); - foscl = foscl / (1 << ndiv_idx); - foscl = foscl / (mdiv_idx + 1) / 10; - diff = abs(foscl - i2c->twsi_freq); - if (diff < delta_hz) { - delta_hz = diff; - thp = thp_idx; - mdiv = mdiv_idx; - ndiv = ndiv_idx; - } - } - } - } - octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); -} - -static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) -{ - u8 status; - int tries; - - /* disable high level controller, enable bus access */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); - - /* reset controller */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); - - for (tries = 10; tries; tries--) { - udelay(1); - status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); - if (status == STAT_IDLE) - return 0; - } - dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); - return -EIO; -} - static int octeon_i2c_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; -- cgit From 9cb9480e473b910e226312574f997bec29f47ae4 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 11 Apr 2016 17:28:34 +0200 Subject: i2c: octeon: Rename [read|write]_sw to reg_[read|write] Rename the [read|write]_sw functions to make it clearer they access the TWSI registers. Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index f647667a3a47..43498a427d16 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -80,14 +80,14 @@ struct octeon_i2c { }; /** - * octeon_i2c_write_sw - write an I2C core register + * octeon_i2c_reg_write - write an I2C core register * @i2c: The struct octeon_i2c * @eop_reg: Register selector * @data: Value to be written * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) +static void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8 data) { u64 tmp; @@ -98,7 +98,7 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) } /** - * octeon_i2c_read_sw - read lower bits of an I2C core register + * octeon_i2c_reg_read - read lower bits of an I2C core register * @i2c: The struct octeon_i2c * @eop_reg: Register selector * @@ -106,7 +106,7 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +static u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) { u64 tmp; @@ -189,7 +189,7 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; } /** @@ -252,8 +252,8 @@ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) } } } - octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); + octeon_i2c_reg_write(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); } static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) @@ -262,14 +262,14 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) int tries; /* disable high level controller, enable bus access */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); /* reset controller */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0); for (tries = 10; tries; tries--) { udelay(1); - status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + status = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); if (status == STAT_IDLE) return 0; } @@ -288,19 +288,19 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) int result; u8 data; - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); if (result) { - if (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { + if (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { /* * Controller refused to send start flag May * be a client is holding SDA low - let's try * to free it. */ octeon_i2c_unblock(i2c); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); } @@ -308,7 +308,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) return result; } - data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + data = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); if ((data != STAT_START) && (data != STAT_RSTART)) { dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); return -EIO; @@ -320,7 +320,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) /* send STOP to the bus */ static void octeon_i2c_stop(struct octeon_i2c *i2c) { - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB | TWSI_CTL_STP); } @@ -345,15 +345,15 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, if (result) return result; - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) return result; for (i = 0; i < length; i++) { - tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + tmp = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) { dev_err(i2c->dev, @@ -362,8 +362,8 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, return -EIO; } - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) @@ -398,15 +398,15 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, if (result) return result; - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target << 1) | 1); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, (target << 1) | 1); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) return result; for (i = 0; i < length; i++) { - tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + tmp = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) { dev_err(i2c->dev, @@ -416,17 +416,17 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, } if (i + 1 < length) - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB | TWSI_CTL_AAK); else - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) return result; - data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + data[i] = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA); if (recv_len && i == 0) { if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) { dev_err(i2c->dev, -- cgit From c57db7098b72f406a4834b4b74268db54eae3fb7 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 11 Apr 2016 17:28:35 +0200 Subject: i2c: octeon: Introduce helper functions for register access Add helper functions for control, data and status register access. This simplifies the code and makes the purpose of the register access clearer. Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 56 +++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 43498a427d16..b25c49151ea4 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -97,6 +97,11 @@ static void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8 data) } while ((tmp & SW_TWSI_V) != 0); } +#define octeon_i2c_ctl_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, val) +#define octeon_i2c_data_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, val) + /** * octeon_i2c_reg_read - read lower bits of an I2C core register * @i2c: The struct octeon_i2c @@ -118,6 +123,13 @@ static u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) return tmp & 0xFF; } +#define octeon_i2c_ctl_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) +#define octeon_i2c_data_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA) +#define octeon_i2c_stat_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) + /** * octeon_i2c_write_int - write the TWSI_INT register * @i2c: The struct octeon_i2c @@ -189,7 +201,7 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG) != 0; } /** @@ -262,14 +274,14 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) int tries; /* disable high level controller, enable bus access */ - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); /* reset controller */ octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0); for (tries = 10; tries; tries--) { udelay(1); - status = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); + status = octeon_i2c_stat_read(i2c); if (status == STAT_IDLE) return 0; } @@ -288,27 +300,25 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) int result; u8 data; - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STA); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); if (result) { - if (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { + if (octeon_i2c_stat_read(i2c) == STAT_IDLE) { /* * Controller refused to send start flag May * be a client is holding SDA low - let's try * to free it. */ octeon_i2c_unblock(i2c); - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STA); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); } if (result) return result; } - data = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); + data = octeon_i2c_stat_read(i2c); if ((data != STAT_START) && (data != STAT_RSTART)) { dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); return -EIO; @@ -320,8 +330,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) /* send STOP to the bus */ static void octeon_i2c_stop(struct octeon_i2c *i2c) { - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STP); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STP); } /** @@ -345,15 +354,15 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, if (result) return result; - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_data_write(i2c, target << 1); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) return result; for (i = 0; i < length; i++) { - tmp = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); + tmp = octeon_i2c_stat_read(i2c); if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) { dev_err(i2c->dev, @@ -362,8 +371,8 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, return -EIO; } - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_data_write(i2c, data[i]); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) @@ -398,16 +407,15 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, if (result) return result; - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, (target << 1) | 1); - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_data_write(i2c, (target << 1) | 1); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) return result; for (i = 0; i < length; i++) { - tmp = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); - + tmp = octeon_i2c_stat_read(i2c); if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) { dev_err(i2c->dev, "%s: bad status before read (0x%x)\n", @@ -416,17 +424,15 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, } if (i + 1 < length) - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_AAK); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); else - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) return result; - data[i] = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA); + data[i] = octeon_i2c_data_read(i2c); if (recv_len && i == 0) { if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) { dev_err(i2c->dev, -- cgit From b69e5c672d57fa300fef368106ab61c9214a760d Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 11 Apr 2016 17:28:36 +0200 Subject: i2c: octeon: Remove superfluous check in octeon_i2c_test_iflg Remove superfluous check and stray newline. Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index b25c49151ea4..4275007c2907 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -198,10 +198,9 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } - static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG); } /** -- cgit From 1ab92956d4aac52d0c59c229a0790a672063ece0 Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 17 Mar 2016 00:57:17 +0800 Subject: i2c: rk3x: switch to i2c generic dt parsing Switch to the new generic functions: i2c_parse_fw_timings(). Signed-off-by: David Wu Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 87 ++++++++++++------------------------------- 1 file changed, 24 insertions(+), 63 deletions(-) diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 9096d17beb5b..c4b0d8973694 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -101,10 +101,7 @@ struct rk3x_i2c { struct notifier_block clk_rate_nb; /* Settings */ - unsigned int scl_frequency; - unsigned int scl_rise_ns; - unsigned int scl_fall_ns; - unsigned int sda_fall_ns; + struct i2c_timings t; /* Synchronization & notification */ spinlock_t lock; @@ -437,10 +434,7 @@ out: * Calculate divider values for desired SCL frequency * * @clk_rate: I2C input clock rate - * @scl_rate: Desired SCL rate - * @scl_rise_ns: How many ns it takes for SCL to rise. - * @scl_fall_ns: How many ns it takes for SCL to fall. - * @sda_fall_ns: How many ns it takes for SDA to fall. + * @t: Known I2C timing information. * @div_low: Divider output for low * @div_high: Divider output for high * @@ -448,11 +442,10 @@ out: * a best-effort divider value is returned in divs. If the target rate is * too high, we silently use the highest possible rate. */ -static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, - unsigned long scl_rise_ns, - unsigned long scl_fall_ns, - unsigned long sda_fall_ns, - unsigned long *div_low, unsigned long *div_high) +static int rk3x_i2c_calc_divs(unsigned long clk_rate, + struct i2c_timings *t, + unsigned long *div_low, + unsigned long *div_high) { unsigned long spec_min_low_ns, spec_min_high_ns; unsigned long spec_setup_start, spec_max_data_hold_ns; @@ -472,12 +465,12 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, int ret = 0; /* Only support standard-mode and fast-mode */ - if (WARN_ON(scl_rate > 400000)) - scl_rate = 400000; + if (WARN_ON(t->bus_freq_hz > 400000)) + t->bus_freq_hz = 400000; /* prevent scl_rate_khz from becoming 0 */ - if (WARN_ON(scl_rate < 1000)) - scl_rate = 1000; + if (WARN_ON(t->bus_freq_hz < 1000)) + t->bus_freq_hz = 1000; /* * min_low_ns: The minimum number of ns we need to hold low to @@ -491,7 +484,7 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, * This is because the i2c host on Rockchip holds the data line * for half the low time. */ - if (scl_rate <= 100000) { + if (t->bus_freq_hz <= 100000) { /* Standard-mode */ spec_min_low_ns = 4700; spec_setup_start = 4700; @@ -506,7 +499,7 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, spec_max_data_hold_ns = 900; data_hold_buffer_ns = 50; } - min_high_ns = scl_rise_ns + spec_min_high_ns; + min_high_ns = t->scl_rise_ns + spec_min_high_ns; /* * Timings for repeated start: @@ -517,18 +510,18 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, * we meet tSU;STA and tHD;STA times. */ min_high_ns = max(min_high_ns, - DIV_ROUND_UP((scl_rise_ns + spec_setup_start) * 1000, 875)); + DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start) * 1000, 875)); min_high_ns = max(min_high_ns, - DIV_ROUND_UP((scl_rise_ns + spec_setup_start + - sda_fall_ns + spec_min_high_ns), 2)); + DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start + + t->sda_fall_ns + spec_min_high_ns), 2)); - min_low_ns = scl_fall_ns + spec_min_low_ns; + min_low_ns = t->scl_fall_ns + spec_min_low_ns; max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns; min_total_ns = min_low_ns + min_high_ns; /* Adjust to avoid overflow */ clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000); - scl_rate_khz = scl_rate / 1000; + scl_rate_khz = t->bus_freq_hz / 1000; /* * We need the total div to be >= this number @@ -616,14 +609,13 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) { + struct i2c_timings *t = &i2c->t; unsigned long div_low, div_high; u64 t_low_ns, t_high_ns; int ret; - ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns, - i2c->scl_fall_ns, i2c->sda_fall_ns, - &div_low, &div_high); - WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency); + ret = rk3x_i2c_calc_divs(clk_rate, t, &div_low, &div_high); + WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz); clk_enable(i2c->clk); i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV); @@ -634,7 +626,7 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) dev_dbg(i2c->dev, "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n", clk_rate / 1000, - 1000000000 / i2c->scl_frequency, + 1000000000 / t->bus_freq_hz, t_low_ns, t_high_ns); } @@ -664,9 +656,7 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long switch (event) { case PRE_RATE_CHANGE: - if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency, - i2c->scl_rise_ns, i2c->scl_fall_ns, - i2c->sda_fall_ns, + if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t, &div_low, &div_high) != 0) return NOTIFY_STOP; @@ -879,37 +869,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev) match = of_match_node(rk3x_i2c_match, np); i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data; - if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", - &i2c->scl_frequency)) { - dev_info(&pdev->dev, "using default SCL frequency: %d\n", - DEFAULT_SCL_RATE); - i2c->scl_frequency = DEFAULT_SCL_RATE; - } - - if (i2c->scl_frequency == 0 || i2c->scl_frequency > 400 * 1000) { - dev_warn(&pdev->dev, "invalid SCL frequency specified.\n"); - dev_warn(&pdev->dev, "using default SCL frequency: %d\n", - DEFAULT_SCL_RATE); - i2c->scl_frequency = DEFAULT_SCL_RATE; - } - - /* - * Read rise and fall time from device tree. If not available use - * the default maximum timing from the specification. - */ - if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-rising-time-ns", - &i2c->scl_rise_ns)) { - if (i2c->scl_frequency <= 100000) - i2c->scl_rise_ns = 1000; - else - i2c->scl_rise_ns = 300; - } - if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-falling-time-ns", - &i2c->scl_fall_ns)) - i2c->scl_fall_ns = 300; - if (of_property_read_u32(pdev->dev.of_node, "i2c-sda-falling-time-ns", - &i2c->sda_fall_ns)) - i2c->sda_fall_ns = i2c->scl_fall_ns; + /* use common interface to get I2C timing properties */ + i2c_parse_fw_timings(&pdev->dev, &i2c->t, true); strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; -- cgit From 5057e8e07f2521649eb444492433f419f93de37a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 11 Apr 2016 11:57:17 -0700 Subject: eeprom: at24: remove a reduntant if The second check for I2C_FUNC_I2C is reduntant, so remove it. Signed-off-by: Bartosz Golaszewski [wsa: reworded commit message] Signed-off-by: Wolfram Sang --- drivers/misc/eeprom/at24.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 089d6943f68a..001a9af8e36c 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -544,10 +544,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) } else { return -EPFNOSUPPORT; } - } - /* Use I2C operations unless we're stuck with SMBus extensions. */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA; -- cgit From 1d98d0ec0ef3594901c2356773c191304703f17e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 11 Apr 2016 11:57:21 -0700 Subject: eeprom: at24: replace msleep() with usleep_range() We cannot expect msleep(1) to actually sleep for a period shorter than 20 ms. Replace all calls to msleep() with usleep_range(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Wolfram Sang --- drivers/misc/eeprom/at24.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 001a9af8e36c..6cc17b7779a5 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -245,8 +245,7 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, if (status == count) return count; - /* REVISIT: at HZ=100, this is sloooow */ - msleep(1); + usleep_range(1000, 1500); } while (time_before(read_time, timeout)); return -ETIMEDOUT; @@ -365,8 +364,7 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf, if (status == count) return count; - /* REVISIT: at HZ=100, this is sloooow */ - msleep(1); + usleep_range(1000, 1500); } while (time_before(write_time, timeout)); return -ETIMEDOUT; -- cgit From a7ab72390b77062420fb50e4451f71c9321aae05 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:38:50 +0200 Subject: i2c: mux: add common data for every i2c-mux instance All i2c-muxes have a parent adapter and one or many child adapters. A mux also has some means of selection. Previously, this was stored per child adapter, but it is only needed to keep track of this per mux. Add an i2c mux core, that keeps track of this consistently. Also add some glue for users of the old interface, which will create one implicit mux core per child adapter. Signed-off-by: Peter Rosin Tested-by: Antti Palosaari Tested-by: Crestez Dan Leonard Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-mux.c | 175 ++++++++++++++++++++++++++++++++++++------------ include/linux/i2c-mux.h | 34 ++++++++++ 2 files changed, 168 insertions(+), 41 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index d4022878b2f0..5ce1b0704cb5 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -28,33 +28,34 @@ #include /* multiplexer per channel data */ +struct i2c_mux_priv_old { + void *mux_priv; + int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); + int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); +}; + struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; - - struct i2c_adapter *parent; - struct device *mux_dev; - void *mux_priv; + struct i2c_mux_core *muxc; u32 chan_id; - - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); }; static int i2c_mux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Switch to the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = __i2c_transfer(parent, msgs, num); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -65,17 +66,18 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, int size, union i2c_smbus_data *data) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; int ret; /* Select the right mux port and perform the transfer. */ - ret = priv->select(parent, priv->mux_priv, priv->chan_id); + ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) ret = parent->algo->smbus_xfer(parent, addr, flags, read_write, command, size, data); - if (priv->deselect) - priv->deselect(parent, priv->mux_priv, priv->chan_id); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); return ret; } @@ -84,7 +86,7 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, static u32 i2c_mux_functionality(struct i2c_adapter *adap) { struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_adapter *parent = priv->parent; + struct i2c_adapter *parent = priv->muxc->parent; return parent->algo->functionality(parent); } @@ -102,6 +104,20 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); + + return priv->select(muxc->parent, priv->mux_priv, chan); +} + +static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); + + return priv->deselect(muxc->parent, priv->mux_priv, chan); +} + struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, struct device *mux_dev, void *mux_priv, u32 force_nr, u32 chan_id, @@ -111,21 +127,77 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, int (*deselect) (struct i2c_adapter *, void *, u32)) { + struct i2c_mux_core *muxc; + struct i2c_mux_priv_old *priv; + int ret; + + muxc = i2c_mux_alloc(parent, mux_dev, 1, sizeof(*priv), 0, + i2c_mux_select, i2c_mux_deselect); + if (!muxc) + return NULL; + + priv = i2c_mux_priv(muxc); + priv->select = select; + priv->deselect = deselect; + priv->mux_priv = mux_priv; + + ret = i2c_mux_add_adapter(muxc, force_nr, chan_id, class); + if (ret) { + devm_kfree(mux_dev, muxc); + return NULL; + } + + return muxc->adapter[0]; +} +EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); + +struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + struct device *dev, int max_adapters, + int sizeof_priv, u32 flags, + int (*select)(struct i2c_mux_core *, u32), + int (*deselect)(struct i2c_mux_core *, u32)) +{ + struct i2c_mux_core *muxc; + + muxc = devm_kzalloc(dev, sizeof(*muxc) + + max_adapters * sizeof(muxc->adapter[0]) + + sizeof_priv, GFP_KERNEL); + if (!muxc) + return NULL; + if (sizeof_priv) + muxc->priv = &muxc->adapter[max_adapters]; + + muxc->parent = parent; + muxc->dev = dev; + muxc->select = select; + muxc->deselect = deselect; + muxc->max_adapters = max_adapters; + + return muxc; +} +EXPORT_SYMBOL_GPL(i2c_mux_alloc); + +int i2c_mux_add_adapter(struct i2c_mux_core *muxc, + u32 force_nr, u32 chan_id, + unsigned int class) +{ + struct i2c_adapter *parent = muxc->parent; struct i2c_mux_priv *priv; char symlink_name[20]; int ret; - priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL); + if (muxc->num_adapters >= muxc->max_adapters) { + dev_err(muxc->dev, "No room for more i2c-mux adapters\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) - return NULL; + return -ENOMEM; /* Set up private adapter data */ - priv->parent = parent; - priv->mux_dev = mux_dev; - priv->mux_priv = mux_priv; + priv->muxc = muxc; priv->chan_id = chan_id; - priv->select = select; - priv->deselect = deselect; /* Need to do algo dynamically because we don't know ahead * of time what sort of physical adapter we'll be dealing with. @@ -159,11 +231,11 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, * Try to populate the mux adapter's of_node, expands to * nothing if !CONFIG_OF. */ - if (mux_dev->of_node) { + if (muxc->dev->of_node) { struct device_node *child; u32 reg; - for_each_child_of_node(mux_dev->of_node, child) { + for_each_child_of_node(muxc->dev->of_node, child) { ret = of_property_read_u32(child, "reg", ®); if (ret) continue; @@ -177,8 +249,9 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, /* * Associate the mux channel with an ACPI node. */ - if (has_acpi_companion(mux_dev)) - acpi_preset_companion(&priv->adap.dev, ACPI_COMPANION(mux_dev), + if (has_acpi_companion(muxc->dev)) + acpi_preset_companion(&priv->adap.dev, + ACPI_COMPANION(muxc->dev), chan_id); if (force_nr) { @@ -192,33 +265,53 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, "failed to add mux-adapter (error=%d)\n", ret); kfree(priv); - return NULL; + return ret; } - WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"), - "can't create symlink to mux device\n"); + WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj, + "mux_device"), + "can't create symlink to mux device\n"); snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); - WARN(sysfs_create_link(&mux_dev->kobj, &priv->adap.dev.kobj, symlink_name), - "can't create symlink for channel %u\n", chan_id); + WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj, + symlink_name), + "can't create symlink for channel %u\n", chan_id); dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", i2c_adapter_id(&priv->adap)); - return &priv->adap; + muxc->adapter[muxc->num_adapters++] = &priv->adap; + return 0; } -EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); +EXPORT_SYMBOL_GPL(i2c_mux_add_adapter); -void i2c_del_mux_adapter(struct i2c_adapter *adap) +void i2c_mux_del_adapters(struct i2c_mux_core *muxc) { - struct i2c_mux_priv *priv = adap->algo_data; char symlink_name[20]; - snprintf(symlink_name, sizeof(symlink_name), "channel-%u", priv->chan_id); - sysfs_remove_link(&priv->mux_dev->kobj, symlink_name); + while (muxc->num_adapters) { + struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters]; + struct i2c_mux_priv *priv = adap->algo_data; + + muxc->adapter[muxc->num_adapters] = NULL; + + snprintf(symlink_name, sizeof(symlink_name), + "channel-%u", priv->chan_id); + sysfs_remove_link(&muxc->dev->kobj, symlink_name); + + sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); + i2c_del_adapter(adap); + kfree(priv); + } +} +EXPORT_SYMBOL_GPL(i2c_mux_del_adapters); + +void i2c_del_mux_adapter(struct i2c_adapter *adap) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; - sysfs_remove_link(&priv->adap.dev.kobj, "mux_device"); - i2c_del_adapter(adap); - kfree(priv); + i2c_mux_del_adapters(muxc); + devm_kfree(muxc->dev, muxc); } EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index b5f9a007a3ab..71ac1b3f4f68 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -27,6 +27,31 @@ #ifdef __KERNEL__ +struct i2c_mux_core { + struct i2c_adapter *parent; + struct device *dev; + + void *priv; + + int (*select)(struct i2c_mux_core *, u32 chan_id); + int (*deselect)(struct i2c_mux_core *, u32 chan_id); + + int num_adapters; + int max_adapters; + struct i2c_adapter *adapter[0]; +}; + +struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, + struct device *dev, int max_adapters, + int sizeof_priv, u32 flags, + int (*select)(struct i2c_mux_core *, u32), + int (*deselect)(struct i2c_mux_core *, u32)); + +static inline void *i2c_mux_priv(struct i2c_mux_core *muxc) +{ + return muxc->priv; +} + /* * Called to create a i2c bus on a multiplexed bus segment. * The mux_dev and chan_id parameters are passed to the select @@ -41,8 +66,17 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, void *mux_dev, u32 chan_id), int (*deselect) (struct i2c_adapter *, void *mux_dev, u32 chan_id)); +/* + * Called to create an i2c bus on a multiplexed bus segment. + * The chan_id parameter is passed to the select and deselect + * callback functions to perform hardware-specific mux control. + */ +int i2c_mux_add_adapter(struct i2c_mux_core *muxc, + u32 force_nr, u32 chan_id, + unsigned int class); void i2c_del_mux_adapter(struct i2c_adapter *adap); +void i2c_mux_del_adapters(struct i2c_mux_core *muxc); #endif /* __KERNEL__ */ -- cgit From bb44814763ff4f69d83818342c2b4ba09efbab0f Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:39:05 +0200 Subject: i2c: i2c-mux-gpio: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-gpio.c | 55 ++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index b8e11c16d98c..f6270ee934f9 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -18,8 +18,6 @@ #include struct gpiomux { - struct i2c_adapter *parent; - struct i2c_adapter **adap; /* child busses */ struct i2c_mux_gpio_platform_data data; unsigned gpio_base; }; @@ -33,18 +31,18 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) val & (1 << i)); } -static int i2c_mux_gpio_select(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) { - struct gpiomux *mux = data; + struct gpiomux *mux = i2c_mux_priv(muxc); i2c_mux_gpio_set(mux, chan); return 0; } -static int i2c_mux_gpio_deselect(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct gpiomux *mux = data; + struct gpiomux *mux = i2c_mux_priv(muxc); i2c_mux_gpio_set(mux, mux->data.idle); @@ -136,19 +134,15 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, static int i2c_mux_gpio_probe(struct platform_device *pdev) { + struct i2c_mux_core *muxc; struct gpiomux *mux; struct i2c_adapter *parent; - int (*deselect) (struct i2c_adapter *, void *, u32); unsigned initial_state, gpio_base; int i, ret; mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) { - dev_err(&pdev->dev, "Cannot allocate gpiomux structure"); + if (!mux) return -ENOMEM; - } - - platform_set_drvdata(pdev, mux); if (!dev_get_platdata(&pdev->dev)) { ret = i2c_mux_gpio_probe_dt(mux, pdev); @@ -180,24 +174,23 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) if (!parent) return -EPROBE_DEFER; - mux->parent = parent; - mux->gpio_base = gpio_base; - - mux->adap = devm_kzalloc(&pdev->dev, - sizeof(*mux->adap) * mux->data.n_values, - GFP_KERNEL); - if (!mux->adap) { - dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); + muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0, + i2c_mux_gpio_select, NULL); + if (!muxc) { ret = -ENOMEM; goto alloc_failed; } + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); + + mux->gpio_base = gpio_base; if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { initial_state = mux->data.idle; - deselect = i2c_mux_gpio_deselect; + muxc->deselect = i2c_mux_gpio_deselect; } else { initial_state = mux->data.values[0]; - deselect = NULL; } for (i = 0; i < mux->data.n_gpios; i++) { @@ -223,11 +216,8 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; - mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, - mux->data.values[i], class, - i2c_mux_gpio_select, deselect); - if (!mux->adap[i]) { - ret = -ENODEV; + ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class); + if (ret) { dev_err(&pdev->dev, "Failed to add adapter %d\n", i); goto add_adapter_failed; } @@ -239,8 +229,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) return 0; add_adapter_failed: - for (; i > 0; i--) - i2c_del_mux_adapter(mux->adap[i - 1]); + i2c_mux_del_adapters(muxc); i = mux->data.n_gpios; err_request_gpio: for (; i > 0; i--) @@ -253,16 +242,16 @@ alloc_failed: static int i2c_mux_gpio_remove(struct platform_device *pdev) { - struct gpiomux *mux = platform_get_drvdata(pdev); + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); + struct gpiomux *mux = i2c_mux_priv(muxc); int i; - for (i = 0; i < mux->data.n_values; i++) - i2c_del_mux_adapter(mux->adap[i]); + i2c_mux_del_adapters(muxc); for (i = 0; i < mux->data.n_gpios; i++) gpio_free(mux->gpio_base + mux->data.gpios[i]); - i2c_put_adapter(mux->parent); + i2c_put_adapter(muxc->parent); return 0; } -- cgit From 4bbe7fb0a23244937be315866ae166ba74e32d06 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:39:31 +0200 Subject: i2c: i2c-mux-pinctrl: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-pinctrl.c | 83 ++++++++++++++----------------------- 1 file changed, 30 insertions(+), 53 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index b5a982ba8898..1b8dc711815e 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -26,34 +26,29 @@ #include struct i2c_mux_pinctrl { - struct device *dev; struct i2c_mux_pinctrl_platform_data *pdata; struct pinctrl *pinctrl; struct pinctrl_state **states; struct pinctrl_state *state_idle; - struct i2c_adapter *parent; - struct i2c_adapter **busses; }; -static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = data; + struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); return pinctrl_select_state(mux->pinctrl, mux->states[chan]); } -static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_mux_pinctrl *mux = data; + struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); return pinctrl_select_state(mux->pinctrl, mux->state_idle); } #ifdef CONFIG_OF static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, - struct platform_device *pdev) + struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; int num_names, i, ret; @@ -64,15 +59,12 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, return 0; mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); - if (!mux->pdata) { - dev_err(mux->dev, - "Cannot allocate i2c_mux_pinctrl_platform_data\n"); + if (!mux->pdata) return -ENOMEM; - } num_names = of_property_count_strings(np, "pinctrl-names"); if (num_names < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", num_names); return num_names; } @@ -80,23 +72,22 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata->pinctrl_states) * num_names, GFP_KERNEL); - if (!mux->pdata->pinctrl_states) { - dev_err(mux->dev, "Cannot allocate pinctrl_states\n"); + if (!mux->pdata->pinctrl_states) return -ENOMEM; - } for (i = 0; i < num_names; i++) { ret = of_property_read_string_index(np, "pinctrl-names", i, &mux->pdata->pinctrl_states[mux->pdata->bus_count]); if (ret < 0) { - dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", + dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", ret); return ret; } if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], "idle")) { if (i != num_names - 1) { - dev_err(mux->dev, "idle state must be last\n"); + dev_err(&pdev->dev, + "idle state must be last\n"); return -EINVAL; } mux->pdata->pinctrl_state_idle = "idle"; @@ -107,13 +98,13 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, adapter_np = of_parse_phandle(np, "i2c-parent", 0); if (!adapter_np) { - dev_err(mux->dev, "Cannot parse i2c-parent\n"); + dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); return -ENODEV; } adapter = of_find_i2c_adapter_by_node(adapter_np); of_node_put(adapter_np); if (!adapter) { - dev_err(mux->dev, "Cannot find parent bus\n"); + dev_err(&pdev->dev, "Cannot find parent bus\n"); return -EPROBE_DEFER; } mux->pdata->parent_bus_num = i2c_adapter_id(adapter); @@ -131,19 +122,15 @@ static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, static int i2c_mux_pinctrl_probe(struct platform_device *pdev) { + struct i2c_mux_core *muxc; struct i2c_mux_pinctrl *mux; - int (*deselect)(struct i2c_adapter *, void *, u32); int i, ret; mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); if (!mux) { - dev_err(&pdev->dev, "Cannot allocate i2c_mux_pinctrl\n"); ret = -ENOMEM; goto err; } - platform_set_drvdata(pdev, mux); - - mux->dev = &pdev->dev; mux->pdata = dev_get_platdata(&pdev->dev); if (!mux->pdata) { @@ -166,14 +153,15 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) goto err; } - mux->busses = devm_kzalloc(&pdev->dev, - sizeof(*mux->busses) * mux->pdata->bus_count, - GFP_KERNEL); - if (!mux->busses) { - dev_err(&pdev->dev, "Cannot allocate busses\n"); + muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0, + i2c_mux_pinctrl_select, NULL); + if (!muxc) { ret = -ENOMEM; goto err; } + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); mux->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(mux->pinctrl)) { @@ -203,13 +191,11 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) goto err; } - deselect = i2c_mux_pinctrl_deselect; - } else { - deselect = NULL; + muxc->deselect = i2c_mux_pinctrl_deselect; } - mux->parent = i2c_get_adapter(mux->pdata->parent_bus_num); - if (!mux->parent) { + muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num); + if (!muxc->parent) { dev_err(&pdev->dev, "Parent adapter (%d) not found\n", mux->pdata->parent_bus_num); ret = -EPROBE_DEFER; @@ -220,12 +206,8 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) u32 bus = mux->pdata->base_bus_num ? (mux->pdata->base_bus_num + i) : 0; - mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, - mux, bus, i, 0, - i2c_mux_pinctrl_select, - deselect); - if (!mux->busses[i]) { - ret = -ENODEV; + ret = i2c_mux_add_adapter(muxc, bus, i, 0); + if (ret) { dev_err(&pdev->dev, "Failed to add adapter %d\n", i); goto err_del_adapter; } @@ -234,23 +216,18 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) return 0; err_del_adapter: - for (; i > 0; i--) - i2c_del_mux_adapter(mux->busses[i - 1]); - i2c_put_adapter(mux->parent); + i2c_mux_del_adapters(muxc); + i2c_put_adapter(muxc->parent); err: return ret; } static int i2c_mux_pinctrl_remove(struct platform_device *pdev) { - struct i2c_mux_pinctrl *mux = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < mux->pdata->bus_count; i++) - i2c_del_mux_adapter(mux->busses[i]); - - i2c_put_adapter(mux->parent); + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); + i2c_mux_del_adapters(muxc); + i2c_put_adapter(muxc->parent); return 0; } -- cgit From 8aacd90166f6b60df83e0907b1e931e3971a92ab Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:39:46 +0200 Subject: i2c: i2c-arb-gpio-challenge: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 47 +++++++++++++----------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c index 402e3a6c671a..a90bbc4037dd 100644 --- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -28,8 +28,6 @@ /** * struct i2c_arbitrator_data - Driver data for I2C arbitrator * - * @parent: Parent adapter - * @child: Child bus * @our_gpio: GPIO we'll use to claim. * @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO == * this then consider it released. @@ -42,8 +40,6 @@ */ struct i2c_arbitrator_data { - struct i2c_adapter *parent; - struct i2c_adapter *child; int our_gpio; int our_gpio_release; int their_gpio; @@ -59,9 +55,9 @@ struct i2c_arbitrator_data { * * Use the GPIO-based signalling protocol; return -EBUSY if we fail. */ -static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan) +static int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan) { - const struct i2c_arbitrator_data *arb = data; + const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc); unsigned long stop_retry, stop_time; /* Start a round of trying to claim the bus */ @@ -93,7 +89,7 @@ static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan) /* Give up, release our claim */ gpio_set_value(arb->our_gpio, arb->our_gpio_release); udelay(arb->slew_delay_us); - dev_err(&adap->dev, "Could not claim bus, timeout\n"); + dev_err(muxc->dev, "Could not claim bus, timeout\n"); return -EBUSY; } @@ -102,10 +98,9 @@ static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan) * * Release the I2C bus using the GPIO-based signalling protocol. */ -static int i2c_arbitrator_deselect(struct i2c_adapter *adap, void *data, - u32 chan) +static int i2c_arbitrator_deselect(struct i2c_mux_core *muxc, u32 chan) { - const struct i2c_arbitrator_data *arb = data; + const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc); /* Release the bus and wait for the other master to notice */ gpio_set_value(arb->our_gpio, arb->our_gpio_release); @@ -119,6 +114,7 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device_node *parent_np; + struct i2c_mux_core *muxc; struct i2c_arbitrator_data *arb; enum of_gpio_flags gpio_flags; unsigned long out_init; @@ -134,12 +130,13 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) return -EINVAL; } - arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL); - if (!arb) { - dev_err(dev, "Cannot allocate i2c_arbitrator_data\n"); + muxc = i2c_mux_alloc(NULL, dev, 1, sizeof(*arb), 0, + i2c_arbitrator_select, i2c_arbitrator_deselect); + if (!muxc) return -ENOMEM; - } - platform_set_drvdata(pdev, arb); + arb = i2c_mux_priv(muxc); + + platform_set_drvdata(pdev, muxc); /* Request GPIOs */ ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags); @@ -196,21 +193,18 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) dev_err(dev, "Cannot parse i2c-parent\n"); return -EINVAL; } - arb->parent = of_get_i2c_adapter_by_node(parent_np); + muxc->parent = of_get_i2c_adapter_by_node(parent_np); of_node_put(parent_np); - if (!arb->parent) { + if (!muxc->parent) { dev_err(dev, "Cannot find parent bus\n"); return -EPROBE_DEFER; } /* Actually add the mux adapter */ - arb->child = i2c_add_mux_adapter(arb->parent, dev, arb, 0, 0, 0, - i2c_arbitrator_select, - i2c_arbitrator_deselect); - if (!arb->child) { + ret = i2c_mux_add_adapter(muxc, 0, 0, 0); + if (ret) { dev_err(dev, "Failed to add adapter\n"); - ret = -ENODEV; - i2c_put_adapter(arb->parent); + i2c_put_adapter(muxc->parent); } return ret; @@ -218,11 +212,10 @@ static int i2c_arbitrator_probe(struct platform_device *pdev) static int i2c_arbitrator_remove(struct platform_device *pdev) { - struct i2c_arbitrator_data *arb = platform_get_drvdata(pdev); - - i2c_del_mux_adapter(arb->child); - i2c_put_adapter(arb->parent); + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); + i2c_mux_del_adapters(muxc); + i2c_put_adapter(muxc->parent); return 0; } -- cgit From ab88b97c69ce375ef10c551133d6d19ffd55d763 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:40:02 +0200 Subject: i2c: i2c-mux-pca9541: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-pca9541.c | 58 +++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index d0ba424adebc..3cb8af635db5 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -73,7 +73,7 @@ #define SELECT_DELAY_LONG 1000 struct pca9541 { - struct i2c_adapter *mux_adap; + struct i2c_client *client; unsigned long select_timeout; unsigned long arb_timeout; }; @@ -217,7 +217,8 @@ static const u8 pca9541_control[16] = { */ static int pca9541_arbitrate(struct i2c_client *client) { - struct pca9541 *data = i2c_get_clientdata(client); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); int reg; reg = pca9541_reg_read(client, PCA9541_CONTROL); @@ -285,9 +286,10 @@ static int pca9541_arbitrate(struct i2c_client *client) return 0; } -static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan) +static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan) { - struct pca9541 *data = i2c_get_clientdata(client); + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; int ret; unsigned long timeout = jiffies + ARB2_TIMEOUT; /* give up after this time */ @@ -309,9 +311,11 @@ static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan) return -ETIMEDOUT; } -static int pca9541_release_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan) { + struct pca9541 *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + pca9541_release_bus(client); return 0; } @@ -324,20 +328,13 @@ static int pca9541_probe(struct i2c_client *client, { struct i2c_adapter *adap = client->adapter; struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev); + struct i2c_mux_core *muxc; struct pca9541 *data; int force; - int ret = -ENODEV; + int ret; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA)) - goto err; - - data = kzalloc(sizeof(struct pca9541), GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err; - } - - i2c_set_clientdata(client, data); + return -ENODEV; /* * I2C accesses are unprotected here. @@ -352,34 +349,33 @@ static int pca9541_probe(struct i2c_client *client, force = 0; if (pdata) force = pdata->modes[0].adap_id; - data->mux_adap = i2c_add_mux_adapter(adap, &client->dev, client, - force, 0, 0, - pca9541_select_chan, - pca9541_release_chan); + muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), 0, + pca9541_select_chan, pca9541_release_chan); + if (!muxc) + return -ENOMEM; - if (data->mux_adap == NULL) { + data = i2c_mux_priv(muxc); + data->client = client; + + i2c_set_clientdata(client, muxc); + + ret = i2c_mux_add_adapter(muxc, force, 0, 0); + if (ret) { dev_err(&client->dev, "failed to register master selector\n"); - goto exit_free; + return ret; } dev_info(&client->dev, "registered master selector for I2C %s\n", client->name); return 0; - -exit_free: - kfree(data); -err: - return ret; } static int pca9541_remove(struct i2c_client *client) { - struct pca9541 *data = i2c_get_clientdata(client); - - i2c_del_mux_adapter(data->mux_adap); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); - kfree(data); + i2c_mux_del_adapters(muxc); return 0; } -- cgit From 7fcac980717532a20762e03f0d228bfc58393ed3 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:40:14 +0200 Subject: i2c: i2c-mux-pca954x: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Add a mask to handle the case where not all child adapters should cause a mux deselect to happen, now that there is a common deselect op for all child adapters. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-pca954x.c | 61 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index acfcef3d4068..528e755c468f 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -60,9 +60,10 @@ enum pca_type { struct pca954x { enum pca_type type; - struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS]; u8 last_chan; /* last register value */ + u8 deselect; + struct i2c_client *client; }; struct chip_desc { @@ -146,10 +147,10 @@ static int pca954x_reg_write(struct i2c_adapter *adap, return ret; } -static int pca954x_select_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan) { - struct pca954x *data = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; const struct chip_desc *chip = &chips[data->type]; u8 regval; int ret = 0; @@ -162,21 +163,24 @@ static int pca954x_select_chan(struct i2c_adapter *adap, /* Only select the channel if its different from the last channel */ if (data->last_chan != regval) { - ret = pca954x_reg_write(adap, client, regval); + ret = pca954x_reg_write(muxc->parent, client, regval); data->last_chan = regval; } return ret; } -static int pca954x_deselect_mux(struct i2c_adapter *adap, - void *client, u32 chan) +static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan) { - struct pca954x *data = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); + struct i2c_client *client = data->client; + + if (!(data->deselect & (1 << chan))) + return 0; /* Deselect active channel */ data->last_chan = 0; - return pca954x_reg_write(adap, client, data->last_chan); + return pca954x_reg_write(muxc->parent, client, data->last_chan); } /* @@ -191,17 +195,22 @@ static int pca954x_probe(struct i2c_client *client, bool idle_disconnect_dt; struct gpio_desc *gpio; int num, force, class; + struct i2c_mux_core *muxc; struct pca954x *data; int ret; if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(struct pca954x), GFP_KERNEL); - if (!data) + muxc = i2c_mux_alloc(adap, &client->dev, + PCA954X_MAX_NCHANS, sizeof(*data), 0, + pca954x_select_chan, pca954x_deselect_mux); + if (!muxc) return -ENOMEM; + data = i2c_mux_priv(muxc); - i2c_set_clientdata(client, data); + i2c_set_clientdata(client, muxc); + data->client = client; /* Get the mux out of reset if a reset GPIO is specified. */ gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); @@ -238,16 +247,13 @@ static int pca954x_probe(struct i2c_client *client, /* discard unconfigured channels */ break; idle_disconnect_pd = pdata->modes[num].deselect_on_exit; + data->deselect |= (idle_disconnect_pd + || idle_disconnect_dt) << num; } - data->virt_adaps[num] = - i2c_add_mux_adapter(adap, &client->dev, client, - force, num, class, pca954x_select_chan, - (idle_disconnect_pd || idle_disconnect_dt) - ? pca954x_deselect_mux : NULL); + ret = i2c_mux_add_adapter(muxc, force, num, class); - if (data->virt_adaps[num] == NULL) { - ret = -ENODEV; + if (ret) { dev_err(&client->dev, "failed to register multiplexed adapter" " %d as bus %d\n", num, force); @@ -263,23 +269,15 @@ static int pca954x_probe(struct i2c_client *client, return 0; virt_reg_failed: - for (num--; num >= 0; num--) - i2c_del_mux_adapter(data->virt_adaps[num]); + i2c_mux_del_adapters(muxc); return ret; } static int pca954x_remove(struct i2c_client *client) { - struct pca954x *data = i2c_get_clientdata(client); - const struct chip_desc *chip = &chips[data->type]; - int i; - - for (i = 0; i < chip->nchans; ++i) - if (data->virt_adaps[i]) { - i2c_del_mux_adapter(data->virt_adaps[i]); - data->virt_adaps[i] = NULL; - } + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + i2c_mux_del_adapters(muxc); return 0; } @@ -287,7 +285,8 @@ static int pca954x_remove(struct i2c_client *client) static int pca954x_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct pca954x *data = i2c_get_clientdata(client); + struct i2c_mux_core *muxc = i2c_get_clientdata(client); + struct pca954x *data = i2c_mux_priv(muxc); data->last_chan = 0; return i2c_smbus_write_byte(client, 0); -- cgit From 193304aef8523265884f08144b1f98ad272e2d7e Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:40:27 +0200 Subject: i2c: i2c-mux-reg: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-reg.c | 69 +++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index 5fbd5bd0878f..6773cadf7c9f 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -21,8 +21,6 @@ #include struct regmux { - struct i2c_adapter *parent; - struct i2c_adapter **adap; /* child busses */ struct i2c_mux_reg_platform_data data; }; @@ -64,18 +62,16 @@ static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id) return 0; } -static int i2c_mux_reg_select(struct i2c_adapter *adap, void *data, - unsigned int chan) +static int i2c_mux_reg_select(struct i2c_mux_core *muxc, u32 chan) { - struct regmux *mux = data; + struct regmux *mux = i2c_mux_priv(muxc); return i2c_mux_reg_set(mux, chan); } -static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data, - unsigned int chan) +static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct regmux *mux = data; + struct regmux *mux = i2c_mux_priv(muxc); if (mux->data.idle_in_use) return i2c_mux_reg_set(mux, mux->data.idle); @@ -85,7 +81,7 @@ static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data, #ifdef CONFIG_OF static int i2c_mux_reg_probe_dt(struct regmux *mux, - struct platform_device *pdev) + struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device_node *adapter_np, *child; @@ -107,7 +103,6 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, if (!adapter) return -EPROBE_DEFER; - mux->parent = adapter; mux->data.parent = i2c_adapter_id(adapter); put_device(&adapter->dev); @@ -161,7 +156,7 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, } #else static int i2c_mux_reg_probe_dt(struct regmux *mux, - struct platform_device *pdev) + struct platform_device *pdev) { return 0; } @@ -169,10 +164,10 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux, static int i2c_mux_reg_probe(struct platform_device *pdev) { + struct i2c_mux_core *muxc; struct regmux *mux; struct i2c_adapter *parent; struct resource *res; - int (*deselect)(struct i2c_adapter *, void *, u32); unsigned int class; int i, ret, nr; @@ -180,17 +175,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) if (!mux) return -ENOMEM; - platform_set_drvdata(pdev, mux); - if (dev_get_platdata(&pdev->dev)) { memcpy(&mux->data, dev_get_platdata(&pdev->dev), sizeof(mux->data)); - - parent = i2c_get_adapter(mux->data.parent); - if (!parent) - return -EPROBE_DEFER; - - mux->parent = parent; } else { ret = i2c_mux_reg_probe_dt(mux, pdev); if (ret < 0) { @@ -199,6 +186,10 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) } } + parent = i2c_get_adapter(mux->data.parent); + if (!parent) + return -EPROBE_DEFER; + if (!mux->data.reg) { dev_info(&pdev->dev, "Register not set, using platform resource\n"); @@ -215,55 +206,45 @@ static int i2c_mux_reg_probe(struct platform_device *pdev) return -EINVAL; } - mux->adap = devm_kzalloc(&pdev->dev, - sizeof(*mux->adap) * mux->data.n_values, - GFP_KERNEL); - if (!mux->adap) { - dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); + muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0, + i2c_mux_reg_select, NULL); + if (!muxc) return -ENOMEM; - } + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); if (mux->data.idle_in_use) - deselect = i2c_mux_reg_deselect; - else - deselect = NULL; + muxc->deselect = i2c_mux_reg_deselect; for (i = 0; i < mux->data.n_values; i++) { nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; class = mux->data.classes ? mux->data.classes[i] : 0; - mux->adap[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, mux, - nr, mux->data.values[i], - class, i2c_mux_reg_select, - deselect); - if (!mux->adap[i]) { - ret = -ENODEV; + ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class); + if (ret) { dev_err(&pdev->dev, "Failed to add adapter %d\n", i); goto add_adapter_failed; } } dev_dbg(&pdev->dev, "%d port mux on %s adapter\n", - mux->data.n_values, mux->parent->name); + mux->data.n_values, muxc->parent->name); return 0; add_adapter_failed: - for (; i > 0; i--) - i2c_del_mux_adapter(mux->adap[i - 1]); + i2c_mux_del_adapters(muxc); return ret; } static int i2c_mux_reg_remove(struct platform_device *pdev) { - struct regmux *mux = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < mux->data.n_values; i++) - i2c_del_mux_adapter(mux->adap[i]); + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); - i2c_put_adapter(mux->parent); + i2c_mux_del_adapters(muxc); + i2c_put_adapter(muxc->parent); return 0; } -- cgit From 51f97f6dd73d9349f8e6a1d36ac5f7371d275fb3 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:40:49 +0200 Subject: iio: imu: inv_mpu6050: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Acked-by: Jonathan Cameron Tested-by: Crestez Dan Leonard Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 1 - drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 33 +++++++++++++++--------------- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 3 ++- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index 2771106fd650..f62b8bd9ad7e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -183,7 +183,7 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client) } else return 0; /* no secondary addr, which is OK */ } - st->mux_client = i2c_new_device(st->mux_adapter, &info); + st->mux_client = i2c_new_device(st->muxc->adapter[0], &info); if (!st->mux_client) return -ENODEV; } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index d192953e9a38..0c2bded2b5b7 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include "inv_mpu_iio.h" diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index f581256d9d4c..3a078df84224 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include "inv_mpu_iio.h" @@ -52,10 +51,9 @@ static int inv_mpu6050_write_reg_unlocked(struct i2c_client *client, return 0; } -static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv, - u32 chan_id) +static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); int ret = 0; @@ -84,10 +82,9 @@ write_error: return ret; } -static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap, - void *mux_priv, u32 chan_id) +static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); @@ -136,16 +133,18 @@ static int inv_mpu_probe(struct i2c_client *client, return result; st = iio_priv(dev_get_drvdata(&client->dev)); - st->mux_adapter = i2c_add_mux_adapter(client->adapter, - &client->dev, - client, - 0, 0, 0, - inv_mpu6050_select_bypass, - inv_mpu6050_deselect_bypass); - if (!st->mux_adapter) { - result = -ENODEV; + st->muxc = i2c_mux_alloc(client->adapter, &client->dev, + 1, 0, 0, + inv_mpu6050_select_bypass, + inv_mpu6050_deselect_bypass); + if (!st->muxc) { + result = -ENOMEM; goto out_unreg_device; } + st->muxc->priv = dev_get_drvdata(&client->dev); + result = i2c_mux_add_adapter(st->muxc, 0, 0, 0); + if (result) + goto out_unreg_device; result = inv_mpu_acpi_create_mux_client(client); if (result) @@ -154,7 +153,7 @@ static int inv_mpu_probe(struct i2c_client *client, return 0; out_del_mux: - i2c_del_mux_adapter(st->mux_adapter); + i2c_mux_del_adapters(st->muxc); out_unreg_device: inv_mpu_core_remove(&client->dev); return result; @@ -166,7 +165,7 @@ static int inv_mpu_remove(struct i2c_client *client) struct inv_mpu6050_state *st = iio_priv(indio_dev); inv_mpu_acpi_delete_mux_client(client); - i2c_del_mux_adapter(st->mux_adapter); + i2c_mux_del_adapters(st->muxc); return inv_mpu_core_remove(&client->dev); } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index e302a49703bf..bb3cef6d7059 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ #include +#include #include #include #include @@ -127,7 +128,7 @@ struct inv_mpu6050_state { const struct inv_mpu6050_hw *hw; enum inv_devices chip_type; spinlock_t time_stamp_lock; - struct i2c_adapter *mux_adapter; + struct i2c_mux_core *muxc; struct i2c_client *mux_client; unsigned int powerup_count; struct inv_mpu6050_platform_data plat_data; -- cgit From e00fed40f48e43bdb5e018156d077c65b61f93bf Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:41:02 +0200 Subject: [media] m88ds3103: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Tested-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/media/dvb-frontends/m88ds3103.c | 19 +++++++++++-------- drivers/media/dvb-frontends/m88ds3103_priv.h | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 76883600ec6f..5557ef8fc704 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1251,9 +1251,9 @@ static void m88ds3103_release(struct dvb_frontend *fe) i2c_unregister_device(client); } -static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int m88ds3103_select(struct i2c_mux_core *muxc, u32 chan) { - struct m88ds3103_dev *dev = mux_priv; + struct m88ds3103_dev *dev = i2c_mux_priv(muxc); struct i2c_client *client = dev->client; int ret; struct i2c_msg msg = { @@ -1374,7 +1374,7 @@ static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - return dev->i2c_adapter; + return dev->muxc->adapter[0]; } static int m88ds3103_probe(struct i2c_client *client, @@ -1467,13 +1467,16 @@ static int m88ds3103_probe(struct i2c_client *client, goto err_kfree; /* create mux i2c adapter for tuner */ - dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev, - dev, 0, 0, 0, m88ds3103_select, - NULL); - if (dev->i2c_adapter == NULL) { + dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0, + m88ds3103_select, NULL); + if (!dev->muxc) { ret = -ENOMEM; goto err_kfree; } + dev->muxc->priv = dev; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_kfree; /* create dvb_frontend */ memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); @@ -1502,7 +1505,7 @@ static int m88ds3103_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - i2c_del_mux_adapter(dev->i2c_adapter); + i2c_mux_del_adapters(dev->muxc); kfree(dev); return 0; diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index eee8c22c51ec..c5b4e177c6ea 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -42,7 +42,7 @@ struct m88ds3103_dev { enum fe_status fe_status; u32 dvbv3_ber; /* for old DVBv3 API read_ber */ bool warm; /* FW running */ - struct i2c_adapter *i2c_adapter; + struct i2c_mux_core *muxc; /* auto detect chip id to do different config */ u8 chip_id; /* main mclk is calculated for M88RS6000 dynamically */ -- cgit From a0119159e66e2e67154384d7e20a0ebf46cfa32b Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:41:13 +0200 Subject: [media] rtl2830: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Tested-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/media/dvb-frontends/rtl2830.c | 20 ++++++++++++-------- drivers/media/dvb-frontends/rtl2830_priv.h | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index 3f96429af0e5..d25d1e0cd4ca 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -677,9 +677,9 @@ err: * adapter lock is already taken by tuner driver. * Gate is closed automatically after single I2C transfer. */ -static int rtl2830_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id) +static int rtl2830_select(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; @@ -712,7 +712,7 @@ static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - return dev->adapter; + return dev->muxc->adapter[0]; } /* @@ -865,12 +865,16 @@ static int rtl2830_probe(struct i2c_client *client, goto err_regmap_exit; /* create muxed i2c adapter for tuner */ - dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, - client, 0, 0, 0, rtl2830_select, NULL); - if (dev->adapter == NULL) { - ret = -ENODEV; + dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0, + rtl2830_select, NULL); + if (!dev->muxc) { + ret = -ENOMEM; goto err_regmap_exit; } + dev->muxc->priv = client; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_regmap_exit; /* create dvb frontend */ memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops)); @@ -903,7 +907,7 @@ static int rtl2830_remove(struct i2c_client *client) /* stop statistics polling */ cancel_delayed_work_sync(&dev->stat_work); - i2c_del_mux_adapter(dev->adapter); + i2c_mux_del_adapters(dev->muxc); regmap_exit(dev->regmap); kfree(dev); diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h index cf793f39a09b..da4909543da2 100644 --- a/drivers/media/dvb-frontends/rtl2830_priv.h +++ b/drivers/media/dvb-frontends/rtl2830_priv.h @@ -29,7 +29,7 @@ struct rtl2830_dev { struct rtl2830_platform_data *pdata; struct i2c_client *client; struct regmap *regmap; - struct i2c_adapter *adapter; + struct i2c_mux_core *muxc; struct dvb_frontend fe; bool sleeping; unsigned long filters; -- cgit From cddcc40b1b1553010acb89add84c64b5d123ec94 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:41:25 +0200 Subject: [media] rtl2832: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Tested-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/media/dvb-frontends/rtl2832.c | 25 ++++++++++++++----------- drivers/media/dvb-frontends/rtl2832_priv.h | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 7c96f7679669..1b23788797b5 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -847,9 +847,9 @@ err: dev_dbg(&client->dev, "failed=%d\n", ret); } -static int rtl2832_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id) +static int rtl2832_select(struct i2c_mux_core *muxc, u32 chan_id) { - struct rtl2832_dev *dev = mux_priv; + struct rtl2832_dev *dev = i2c_mux_priv(muxc); struct i2c_client *client = dev->client; int ret; @@ -870,10 +870,9 @@ err: return ret; } -static int rtl2832_deselect(struct i2c_adapter *adap, void *mux_priv, - u32 chan_id) +static int rtl2832_deselect(struct i2c_mux_core *muxc, u32 chan_id) { - struct rtl2832_dev *dev = mux_priv; + struct rtl2832_dev *dev = i2c_mux_priv(muxc); schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100)); return 0; @@ -1059,7 +1058,7 @@ static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client) struct rtl2832_dev *dev = i2c_get_clientdata(client); dev_dbg(&client->dev, "\n"); - return dev->i2c_adapter_tuner; + return dev->muxc->adapter[0]; } static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable) @@ -1242,12 +1241,16 @@ static int rtl2832_probe(struct i2c_client *client, goto err_regmap_exit; /* create muxed i2c adapter for demod tuner bus */ - dev->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, dev, - 0, 0, 0, rtl2832_select, rtl2832_deselect); - if (dev->i2c_adapter_tuner == NULL) { - ret = -ENODEV; + dev->muxc = i2c_mux_alloc(i2c, &i2c->dev, 1, 0, 0, + rtl2832_select, rtl2832_deselect); + if (!dev->muxc) { + ret = -ENOMEM; goto err_regmap_exit; } + dev->muxc->priv = dev; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_regmap_exit; /* create dvb_frontend */ memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops)); @@ -1282,7 +1285,7 @@ static int rtl2832_remove(struct i2c_client *client) cancel_delayed_work_sync(&dev->i2c_gate_work); - i2c_del_mux_adapter(dev->i2c_adapter_tuner); + i2c_mux_del_adapters(dev->muxc); regmap_exit(dev->regmap); diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index 6b875f462f8b..d8f97d14f6fd 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -36,7 +36,7 @@ struct rtl2832_dev { struct mutex regmap_mutex; struct regmap_config regmap_config; struct regmap *regmap; - struct i2c_adapter *i2c_adapter_tuner; + struct i2c_mux_core *muxc; struct dvb_frontend fe; enum fe_status fe_status; u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ -- cgit From 58d7b541dd96535b03cc9dd13fe9efff2a5402e0 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:41:36 +0200 Subject: [media] si2168: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select/deselect ops to be in terms of the i2c mux core instead of the child adapter. Tested-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/media/dvb-frontends/si2168.c | 25 +++++++++++++++---------- drivers/media/dvb-frontends/si2168_priv.h | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 821a8f481507..5583827c386e 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -615,9 +615,9 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe, * We must use unlocked I2C I/O because I2C adapter lock is already taken * by the caller (usually tuner driver). */ -static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int si2168_select(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); int ret; struct si2168_cmd cmd; @@ -635,9 +635,9 @@ err: return ret; } -static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) +static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan) { - struct i2c_client *client = mux_priv; + struct i2c_client *client = i2c_mux_priv(muxc); int ret; struct si2168_cmd cmd; @@ -709,17 +709,22 @@ static int si2168_probe(struct i2c_client *client, } /* create mux i2c adapter for tuner */ - dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, - client, 0, 0, 0, si2168_select, si2168_deselect); - if (dev->adapter == NULL) { - ret = -ENODEV; + dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, + 1, 0, 0, + si2168_select, si2168_deselect); + if (!dev->muxc) { + ret = -ENOMEM; goto err_kfree; } + dev->muxc->priv = client; + ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0); + if (ret) + goto err_kfree; /* create dvb_frontend */ memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); dev->fe.demodulator_priv = client; - *config->i2c_adapter = dev->adapter; + *config->i2c_adapter = dev->muxc->adapter[0]; *config->fe = &dev->fe; dev->ts_mode = config->ts_mode; dev->ts_clock_inv = config->ts_clock_inv; @@ -743,7 +748,7 @@ static int si2168_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); - i2c_del_mux_adapter(dev->adapter); + i2c_mux_del_adapters(dev->muxc); dev->fe.ops.release = NULL; dev->fe.demodulator_priv = NULL; diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index c07e6fe2cb10..165bf1412063 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -29,7 +29,7 @@ /* state struct */ struct si2168_dev { - struct i2c_adapter *adapter; + struct i2c_mux_core *muxc; struct dvb_frontend fe; enum fe_delivery_system delivery_system; enum fe_status fe_status; -- cgit From 05e0dfd0311bd6dd3cdb6cb32acd1e701c451e9b Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:42:17 +0200 Subject: [media] cx231xx: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Tested-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/media/usb/cx231xx/cx231xx-core.c | 6 ++-- drivers/media/usb/cx231xx/cx231xx-i2c.c | 47 ++++++++++++++++---------------- drivers/media/usb/cx231xx/cx231xx.h | 4 ++- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index f497888d94bf..f7aac2abd783 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -1304,6 +1304,9 @@ int cx231xx_dev_init(struct cx231xx *dev) cx231xx_i2c_register(&dev->i2c_bus[1]); cx231xx_i2c_register(&dev->i2c_bus[2]); + errCode = cx231xx_i2c_mux_create(dev); + if (errCode < 0) + return errCode; cx231xx_i2c_mux_register(dev, 0); cx231xx_i2c_mux_register(dev, 1); @@ -1426,8 +1429,7 @@ EXPORT_SYMBOL_GPL(cx231xx_dev_init); void cx231xx_dev_uninit(struct cx231xx *dev) { /* Un Initialize I2C bus */ - cx231xx_i2c_mux_unregister(dev, 1); - cx231xx_i2c_mux_unregister(dev, 0); + cx231xx_i2c_mux_unregister(dev); cx231xx_i2c_unregister(&dev->i2c_bus[2]); cx231xx_i2c_unregister(&dev->i2c_bus[1]); cx231xx_i2c_unregister(&dev->i2c_bus[0]); diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index a29c345b027d..473cd3433fe5 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -557,40 +557,41 @@ int cx231xx_i2c_unregister(struct cx231xx_i2c *bus) * cx231xx_i2c_mux_select() * switch i2c master number 1 between port1 and port3 */ -static int cx231xx_i2c_mux_select(struct i2c_adapter *adap, - void *mux_priv, u32 chan_id) +static int cx231xx_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan_id) { - struct cx231xx *dev = mux_priv; + struct cx231xx *dev = i2c_mux_priv(muxc); return cx231xx_enable_i2c_port_3(dev, chan_id); } +int cx231xx_i2c_mux_create(struct cx231xx *dev) +{ + dev->muxc = i2c_mux_alloc(&dev->i2c_bus[1].i2c_adap, dev->dev, 2, 0, 0, + cx231xx_i2c_mux_select, NULL); + if (!dev->muxc) + return -ENOMEM; + dev->muxc->priv = dev; + return 0; +} + int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no) { - struct i2c_adapter *i2c_parent = &dev->i2c_bus[1].i2c_adap; - /* what is the correct mux_dev? */ - struct device *mux_dev = dev->dev; - - dev->i2c_mux_adap[mux_no] = i2c_add_mux_adapter(i2c_parent, - mux_dev, - dev /* mux_priv */, - 0, - mux_no /* chan_id */, - 0 /* class */, - &cx231xx_i2c_mux_select, - NULL); - - if (!dev->i2c_mux_adap[mux_no]) + int rc; + + rc = i2c_mux_add_adapter(dev->muxc, + 0, + mux_no /* chan_id */, + 0 /* class */); + if (rc) dev_warn(dev->dev, "i2c mux %d register FAILED\n", mux_no); - return 0; + return rc; } -void cx231xx_i2c_mux_unregister(struct cx231xx *dev, int mux_no) +void cx231xx_i2c_mux_unregister(struct cx231xx *dev) { - i2c_del_mux_adapter(dev->i2c_mux_adap[mux_no]); - dev->i2c_mux_adap[mux_no] = NULL; + i2c_mux_del_adapters(dev->muxc); } struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port) @@ -603,9 +604,9 @@ struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port) case I2C_2: return &dev->i2c_bus[2].i2c_adap; case I2C_1_MUX_1: - return dev->i2c_mux_adap[0]; + return dev->muxc->adapter[0]; case I2C_1_MUX_3: - return dev->i2c_mux_adap[1]; + return dev->muxc->adapter[1]; default: return NULL; } diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index 69f6d20870f5..90c867683076 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -624,6 +624,7 @@ struct cx231xx { /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ struct cx231xx_i2c i2c_bus[3]; + struct i2c_mux_core *muxc; struct i2c_adapter *i2c_mux_adap[2]; unsigned int xc_fw_load_done:1; @@ -760,8 +761,9 @@ int cx231xx_reset_analog_tuner(struct cx231xx *dev); void cx231xx_do_i2c_scan(struct cx231xx *dev, int i2c_port); int cx231xx_i2c_register(struct cx231xx_i2c *bus); int cx231xx_i2c_unregister(struct cx231xx_i2c *bus); +int cx231xx_i2c_mux_create(struct cx231xx *dev); int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no); -void cx231xx_i2c_mux_unregister(struct cx231xx *dev, int mux_no); +void cx231xx_i2c_mux_unregister(struct cx231xx *dev); struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port); /* Internal block control functions */ -- cgit From b6e3b7171b1e416963e36bb91ff6cce1caaaf675 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Apr 2016 08:42:47 +0200 Subject: of/unittest: convert to use an explicit i2c mux core Allocate an explicit i2c mux core to handle parent and child adapters etc. Update the select op to be in terms of the i2c mux core instead of the child adapter. Acked-by: Rob Herring Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/of/unittest.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index e986e6ee52e0..c1ebbfb79453 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1692,13 +1692,7 @@ static struct i2c_driver unittest_i2c_dev_driver = { #if IS_BUILTIN(CONFIG_I2C_MUX) -struct unittest_i2c_mux_data { - int nchans; - struct i2c_adapter *adap[]; -}; - -static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap, - void *client, u32 chan) +static int unittest_i2c_mux_select_chan(struct i2c_mux_core *muxc, u32 chan) { return 0; } @@ -1706,11 +1700,11 @@ static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap, static int unittest_i2c_mux_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int ret, i, nchans, size; + int ret, i, nchans; struct device *dev = &client->dev; struct i2c_adapter *adap = to_i2c_adapter(dev->parent); struct device_node *np = client->dev.of_node, *child; - struct unittest_i2c_mux_data *stm; + struct i2c_mux_core *muxc; u32 reg, max_reg; dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); @@ -1734,25 +1728,20 @@ static int unittest_i2c_mux_probe(struct i2c_client *client, return -EINVAL; } - size = offsetof(struct unittest_i2c_mux_data, adap[nchans]); - stm = devm_kzalloc(dev, size, GFP_KERNEL); - if (!stm) { - dev_err(dev, "Out of memory\n"); + muxc = i2c_mux_alloc(adap, dev, nchans, 0, 0, + unittest_i2c_mux_select_chan, NULL); + if (!muxc) return -ENOMEM; - } - stm->nchans = nchans; for (i = 0; i < nchans; i++) { - stm->adap[i] = i2c_add_mux_adapter(adap, dev, client, - 0, i, 0, unittest_i2c_mux_select_chan, NULL); - if (!stm->adap[i]) { + ret = i2c_mux_add_adapter(muxc, 0, i, 0); + if (ret) { dev_err(dev, "Failed to register mux #%d\n", i); - for (i--; i >= 0; i--) - i2c_del_mux_adapter(stm->adap[i]); + i2c_mux_del_adapters(muxc); return -ENODEV; } } - i2c_set_clientdata(client, stm); + i2c_set_clientdata(client, muxc); return 0; }; @@ -1761,12 +1750,10 @@ static int unittest_i2c_mux_remove(struct i2c_client *client) { struct device *dev = &client->dev; struct device_node *np = client->dev.of_node; - struct unittest_i2c_mux_data *stm = i2c_get_clientdata(client); - int i; + struct i2c_mux_core *muxc = i2c_get_clientdata(client); dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); - for (i = stm->nchans - 1; i >= 0; i--) - i2c_del_mux_adapter(stm->adap[i]); + i2c_mux_del_adapters(muxc); return 0; } -- cgit From 23fe440c59b9f08afe108e7ec7b6714cb2a3b955 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 2 Mar 2016 15:14:22 +0100 Subject: i2c: mux: drop old unused i2c-mux api All i2c mux users are using an explicit i2c mux core, drop support for implicit i2c mux cores. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-mux.c | 63 ------------------------------------------------- include/linux/i2c-mux.h | 15 ------------ 2 files changed, 78 deletions(-) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 5ce1b0704cb5..25e9336b0e6e 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -28,12 +28,6 @@ #include /* multiplexer per channel data */ -struct i2c_mux_priv_old { - void *mux_priv; - int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id); - int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id); -}; - struct i2c_mux_priv { struct i2c_adapter adap; struct i2c_algorithm algo; @@ -104,53 +98,6 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } -static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->select(muxc->parent, priv->mux_priv, chan); -} - -static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) -{ - struct i2c_mux_priv_old *priv = i2c_mux_priv(muxc); - - return priv->deselect(muxc->parent, priv->mux_priv, chan); -} - -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *, u32), - int (*deselect) (struct i2c_adapter *, - void *, u32)) -{ - struct i2c_mux_core *muxc; - struct i2c_mux_priv_old *priv; - int ret; - - muxc = i2c_mux_alloc(parent, mux_dev, 1, sizeof(*priv), 0, - i2c_mux_select, i2c_mux_deselect); - if (!muxc) - return NULL; - - priv = i2c_mux_priv(muxc); - priv->select = select; - priv->deselect = deselect; - priv->mux_priv = mux_priv; - - ret = i2c_mux_add_adapter(muxc, force_nr, chan_id, class); - if (ret) { - devm_kfree(mux_dev, muxc); - return NULL; - } - - return muxc->adapter[0]; -} -EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); - struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, struct device *dev, int max_adapters, int sizeof_priv, u32 flags, @@ -305,16 +252,6 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc) } EXPORT_SYMBOL_GPL(i2c_mux_del_adapters); -void i2c_del_mux_adapter(struct i2c_adapter *adap) -{ - struct i2c_mux_priv *priv = adap->algo_data; - struct i2c_mux_core *muxc = priv->muxc; - - i2c_mux_del_adapters(muxc); - devm_kfree(muxc->dev, muxc); -} -EXPORT_SYMBOL_GPL(i2c_del_mux_adapter); - MODULE_AUTHOR("Rodolfo Giometti "); MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index 71ac1b3f4f68..2fa93fe1345e 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -52,20 +52,6 @@ static inline void *i2c_mux_priv(struct i2c_mux_core *muxc) return muxc->priv; } -/* - * Called to create a i2c bus on a multiplexed bus segment. - * The mux_dev and chan_id parameters are passed to the select - * and deselect callback functions to perform hardware-specific - * mux control. - */ -struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, - struct device *mux_dev, - void *mux_priv, u32 force_nr, u32 chan_id, - unsigned int class, - int (*select) (struct i2c_adapter *, - void *mux_dev, u32 chan_id), - int (*deselect) (struct i2c_adapter *, - void *mux_dev, u32 chan_id)); /* * Called to create an i2c bus on a multiplexed bus segment. * The chan_id parameter is passed to the select and deselect @@ -75,7 +61,6 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, u32 force_nr, u32 chan_id, unsigned int class); -void i2c_del_mux_adapter(struct i2c_adapter *adap); void i2c_mux_del_adapters(struct i2c_mux_core *muxc); #endif /* __KERNEL__ */ -- cgit From afc34be05331962c2326c24bd0b47baff193b659 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Thu, 24 Mar 2016 20:59:16 +0200 Subject: i2c: dln2: Pass forward ACPI companion Share the ACPI companion for the platform device with the i2c adapter, so that the adapter has access to the properties defined in ACPI tables. Signed-off-by: Irina Tirdea Acked-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-dln2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c index 1600edd57ce9..f2eb4f76591f 100644 --- a/drivers/i2c/busses/i2c-dln2.c +++ b/drivers/i2c/busses/i2c-dln2.c @@ -19,6 +19,7 @@ #include #include #include +#include #define DLN2_I2C_MODULE_ID 0x03 #define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) @@ -210,6 +211,7 @@ static int dln2_i2c_probe(struct platform_device *pdev) dln2->adapter.algo = &dln2_i2c_usb_algorithm; dln2->adapter.quirks = &dln2_i2c_quirks; dln2->adapter.dev.parent = dev; + ACPI_COMPANION_SET(&dln2->adapter.dev, ACPI_COMPANION(&pdev->dev)); dln2->adapter.dev.of_node = dev->of_node; i2c_set_adapdata(&dln2->adapter, dln2); snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", -- cgit From 126a66caec4a00b8d66dbc3174b0efa905cf68c3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 4 Apr 2016 16:55:23 +0300 Subject: i2c: omap: drop the lock hard irq context The lock is taken while reading two registers. On RT the first lock is taken in hard irq where it might sleep and in the threaded irq. The threaded irq runs in oneshot mode so the hard irq does not run until the thread the completes so there is no reason to grab the lock. Signed-off-by: Sebastian Andrzej Siewior [grygorii.strashko@ti.com: drop locking from isr completely and remove lock field from struct omap_i2c_dev] Signed-off-by: Grygorii Strashko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-omap.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 13c45296ce5b..ab1279b8e240 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -185,7 +185,6 @@ enum { #define OMAP_I2C_IP_V2_INTERRUPTS_MASK 0x6FFF struct omap_i2c_dev { - spinlock_t lock; /* IRQ synchronization */ struct device *dev; void __iomem *base; /* virtual */ int irq; @@ -995,15 +994,12 @@ omap_i2c_isr(int irq, void *dev_id) u16 mask; u16 stat; - spin_lock(&omap->lock); - mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG); stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG); + mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG); if (stat & mask) ret = IRQ_WAKE_THREAD; - spin_unlock(&omap->lock); - return ret; } @@ -1011,12 +1007,10 @@ static irqreturn_t omap_i2c_isr_thread(int this_irq, void *dev_id) { struct omap_i2c_dev *omap = dev_id; - unsigned long flags; u16 bits; u16 stat; int err = 0, count = 0; - spin_lock_irqsave(&omap->lock, flags); do { bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG); stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG); @@ -1142,8 +1136,6 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) omap_i2c_complete_cmd(omap, err); out: - spin_unlock_irqrestore(&omap->lock, flags); - return IRQ_HANDLED; } @@ -1330,8 +1322,6 @@ omap_i2c_probe(struct platform_device *pdev) omap->dev = &pdev->dev; omap->irq = irq; - spin_lock_init(&omap->lock); - platform_set_drvdata(pdev, omap); init_completion(&omap->cmd_complete); -- cgit From 2c89e5d88473429eec62de12f5ae6642cf6a02a5 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 22 Apr 2016 15:44:07 +0200 Subject: i2c: mux: pinctrl: fix indentation for better readability smatch rightfully says: drivers/i2c/muxes/i2c-mux-pinctrl.c:175 i2c_mux_pinctrl_probe() warn: inconsistent indenting Signed-off-by: Wolfram Sang Acked-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-pinctrl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 1b8dc711815e..f4e62f4a50cc 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -172,13 +172,13 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) for (i = 0; i < mux->pdata->bus_count; i++) { mux->states[i] = pinctrl_lookup_state(mux->pinctrl, mux->pdata->pinctrl_states[i]); - if (IS_ERR(mux->states[i])) { - ret = PTR_ERR(mux->states[i]); - dev_err(&pdev->dev, - "Cannot look up pinctrl state %s: %d\n", - mux->pdata->pinctrl_states[i], ret); - goto err; - } + if (IS_ERR(mux->states[i])) { + ret = PTR_ERR(mux->states[i]); + dev_err(&pdev->dev, + "Cannot look up pinctrl state %s: %d\n", + mux->pdata->pinctrl_states[i], ret); + goto err; + } } if (mux->pdata->pinctrl_state_idle) { mux->state_idle = pinctrl_lookup_state(mux->pinctrl, -- cgit From d4644becf86c710298a4af86f463c79375a21f21 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 14 Apr 2016 09:08:04 +0800 Subject: i2c: exynos5: Use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS instead of open-coded Use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS to simplify the code. Signed-off-by: Axel Lin Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-exynos5.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index b29c7500461a..8710052eeb6b 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -847,14 +847,8 @@ static int exynos5_i2c_resume_noirq(struct device *dev) #endif static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend_noirq = exynos5_i2c_suspend_noirq, - .resume_noirq = exynos5_i2c_resume_noirq, - .freeze_noirq = exynos5_i2c_suspend_noirq, - .thaw_noirq = exynos5_i2c_resume_noirq, - .poweroff_noirq = exynos5_i2c_suspend_noirq, - .restore_noirq = exynos5_i2c_resume_noirq, -#endif + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq, + exynos5_i2c_resume_noirq) }; static struct platform_driver exynos5_i2c_driver = { -- cgit From f6903783eba49a2cbb6798b35fcf9d2ce61768b9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 14 Apr 2016 09:08:55 +0800 Subject: i2c: s3c2410: Use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS instead of open-coded Use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS to simplify the code. Signed-off-by: Axel Lin Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 362a6de54833..1bc756313cb8 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -1316,14 +1316,8 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev) #ifdef CONFIG_PM static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { -#ifdef CONFIG_PM_SLEEP - .suspend_noirq = s3c24xx_i2c_suspend_noirq, - .resume_noirq = s3c24xx_i2c_resume_noirq, - .freeze_noirq = s3c24xx_i2c_suspend_noirq, - .thaw_noirq = s3c24xx_i2c_resume_noirq, - .poweroff_noirq = s3c24xx_i2c_suspend_noirq, - .restore_noirq = s3c24xx_i2c_resume_noirq, -#endif + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c24xx_i2c_suspend_noirq, + s3c24xx_i2c_resume_noirq) }; #define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops) -- cgit From b4c715d04006ccc17d9ae0cf673b2f65267c042b Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 25 Apr 2016 16:33:30 +0200 Subject: i2c: octeon: Improve error status checking Introduce a function that checks for valid status codes depending on the phase of a transmit or receive. Also add all existing status codes and improve error handling for various states. The Octeon TWSI has an "assert acknowledge" bit (TWSI_CTL_AAK) that is required to be set in master receive mode until the last byte is requested. The state check needs to consider if this bit was set. Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 129 +++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 23 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 4275007c2907..471d06eb8434 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -55,13 +55,35 @@ #define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */ #define TWSI_CTL_AAK 0x04 /* Assert ACK */ -/* Some status values */ +/* Status values */ +#define STAT_ERROR 0x00 #define STAT_START 0x08 -#define STAT_RSTART 0x10 +#define STAT_REP_START 0x10 #define STAT_TXADDR_ACK 0x18 +#define STAT_TXADDR_NAK 0x20 #define STAT_TXDATA_ACK 0x28 +#define STAT_TXDATA_NAK 0x30 +#define STAT_LOST_ARB_38 0x38 #define STAT_RXADDR_ACK 0x40 +#define STAT_RXADDR_NAK 0x48 #define STAT_RXDATA_ACK 0x50 +#define STAT_RXDATA_NAK 0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST 0xB8 +#define STAT_SLAVE_NAK 0xC0 +#define STAT_SLAVE_ACK 0xC8 +#define STAT_AD2W_ACK 0xD0 +#define STAT_AD2W_NAK 0xD8 #define STAT_IDLE 0xF8 /* TWSI_INT values */ @@ -225,6 +247,67 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } +static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read) +{ + u8 stat = octeon_i2c_stat_read(i2c); + + switch (stat) { + /* Everything is fine */ + case STAT_IDLE: + case STAT_AD2W_ACK: + case STAT_RXADDR_ACK: + case STAT_TXADDR_ACK: + case STAT_TXDATA_ACK: + return 0; + + /* ACK allowed on pre-terminal bytes only */ + case STAT_RXDATA_ACK: + if (!final_read) + return 0; + return -EIO; + + /* NAK allowed on terminal byte only */ + case STAT_RXDATA_NAK: + if (final_read) + return 0; + return -EIO; + + /* Arbitration lost */ + case STAT_LOST_ARB_38: + case STAT_LOST_ARB_68: + case STAT_LOST_ARB_78: + case STAT_LOST_ARB_B0: + return -EAGAIN; + + /* Being addressed as slave, should back off & listen */ + case STAT_SLAVE_60: + case STAT_SLAVE_70: + case STAT_GENDATA_ACK: + case STAT_GENDATA_NAK: + return -EOPNOTSUPP; + + /* Core busy as slave */ + case STAT_SLAVE_80: + case STAT_SLAVE_88: + case STAT_SLAVE_A0: + case STAT_SLAVE_A8: + case STAT_SLAVE_LOST: + case STAT_SLAVE_NAK: + case STAT_SLAVE_ACK: + return -EOPNOTSUPP; + + case STAT_TXDATA_NAK: + return -EIO; + case STAT_TXADDR_NAK: + case STAT_RXADDR_NAK: + case STAT_AD2W_NAK: + return -ENXIO; + default: + dev_err(i2c->dev, "unhandled state: %d\n", stat); + return -EIO; + } +} + /* calculate and set clock divisors */ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) { @@ -318,7 +401,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) } data = octeon_i2c_stat_read(i2c); - if ((data != STAT_START) && (data != STAT_RSTART)) { + if ((data != STAT_START) && (data != STAT_REP_START)) { dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); return -EIO; } @@ -347,7 +430,6 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, const u8 *data, int length) { int i, result; - u8 tmp; result = octeon_i2c_start(i2c); if (result) @@ -361,14 +443,9 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, return result; for (i = 0; i < length; i++) { - tmp = octeon_i2c_stat_read(i2c); - - if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) { - dev_err(i2c->dev, - "%s: bad status before write (0x%x)\n", - __func__, tmp); - return -EIO; - } + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; octeon_i2c_data_write(i2c, data[i]); octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); @@ -397,7 +474,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, u16 *rlength, bool recv_len) { int i, result, length = *rlength; - u8 tmp; + bool final_read = false; if (length < 1) return -EINVAL; @@ -413,19 +490,21 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, if (result) return result; + /* address OK ? */ + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; + for (i = 0; i < length; i++) { - tmp = octeon_i2c_stat_read(i2c); - if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) { - dev_err(i2c->dev, - "%s: bad status before read (0x%x)\n", - __func__, tmp); - return -EIO; - } + /* for the last byte TWSI_CTL_AAK must not be set */ + if (i + 1 == length) + final_read = true; - if (i + 1 < length) - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); - else + /* clear iflg to allow next event */ + if (final_read) octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + else + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); result = octeon_i2c_wait(i2c); if (result) @@ -441,6 +520,10 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, } length += data[i]; } + + result = octeon_i2c_check_status(i2c, final_read); + if (result) + return result; } *rlength = length; return 0; -- cgit From c981e34eee146fe560c46704396342ff979346d7 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 25 Apr 2016 16:33:31 +0200 Subject: i2c: octeon: Use i2c recovery framework Switch to the i2c bus recovery framework using generic SCL recovery. If this fails try to reset the hardware. The recovery is triggered during START on timeout of the interrupt or failure to reach the START / repeated-START condition. The START function is moved to xfer and while at it remove the xfer debug message (i2c core already provides a debug message for this). Signed-off-by: Jan Glauber [wsa: removed one empty line] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 159 ++++++++++++++++++++++++---------------- 1 file changed, 96 insertions(+), 63 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 471d06eb8434..f83f6b8892b5 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -90,6 +90,8 @@ #define TWSI_INT_CORE_EN BIT_ULL(6) #define TWSI_INT_SDA_OVR BIT_ULL(8) #define TWSI_INT_SCL_OVR BIT_ULL(9) +#define TWSI_INT_SDA BIT_ULL(10) +#define TWSI_INT_SCL BIT_ULL(11) struct octeon_i2c { wait_queue_head_t queue; @@ -152,6 +154,17 @@ static u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) #define octeon_i2c_stat_read(i2c) \ octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) +/** + * octeon_i2c_read_int - read the TWSI_INT register + * @i2c: The struct octeon_i2c + * + * Returns the value of the register. + */ +static u64 octeon_i2c_read_int(struct octeon_i2c *i2c) +{ + return __raw_readq(i2c->twsi_base + TWSI_INT); +} + /** * octeon_i2c_write_int - write the TWSI_INT register * @i2c: The struct octeon_i2c @@ -182,33 +195,6 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, 0); } -/** - * octeon_i2c_unblock - unblock the bus - * @i2c: The struct octeon_i2c - * - * If there was a reset while a device was driving 0 to bus, bus is blocked. - * We toggle it free manually by some clock cycles and send a stop. - */ -static void octeon_i2c_unblock(struct octeon_i2c *i2c) -{ - int i; - - dev_dbg(i2c->dev, "%s\n", __func__); - - for (i = 0; i < 9; i++) { - octeon_i2c_write_int(i2c, 0); - udelay(5); - octeon_i2c_write_int(i2c, TWSI_INT_SCL_OVR); - udelay(5); - } - /* hand-crank a STOP */ - octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR | TWSI_INT_SCL_OVR); - udelay(5); - octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR); - udelay(5); - octeon_i2c_write_int(i2c, 0); -} - /* interrupt service routine */ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { @@ -371,6 +357,17 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) return -EIO; } +static int octeon_i2c_recovery(struct octeon_i2c *i2c) +{ + int ret; + + ret = i2c_recover_bus(&i2c->adap); + if (ret) + /* recover failed, try hardware re-init */ + ret = octeon_i2c_init_lowlevel(i2c); + return ret; +} + /** * octeon_i2c_start - send START to the bus * @i2c: The struct octeon_i2c @@ -379,34 +376,23 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) */ static int octeon_i2c_start(struct octeon_i2c *i2c) { - int result; - u8 data; + int ret; + u8 stat; octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); + ret = octeon_i2c_wait(i2c); + if (ret) + goto error; - result = octeon_i2c_wait(i2c); - if (result) { - if (octeon_i2c_stat_read(i2c) == STAT_IDLE) { - /* - * Controller refused to send start flag May - * be a client is holding SDA low - let's try - * to free it. - */ - octeon_i2c_unblock(i2c); - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); - result = octeon_i2c_wait(i2c); - } - if (result) - return result; - } - - data = octeon_i2c_stat_read(i2c); - if ((data != STAT_START) && (data != STAT_REP_START)) { - dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); - return -EIO; - } + stat = octeon_i2c_stat_read(i2c); + if (stat == STAT_START || stat == STAT_REP_START) + /* START successful, bail out */ + return 0; - return 0; +error: + /* START failed, try to recover */ + ret = octeon_i2c_recovery(i2c); + return (ret) ? ret : -EAGAIN; } /* send STOP to the bus */ @@ -431,10 +417,6 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, { int i, result; - result = octeon_i2c_start(i2c); - if (result) - return result; - octeon_i2c_data_write(i2c, target << 1); octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); @@ -479,10 +461,6 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, if (length < 1) return -EINVAL; - result = octeon_i2c_start(i2c); - if (result) - return result; - octeon_i2c_data_write(i2c, (target << 1) | 1); octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); @@ -546,10 +524,10 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, for (i = 0; ret == 0 && i < num; i++) { struct i2c_msg *pmsg = &msgs[i]; - dev_dbg(i2c->dev, - "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n", - pmsg->flags & I2C_M_RD ? "read" : "write", - pmsg->len, pmsg->addr, i + 1, num); + ret = octeon_i2c_start(i2c); + if (ret) + return ret; + if (pmsg->flags & I2C_M_RD) ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, &pmsg->len, pmsg->flags & I2C_M_RECV_LEN); @@ -562,6 +540,60 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, return (ret != 0) ? ret : num; } +static int octeon_i2c_get_scl(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + u64 state; + + state = octeon_i2c_read_int(i2c); + return state & TWSI_INT_SCL; +} + +static void octeon_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + octeon_i2c_write_int(i2c, TWSI_INT_SCL_OVR); +} + +static int octeon_i2c_get_sda(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + u64 state; + + state = octeon_i2c_read_int(i2c); + return state & TWSI_INT_SDA; +} + +static void octeon_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + /* + * The stop resets the state machine, does not _transmit_ STOP unless + * engine was active. + */ + octeon_i2c_stop(i2c); + + octeon_i2c_write_int(i2c, 0); +} + +static void octeon_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + + octeon_i2c_write_int(i2c, 0); +} + +static struct i2c_bus_recovery_info octeon_i2c_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .get_scl = octeon_i2c_get_scl, + .set_scl = octeon_i2c_set_scl, + .get_sda = octeon_i2c_get_sda, + .prepare_recovery = octeon_i2c_prepare_recovery, + .unprepare_recovery = octeon_i2c_unprepare_recovery, +}; + static u32 octeon_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | @@ -642,6 +674,7 @@ static int octeon_i2c_probe(struct platform_device *pdev) i2c->adap = octeon_i2c_ops; i2c->adap.timeout = msecs_to_jiffies(2); i2c->adap.retries = 5; + i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info; i2c->adap.dev.parent = &pdev->dev; i2c->adap.dev.of_node = node; i2c_set_adapdata(&i2c->adap, i2c); -- cgit From 30c24b25142a9171d38e8074758b3a370906fc7d Mon Sep 17 00:00:00 2001 From: Peter Swain Date: Mon, 25 Apr 2016 16:33:33 +0200 Subject: i2c: octeon: Add flush writeq helper function Add helper function that reads back a value after writing to make sure the write is finished and use it in octeon_i2c_write_int(). Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index f83f6b8892b5..08ca66505ce2 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -103,6 +103,12 @@ struct octeon_i2c { struct device *dev; }; +static void octeon_i2c_writeq_flush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + /** * octeon_i2c_reg_write - write an I2C core register * @i2c: The struct octeon_i2c @@ -172,8 +178,7 @@ static u64 octeon_i2c_read_int(struct octeon_i2c *i2c) */ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) { - __raw_writeq(data, i2c->twsi_base + TWSI_INT); - __raw_readq(i2c->twsi_base + TWSI_INT); + octeon_i2c_writeq_flush(data, i2c->twsi_base + TWSI_INT); } /** -- cgit From d1fbff8944bf88e449ae387abd498e5220a2ee10 Mon Sep 17 00:00:00 2001 From: David Daney Date: Mon, 25 Apr 2016 16:33:34 +0200 Subject: i2c: octeon: Enable High-Level Controller Use High-Level Controller (HLC) when possible. The HLC can read/write up to 8 bytes and is completely optional. The most important difference of the HLC is that it only requires one interrupt for a transfer (up to 8 bytes) where the low-level read/write requires 2 interrupts plus one interrupt per transferred byte. Since the interrupts are costly using the HLC improves the performance. Also, the HLC provides improved error handling. Signed-off-by: David Daney Signed-off-by: Jan Glauber [wsa: fixed trivial checkpatch warnings] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 347 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 337 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 08ca66505ce2..3273db714310 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -29,13 +29,23 @@ /* Register offsets */ #define SW_TWSI 0x00 #define TWSI_INT 0x10 +#define SW_TWSI_EXT 0x18 /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIA BIT_ULL(61) /* Extended internal address */ #define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ /* Controller opcode word (bits 60:57) */ #define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA (1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_TWSI_CLK (4ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ @@ -48,7 +58,7 @@ #define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) /* Controller command and status bits */ -#define TWSI_CTL_CE 0x80 +#define TWSI_CTL_CE 0x80 /* High level controller enable */ #define TWSI_CTL_ENAB 0x40 /* Bus enable */ #define TWSI_CTL_STA 0x20 /* Master-mode start, HW clears when done */ #define TWSI_CTL_STP 0x10 /* Master-mode stop, HW clears when done */ @@ -87,6 +97,11 @@ #define STAT_IDLE 0xF8 /* TWSI_INT values */ +#define TWSI_INT_ST_INT BIT_ULL(0) +#define TWSI_INT_TS_INT BIT_ULL(1) +#define TWSI_INT_CORE_INT BIT_ULL(2) +#define TWSI_INT_ST_EN BIT_ULL(4) +#define TWSI_INT_TS_EN BIT_ULL(5) #define TWSI_INT_CORE_EN BIT_ULL(6) #define TWSI_INT_SDA_OVR BIT_ULL(8) #define TWSI_INT_SCL_OVR BIT_ULL(9) @@ -101,6 +116,7 @@ struct octeon_i2c { int sys_freq; void __iomem *twsi_base; struct device *dev; + bool hlc_enabled; }; static void octeon_i2c_writeq_flush(u64 val, void __iomem *addr) @@ -200,6 +216,47 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, 0); } +/* + * Cleanup low-level state & enable high-level controller. + */ +static void octeon_i2c_hlc_enable(struct octeon_i2c *i2c) +{ + int try = 0; + u64 val; + + if (i2c->hlc_enabled) + return; + i2c->hlc_enabled = true; + + while (1) { + val = octeon_i2c_ctl_read(i2c); + if (!(val & (TWSI_CTL_STA | TWSI_CTL_STP))) + break; + + /* clear IFLG event */ + if (val & TWSI_CTL_IFLG) + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + if (try++ > 100) { + pr_err("%s: giving up\n", __func__); + break; + } + + /* spin until any start/stop has finished */ + udelay(10); + } + octeon_i2c_ctl_write(i2c, TWSI_CTL_CE | TWSI_CTL_AAK | TWSI_CTL_ENAB); +} + +static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); +} + /* interrupt service routine */ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { @@ -299,6 +356,245 @@ static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read) } } +static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c) +{ + u64 val = __raw_readq(i2c->twsi_base + SW_TWSI); + + return (val & SW_TWSI_V) == 0; +} + +static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, TWSI_INT_ST_EN); +} + +static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) +{ + /* clear ST/TS events, listen for neither */ + octeon_i2c_write_int(i2c, TWSI_INT_ST_INT | TWSI_INT_TS_INT); +} + +/** + * octeon_i2c_hlc_wait - wait for an HLC operation to complete + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise -ETIMEDOUT. + */ +static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) +{ + int time_left; + + octeon_i2c_hlc_int_enable(i2c); + time_left = wait_event_timeout(i2c->queue, + octeon_i2c_hlc_test_ready(i2c), + i2c->adap.timeout); + octeon_i2c_int_disable(i2c); + if (!time_left) { + octeon_i2c_hlc_int_clear(i2c); + return -ETIMEDOUT; + } + return 0; +} + +/* high-level-controller pure read of up to 8 bytes */ +static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_hlc_enable(i2c); + octeon_i2c_hlc_int_clear(i2c); + + cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10; + else + cmd |= SW_TWSI_OP_7; + + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + + for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) + msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; + + if (msgs[0].len > 4) { + cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); + for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) + msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; + } + +err: + return ret; +} + +/* high-level-controller pure write of up to 8 bytes */ +static int octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_hlc_enable(i2c); + octeon_i2c_hlc_int_clear(i2c); + + cmd = SW_TWSI_V | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10; + else + cmd |= SW_TWSI_OP_7; + + for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) + cmd |= (u64)msgs[0].buf[j] << (8 * i); + + if (msgs[0].len > 4) { + u64 ext = 0; + + for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) + ext |= (u64)msgs[0].buf[j] << (8 * i); + octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT); + } + + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + + ret = octeon_i2c_check_status(i2c, false); + +err: + return ret; +} + +/* high-level-controller composite write+read, msg0=addr, msg1=data */ +static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + int i, j, ret = 0; + u64 cmd; + + octeon_i2c_hlc_enable(i2c); + + cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10_IA; + else + cmd |= SW_TWSI_OP_7_IA; + + if (msgs[0].len == 2) { + u64 ext = 0; + + cmd |= SW_TWSI_EIA; + ext = (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + cmd |= (u64)msgs[0].buf[1] << SW_TWSI_IA_SHIFT; + octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT); + } else { + cmd |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + } + + octeon_i2c_hlc_int_clear(i2c); + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + + for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) + msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; + + if (msgs[1].len > 4) { + cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); + for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) + msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; + } + +err: + return ret; +} + +/* high-level-controller composite write+write, m[0]len<=2, m[1]len<=8 */ +static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) +{ + bool set_ext = false; + int i, j, ret = 0; + u64 cmd, ext = 0; + + octeon_i2c_hlc_enable(i2c); + + cmd = SW_TWSI_V | SW_TWSI_SOVR; + /* SIZE */ + cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; + /* A */ + cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; + + if (msgs[0].flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10_IA; + else + cmd |= SW_TWSI_OP_7_IA; + + if (msgs[0].len == 2) { + cmd |= SW_TWSI_EIA; + ext |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + set_ext = true; + cmd |= (u64)msgs[0].buf[1] << SW_TWSI_IA_SHIFT; + } else { + cmd |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; + } + + for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) + cmd |= (u64)msgs[1].buf[j] << (8 * i); + + if (msgs[1].len > 4) { + for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) + ext |= (u64)msgs[1].buf[j] << (8 * i); + set_ext = true; + } + if (set_ext) + octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT); + + octeon_i2c_hlc_int_clear(i2c); + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + + ret = octeon_i2c_hlc_wait(i2c); + if (ret) + goto err; + + cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + if ((cmd & SW_TWSI_R) == 0) + return -EAGAIN; + + ret = octeon_i2c_check_status(i2c, false); + +err: + return ret; +} + /* calculate and set clock divisors */ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) { @@ -343,23 +639,29 @@ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) { - u8 status; + u8 status = 0; int tries; - /* disable high level controller, enable bus access */ - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); - /* reset controller */ octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0); - for (tries = 10; tries; tries--) { + for (tries = 10; tries && status != STAT_IDLE; tries--) { udelay(1); status = octeon_i2c_stat_read(i2c); if (status == STAT_IDLE) - return 0; + break; } - dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); - return -EIO; + + if (status != STAT_IDLE) { + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", + __func__, status); + return -EIO; + } + + /* toggle twice to force both teardowns */ + octeon_i2c_hlc_enable(i2c); + octeon_i2c_hlc_disable(i2c); + return 0; } static int octeon_i2c_recovery(struct octeon_i2c *i2c) @@ -384,6 +686,8 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) int ret; u8 stat; + octeon_i2c_hlc_disable(i2c); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); ret = octeon_i2c_wait(i2c); if (ret) @@ -526,6 +830,28 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, struct octeon_i2c *i2c = i2c_get_adapdata(adap); int i, ret = 0; + if (num == 1) { + if (msgs[0].len > 0 && msgs[0].len <= 8) { + if (msgs[0].flags & I2C_M_RD) + ret = octeon_i2c_hlc_read(i2c, msgs); + else + ret = octeon_i2c_hlc_write(i2c, msgs); + goto out; + } + } else if (num == 2) { + if ((msgs[0].flags & I2C_M_RD) == 0 && + (msgs[1].flags & I2C_M_RECV_LEN) == 0 && + msgs[0].len > 0 && msgs[0].len <= 2 && + msgs[1].len > 0 && msgs[1].len <= 8 && + msgs[0].addr == msgs[1].addr) { + if (msgs[1].flags & I2C_M_RD) + ret = octeon_i2c_hlc_comp_read(i2c, msgs); + else + ret = octeon_i2c_hlc_comp_write(i2c, msgs); + goto out; + } + } + for (i = 0; ret == 0 && i < num; i++) { struct i2c_msg *pmsg = &msgs[i]; @@ -541,7 +867,7 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, pmsg->len); } octeon_i2c_stop(i2c); - +out: return (ret != 0) ? ret : num; } @@ -580,6 +906,7 @@ static void octeon_i2c_prepare_recovery(struct i2c_adapter *adap) */ octeon_i2c_stop(i2c); + octeon_i2c_hlc_disable(i2c); octeon_i2c_write_int(i2c, 0); } -- cgit From 4729cbe038fdd321839d7780ee93ce1ccf1e7b29 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Mon, 25 Apr 2016 16:33:35 +0200 Subject: i2c: octeon: Add support for cn78xx chips cn78xx has a different interrupt architecture, so we have to manage the interrupts differently. Signed-off-by: David Daney Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-octeon.txt | 6 + drivers/i2c/busses/i2c-octeon.c | 129 +++++++++++++++++++-- 2 files changed, 125 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt index dced82ebe31d..872d485dffab 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt @@ -4,6 +4,12 @@ Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. + or + + compatible: "cavium,octeon-7890-twsi" + + Compatibility with cn78XX SOCs. + - reg: The base address of the TWSI/I2C bus controller register bank. - #address-cells: Must be <1>. diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 3273db714310..6a4f0581a7e0 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -11,6 +11,7 @@ * warranty of any kind, whether express or implied. */ +#include #include #include #include @@ -112,11 +113,18 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; int irq; + int hlc_irq; /* For cn7890 only */ u32 twsi_freq; int sys_freq; void __iomem *twsi_base; struct device *dev; bool hlc_enabled; + void (*int_enable)(struct octeon_i2c *); + void (*int_disable)(struct octeon_i2c *); + void (*hlc_int_enable)(struct octeon_i2c *); + void (*hlc_int_disable)(struct octeon_i2c *); + atomic_t int_enable_cnt; + atomic_t hlc_int_enable_cnt; }; static void octeon_i2c_writeq_flush(u64 val, void __iomem *addr) @@ -216,6 +224,58 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, 0); } +/** + * octeon_i2c_int_enable78 - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(&i2c->int_enable_cnt); + enable_irq(i2c->irq); +} + +static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) +{ + int count; + + /* + * The interrupt can be disabled in two places, but we only + * want to make the disable_irq_nosync() call once, so keep + * track with the atomic variable. + */ + count = atomic_dec_if_positive(cnt); + if (count >= 0) + disable_irq_nosync(irq); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(&i2c->int_enable_cnt, i2c->irq); +} + +/** + * octeon_i2c_hlc_int_enable78 - enable the ST interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(&i2c->hlc_int_enable_cnt); + enable_irq(i2c->hlc_irq); +} + +/* disable the ST interrupt */ +static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(&i2c->hlc_int_enable_cnt, i2c->hlc_irq); +} + /* * Cleanup low-level state & enable high-level controller. */ @@ -262,7 +322,18 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { struct octeon_i2c *i2c = dev_id; - octeon_i2c_int_disable(i2c); + i2c->int_disable(i2c); + wake_up(&i2c->queue); + + return IRQ_HANDLED; +} + +/* HLC interrupt service routine */ +static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->hlc_int_disable(i2c); wake_up(&i2c->queue); return IRQ_HANDLED; @@ -283,10 +354,10 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; - octeon_i2c_int_enable(i2c); + i2c->int_enable(i2c); time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + i2c->int_disable(i2c); if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -384,11 +455,11 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int time_left; - octeon_i2c_hlc_int_enable(i2c); + i2c->hlc_int_enable(i2c); time_left = wait_event_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + i2c->hlc_int_disable(i2c); if (!time_left) { octeon_i2c_hlc_int_clear(i2c); return -ETIMEDOUT; @@ -946,14 +1017,26 @@ static struct i2c_adapter octeon_i2c_ops = { static int octeon_i2c_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + int irq, result = 0, hlc_irq = 0; struct resource *res_mem; struct octeon_i2c *i2c; - int irq, result = 0; + bool cn78xx_style; + + cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi"); + if (cn78xx_style) { + hlc_irq = platform_get_irq(pdev, 0); + if (hlc_irq < 0) + return hlc_irq; - /* All adaptors have an irq. */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + irq = platform_get_irq(pdev, 2); + if (irq < 0) + return irq; + } else { + /* All adaptors have an irq. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + } i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) { @@ -988,6 +1071,31 @@ static int octeon_i2c_probe(struct platform_device *pdev) i2c->irq = irq; + if (cn78xx_style) { + i2c->hlc_irq = hlc_irq; + + i2c->int_enable = octeon_i2c_int_enable78; + i2c->int_disable = octeon_i2c_int_disable78; + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78; + i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78; + + irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN); + irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN); + + result = devm_request_irq(&pdev->dev, i2c->hlc_irq, + octeon_i2c_hlc_isr78, 0, + DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto out; + } + } else { + i2c->int_enable = octeon_i2c_int_enable; + i2c->int_disable = octeon_i2c_int_disable; + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable; + i2c->hlc_int_disable = octeon_i2c_int_disable; + } + result = devm_request_irq(&pdev->dev, i2c->irq, octeon_i2c_isr, 0, DRV_NAME, i2c); if (result < 0) { @@ -1034,6 +1142,7 @@ static int octeon_i2c_remove(struct platform_device *pdev) static const struct of_device_id octeon_i2c_match[] = { { .compatible = "cavium,octeon-3860-twsi", }, + { .compatible = "cavium,octeon-7890-twsi", }, {}, }; MODULE_DEVICE_TABLE(of, octeon_i2c_match); -- cgit From b1b3df2fc808187e3c9e50162b55fb458035b0ab Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Apr 2016 09:04:49 +0200 Subject: i2c: s3c2410: Add missing clock unprepare on probe() error path If during probe() the s3c24xx_i2c_init() failed, the clock was left in disabled but prepared state. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 1bc756313cb8..3f16f30edbd2 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -1200,6 +1200,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) clk_disable(i2c->clk); if (ret != 0) { dev_err(&pdev->dev, "I2C controller init failed\n"); + clk_unprepare(i2c->clk); return ret; } /* find the IRQ for this unit (note, this relies on the init call to -- cgit From ec7c34a4c6c71487a5de4c1b2d47beb2c334aa0d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Apr 2016 09:04:50 +0200 Subject: i2c: s3c2410: Minor function-level comment cleanup Cleanup the weird function-level comments and remove obvious documentation for probe/remove. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 110 ++++++++++++--------------------------- 1 file changed, 32 insertions(+), 78 deletions(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 3f16f30edbd2..7245c81366be 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -163,11 +163,9 @@ static const struct of_device_id s3c24xx_i2c_match[] = { MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); #endif -/* s3c24xx_get_device_quirks - * +/* * Get controller type either from device tree or platform device variant. -*/ - + */ static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev) { if (pdev->dev.of_node) { @@ -179,12 +177,10 @@ static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *p return platform_get_device_id(pdev)->driver_data; } -/* s3c24xx_i2c_master_complete - * - * complete the message and wake up the caller, using the given return code, +/* + * Complete the message and wake up the caller, using the given return code, * or zero to mean ok. -*/ - + */ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) { dev_dbg(i2c->dev, "master_complete %d\n", ret); @@ -217,7 +213,6 @@ static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c) } /* irq enable/disable functions */ - static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c) { unsigned long tmp; @@ -251,11 +246,9 @@ static bool is_ack(struct s3c24xx_i2c *i2c) return false; } -/* s3c24xx_i2c_message_start - * +/* * put the start of a message onto the bus -*/ - + */ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { @@ -364,21 +357,17 @@ static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) /* helper functions to determine the current state in the set of * messages we are sending */ -/* is_lastmsg() - * +/* * returns TRUE if the current message is the last in the set -*/ - + */ static inline int is_lastmsg(struct s3c24xx_i2c *i2c) { return i2c->msg_idx >= (i2c->msg_num - 1); } -/* is_msglast - * +/* * returns TRUE if we this is the last byte in the current message -*/ - + */ static inline int is_msglast(struct s3c24xx_i2c *i2c) { /* msg->len is always 1 for the first byte of smbus block read. @@ -390,21 +379,17 @@ static inline int is_msglast(struct s3c24xx_i2c *i2c) return i2c->msg_ptr == i2c->msg->len-1; } -/* is_msgend - * +/* * returns TRUE if we reached the end of the current message -*/ - + */ static inline int is_msgend(struct s3c24xx_i2c *i2c) { return i2c->msg_ptr >= i2c->msg->len; } -/* i2c_s3c_irq_nextbyte - * +/* * process an interrupt and work out what to do */ - static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) { unsigned long tmp; @@ -568,11 +553,9 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) return ret; } -/* s3c24xx_i2c_irq - * +/* * top level IRQ servicing routine -*/ - + */ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) { struct s3c24xx_i2c *i2c = dev_id; @@ -630,11 +613,9 @@ static inline void s3c24xx_i2c_disable_bus(struct s3c24xx_i2c *i2c) } -/* s3c24xx_i2c_set_master - * +/* * get the i2c bus for a master transaction -*/ - + */ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) { unsigned long iicstat; @@ -652,11 +633,9 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) return -ETIMEDOUT; } -/* s3c24xx_i2c_wait_idle - * +/* * wait for the i2c bus to become idle. -*/ - + */ static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c) { unsigned long iicstat; @@ -706,11 +685,9 @@ static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c) dev_warn(i2c->dev, "timeout waiting for bus idle\n"); } -/* s3c24xx_i2c_doxfer - * +/* * this starts an i2c transfer -*/ - + */ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { @@ -771,12 +748,10 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, return ret; } -/* s3c24xx_i2c_xfer - * +/* * first port of call from the i2c bus code when an message needs * transferring across the i2c bus. -*/ - + */ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { @@ -814,17 +789,14 @@ static u32 s3c24xx_i2c_func(struct i2c_adapter *adap) } /* i2c bus registration info */ - static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, }; -/* s3c24xx_i2c_calcdivisor - * +/* * return the divisor settings for a given frequency -*/ - + */ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, unsigned int *div1, unsigned int *divs) { @@ -850,13 +822,11 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, return clkin / (calc_divs * calc_div1); } -/* s3c24xx_i2c_clockrate - * +/* * work out a divisor for the user requested frequency setting, * either by the requested frequency, or scanning the acceptable * range of frequencies until something is found -*/ - + */ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) { struct s3c2410_platform_i2c *pdata = i2c->pdata; @@ -1028,11 +998,9 @@ static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c) } #endif -/* s3c24xx_i2c_init - * +/* * initialise the controller, set the IO lines and frequency -*/ - + */ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) { struct s3c2410_platform_i2c *pdata; @@ -1068,11 +1036,9 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) } #ifdef CONFIG_OF -/* s3c24xx_i2c_parse_dt - * +/* * Parse the device tree node and retreive the platform data. -*/ - + */ static void s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) { @@ -1111,11 +1077,6 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) } #endif -/* s3c24xx_i2c_probe - * - * called by the bus driver when a suitable device is found -*/ - static int s3c24xx_i2c_probe(struct platform_device *pdev) { struct s3c24xx_i2c *i2c; @@ -1258,11 +1219,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) return 0; } -/* s3c24xx_i2c_remove - * - * called when device is removed from the bus -*/ - static int s3c24xx_i2c_remove(struct platform_device *pdev) { struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); @@ -1326,8 +1282,6 @@ static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { #define S3C24XX_DEV_PM_OPS NULL #endif -/* device driver for platform bus bits */ - static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, -- cgit From 0915833bd5f9987fdcdb491da1e75026bae05683 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Apr 2016 09:04:51 +0200 Subject: i2c: s3c2410: Cleanup indentation and comment style Improve the readability by: - fixing indentation, - switching to proper block comments, - removing spurious blank lines, - checkpatch: void function return statements are not generally useful, - checkpatch: braces {} are not necessary for any arm of this statement, - checkpatch: missing a blank line after declarations. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 113 ++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 7245c81366be..dde1abce07ee 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -170,6 +170,7 @@ static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *p { if (pdev->dev.of_node) { const struct of_device_id *match; + match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node); return (kernel_ulong_t)match->data; } @@ -277,9 +278,10 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS); - /* delay here to ensure the data byte has gotten onto the bus - * before the transaction is started */ - + /* + * delay here to ensure the data byte has gotten onto the bus + * before the transaction is started + */ ndelay(i2c->tx_setup); dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); @@ -354,8 +356,10 @@ static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) s3c24xx_i2c_disable_irq(i2c); } -/* helper functions to determine the current state in the set of - * messages we are sending */ +/* + * helper functions to determine the current state in the set of + * messages we are sending + */ /* * returns TRUE if the current message is the last in the set @@ -370,9 +374,11 @@ static inline int is_lastmsg(struct s3c24xx_i2c *i2c) */ static inline int is_msglast(struct s3c24xx_i2c *i2c) { - /* msg->len is always 1 for the first byte of smbus block read. + /* + * msg->len is always 1 for the first byte of smbus block read. * Actual length will be read from slave. More bytes will be - * read according to the length then. */ + * read according to the length then. + */ if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) return 0; @@ -408,14 +414,13 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) goto out_ack; case STATE_START: - /* last thing we did was send a start condition on the + /* + * last thing we did was send a start condition on the * bus, or started a new i2c message */ - if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ - dev_dbg(i2c->dev, "ack was not received\n"); s3c24xx_i2c_stop(i2c, -ENXIO); goto out_ack; @@ -426,9 +431,10 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) else i2c->state = STATE_WRITE; - /* terminate the transfer if there is nothing to do - * as this is used by the i2c probe to find devices. */ - + /* + * Terminate the transfer if there is nothing to do + * as this is used by the i2c probe to find devices. + */ if (is_lastmsg(i2c) && i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); goto out_ack; @@ -437,14 +443,16 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) if (i2c->state == STATE_READ) goto prepare_read; - /* fall through to the write state, as we will need to - * send a byte as well */ + /* + * fall through to the write state, as we will need to + * send a byte as well + */ case STATE_WRITE: - /* we are writing data to the device... check for the + /* + * we are writing data to the device... check for the * end of the message, and if so, work out what to do */ - if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { if (iicstat & S3C2410_IICSTAT_LASTBIT) { dev_dbg(i2c->dev, "WRITE: No Ack\n"); @@ -460,12 +468,13 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) byte = i2c->msg->buf[i2c->msg_ptr++]; writeb(byte, i2c->regs + S3C2410_IICDS); - /* delay after writing the byte to allow the + /* + * delay after writing the byte to allow the * data setup time on the bus, as writing the * data to the register causes the first bit * to appear on SDA, and SCL will change as - * soon as the interrupt is acknowledged */ - + * soon as the interrupt is acknowledged + */ ndelay(i2c->tx_setup); } else if (!is_lastmsg(i2c)) { @@ -481,10 +490,11 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) if (i2c->msg->flags & I2C_M_NOSTART) { if (i2c->msg->flags & I2C_M_RD) { - /* cannot do this, the controller + /* + * cannot do this, the controller * forces us to send a new START - * when we change direction */ - + * when we change direction + */ s3c24xx_i2c_stop(i2c, -EINVAL); } @@ -497,17 +507,16 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) } else { /* send stop */ - s3c24xx_i2c_stop(i2c, 0); } break; case STATE_READ: - /* we have a byte of data in the data register, do + /* + * we have a byte of data in the data register, do * something with it, and then work out whether we are * going to do any more read/write */ - byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; @@ -522,9 +531,10 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) s3c24xx_i2c_disable_ack(i2c); } else if (is_msgend(i2c)) { - /* ok, we've read the entire buffer, see if there - * is anything else we need to do */ - + /* + * ok, we've read the entire buffer, see if there + * is anything else we need to do + */ if (is_lastmsg(i2c)) { /* last message, send stop and complete */ dev_dbg(i2c->dev, "READ: Send Stop\n"); @@ -578,9 +588,10 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) goto out; } - /* pretty much this leaves us with the fact that we've - * transmitted or received whatever byte we last sent */ - + /* + * pretty much this leaves us with the fact that we've + * transmitted or received whatever byte we last sent + */ i2c_s3c_irq_nextbyte(i2c, status); out: @@ -726,9 +737,10 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, ret = i2c->msg_idx; - /* having these next two as dev_err() makes life very - * noisy when doing an i2cdetect */ - + /* + * Having these next two as dev_err() makes life very + * noisy when doing an i2cdetect + */ if (timeout == 0) dev_dbg(i2c->dev, "timeout\n"); else if (ret != num) @@ -1071,10 +1083,7 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) } #else static void -s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) -{ - return; -} +s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) { } #endif static int s3c24xx_i2c_probe(struct platform_device *pdev) @@ -1117,7 +1126,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) init_waitqueue_head(&i2c->wait); /* find the clock and enable it */ - i2c->dev = &pdev->dev; i2c->clk = devm_clk_get(&pdev->dev, "i2c"); if (IS_ERR(i2c->clk)) { @@ -1127,9 +1135,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); - /* map the registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->regs = devm_ioremap_resource(&pdev->dev, res); @@ -1140,22 +1146,17 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) i2c->regs, res); /* setup info block for the i2c core */ - i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; - i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev); /* inititalise the i2c gpio lines */ - - if (i2c->pdata->cfg_gpio) { + if (i2c->pdata->cfg_gpio) i2c->pdata->cfg_gpio(to_platform_device(i2c->dev)); - } else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) { + else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) return -EINVAL; - } /* initialise the i2c controller */ - clk_prepare_enable(i2c->clk); ret = s3c24xx_i2c_init(i2c); clk_disable(i2c->clk); @@ -1164,10 +1165,11 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) clk_unprepare(i2c->clk); return ret; } - /* find the IRQ for this unit (note, this relies on the init call to + + /* + * find the IRQ for this unit (note, this relies on the init call to * ensure no current IRQs pending */ - if (!(i2c->quirks & QUIRK_POLL)) { i2c->irq = ret = platform_get_irq(pdev, 0); if (ret <= 0) { @@ -1176,9 +1178,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) return ret; } - ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, - dev_name(&pdev->dev), i2c); - + ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, + 0, dev_name(&pdev->dev), i2c); if (ret != 0) { dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); clk_unprepare(i2c->clk); @@ -1193,12 +1194,12 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) return ret; } - /* Note, previous versions of the driver used i2c_add_adapter() + /* + * Note, previous versions of the driver used i2c_add_adapter() * to add the bus at any number. We now pass the bus number via * the platform data, so if unset it will now default to always * being bus 0. */ - i2c->adap.nr = i2c->pdata->bus_num; i2c->adap.dev.of_node = pdev->dev.of_node; -- cgit From 8f8edd491ac64c3cfe4e8ffff2b7385df5c02860 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 20 Apr 2016 10:37:55 -0400 Subject: i2c: s3c2410: Print errno code in error logs The driver not always prints the error code in case of a failure but this information can be very useful for debugging. So let's print if available. Signed-off-by: Javier Martinez Canillas Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index dde1abce07ee..c08830549578 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -926,7 +926,7 @@ static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb, i2c_unlock_adapter(&i2c->adap); if (ret < 0) - dev_err(i2c->dev, "cannot find frequency\n"); + dev_err(i2c->dev, "cannot find frequency (%d)\n", ret); else dev_info(i2c->dev, "setting freq %d\n", got); } @@ -977,7 +977,8 @@ static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c) ret = gpio_request(gpio, "i2c-bus"); if (ret) { - dev_err(i2c->dev, "gpio [%d] request failed\n", gpio); + dev_err(i2c->dev, "gpio [%d] request failed (%d)\n", + gpio, ret); goto free_gpio; } } -- cgit From d16415b2627e91a45778e1888bfdd5c6744294bf Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 20 Apr 2016 10:37:56 -0400 Subject: i2c: s3c2410: Check clk_prepare_enable() return value The clk_prepare_enable() function can fail so check the return value and propagate the error in case of a failure. Signed-off-by: Javier Martinez Canillas Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-s3c2410.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index c08830549578..38dc1cacfd8b 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -1158,7 +1158,12 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) return -EINVAL; /* initialise the i2c controller */ - clk_prepare_enable(i2c->clk); + ret = clk_prepare_enable(i2c->clk); + if (ret) { + dev_err(&pdev->dev, "I2C clock enable failed\n"); + return ret; + } + ret = s3c24xx_i2c_init(i2c); clk_disable(i2c->clk); if (ret != 0) { -- cgit From 392d01de2d2476e8a50cf30f049e369deaaea127 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Tue, 26 Apr 2016 16:26:52 +0200 Subject: i2c: octeon: Remove zero-length message support Zero-length message support (SMBUS QUICK or i2c) never worked with the Octeon hardware. Disable SMBUS QUICK support and bail out in case of a zero-length i2c request. After this change 'i2c-detect -q' will return an error on Octeon but the previously reported results were wrong anyway. Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 6a4f0581a7e0..4fc471ce173e 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -838,9 +838,6 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, int i, result, length = *rlength; bool final_read = false; - if (length < 1) - return -EINVAL; - octeon_i2c_data_write(i2c, (target << 1) | 1); octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); @@ -926,6 +923,12 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, for (i = 0; ret == 0 && i < num; i++) { struct i2c_msg *pmsg = &msgs[i]; + /* zero-length messages are not supported */ + if (!pmsg->len) { + ret = -EOPNOTSUPP; + break; + } + ret = octeon_i2c_start(i2c); if (ret) return ret; @@ -999,7 +1002,7 @@ static struct i2c_bus_recovery_info octeon_i2c_recovery_info = { static u32 octeon_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL; } -- cgit From 1bb1ff3e7c74f4569dddf7bda8054a0bb6ed0073 Mon Sep 17 00:00:00 2001 From: Peter Swain Date: Mon, 25 Apr 2016 16:33:37 +0200 Subject: i2c: octeon: Improve performance if interrupt is early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a race between the TWSI interrupt and the condition that is required before proceeding: Low-level: interrupt flag bit must be set High-level controller: valid bit must be clear If the interrupt comes too early and the condition is not met the wait will time out, and the transfer is aborted leading to very poor performance. To avoid this race retry for the condition ~80 µs later. The retry is avoided on the very first invocation of wait_event_timeout() (which tests the condition before entering the wait and is therefore always wrong in this case). EEPROM reads on 100kHz i2c now measure ~5.2kB/s, about 1/2 what's achievable, and much better than the worst-case 100 bytes/sec before. While at it remove the debug print from the low-level wait function. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 55 ++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 4fc471ce173e..cb418140cbbe 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -109,6 +109,8 @@ #define TWSI_INT_SDA BIT_ULL(10) #define TWSI_INT_SCL BIT_ULL(11) +#define I2C_OCTEON_EVENT_WAIT 80 /* microseconds */ + struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; @@ -339,11 +341,29 @@ static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) return IRQ_HANDLED; } -static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) +static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c) { return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG); } +static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first) +{ + if (octeon_i2c_test_iflg(i2c)) + return true; + + if (*first) { + *first = false; + return false; + } + + /* + * IRQ has signaled an event but IFLG hasn't changed. + * Sleep and retry once. + */ + usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); + return octeon_i2c_test_iflg(i2c); +} + /** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c @@ -353,15 +373,14 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; + bool first = 1; i2c->int_enable(i2c); - time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first), i2c->adap.timeout); i2c->int_disable(i2c); - if (!time_left) { - dev_dbg(i2c->dev, "%s: timeout\n", __func__); + if (!time_left) return -ETIMEDOUT; - } return 0; } @@ -427,11 +446,28 @@ static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read) } } -static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c) +static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c) +{ + return (__raw_readq(i2c->twsi_base + SW_TWSI) & SW_TWSI_V) == 0; +} + +static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first) { - u64 val = __raw_readq(i2c->twsi_base + SW_TWSI); + /* check if valid bit is cleared */ + if (octeon_i2c_hlc_test_valid(i2c)) + return true; - return (val & SW_TWSI_V) == 0; + if (*first) { + *first = false; + return false; + } + + /* + * IRQ has signaled an event but valid bit isn't cleared. + * Sleep and retry once. + */ + usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); + return octeon_i2c_hlc_test_valid(i2c); } static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) @@ -453,11 +489,12 @@ static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) */ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { + bool first = 1; int time_left; i2c->hlc_int_enable(i2c); time_left = wait_event_timeout(i2c->queue, - octeon_i2c_hlc_test_ready(i2c), + octeon_i2c_hlc_test_ready(i2c, &first), i2c->adap.timeout); i2c->hlc_int_disable(i2c); if (!time_left) { -- cgit From 2b899f34e1db9adef8716d07e872a800dfa60790 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 26 Apr 2016 08:27:46 +0200 Subject: i2c: imx: reduce load by using usleep_range instead of udelay Documentation/timers/timers-howto.txt recommends to use usleep_range on delays > 10usec. According to my test results with Neonode zForce touchscreen driver, usleep_range indeed reduces CPU load. Stats collected with "./perf record -a -g -F 1000 sleep 10" i2c-imx with udelay(50): 34.19% 0.00% irq/220-Neonode [kernel.kallsyms] [k] irq_thread ---irq_thread |--33.75%--irq_thread_fn | |--19.27%--0x7f08a878 | | i2c_master_recv | | i2c_transfer | | __i2c_transfer | | i2c_imx_xfer | | |--11.71%--i2c_imx_trx_complete | | |--5.70%--i2c_imx_start <<<<---------------- | | | |--5.38%--__timer_const_udelay | | | | __timer_delay | | | | --5.07%--read_current_timer i2c-imx with usleep_range(50,100) 29.08% 0.00% irq/220-Neonode [kernel.kallsyms] [k] irq_thread ---irq_thread |--28.89%--irq_thread_fn | |--17.21%--0x7f08a878 | | i2c_master_recv | | |--17.14%--i2c_transfer | | | __i2c_transfer | | | i2c_imx_xfer | | | |--14.29%--i2c_imx_trx_complete | | | |--1.42%--i2c_imx_start <<<<---------- | | | | |--0.71%--usleep_range | | | | |--0.53%--i2c_imx_bus_busy Signed-off-by: Oleksij Rempel Signed-off-by: Dirk Behme Reviewed-by: Vladimir Zapolskiy Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 1ca7ef2314f7..1844bc9f7cd5 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -525,7 +525,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR); /* Wait controller to be stable */ - udelay(50); + usleep_range(50, 150); /* Start I2C transaction */ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); -- cgit From dd485951e79a6dcbf18ba1612ad91d45e4cfa9a7 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 26 Apr 2016 13:22:33 -0400 Subject: i2c: nforce2: Use IS_ENABLED() instead of checking for built-in or module The IS_ENABLED() macro checks if a Kconfig symbol has been enabled either built-in or as a module, use that macro instead of open coding the same. Signed-off-by: Javier Martinez Canillas Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-nforce2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 70b3c9158509..42fcc9458432 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -127,7 +127,7 @@ static struct pci_driver nforce2_driver; /* For multiplexing support, we need a global reference to the 1st SMBus channel */ -#if defined CONFIG_I2C_NFORCE2_S4985 || defined CONFIG_I2C_NFORCE2_S4985_MODULE +#if IS_ENABLED(CONFIG_I2C_NFORCE2_S4985) struct i2c_adapter *nforce2_smbus; EXPORT_SYMBOL_GPL(nforce2_smbus); -- cgit From fe600cf6424ff45712a175351b11c3ff1206ff2c Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Apr 2016 11:44:39 +0200 Subject: i2c: octeon: Add workaround for broken irqs on CN3860 CN3860 does not interrupt the CPU when the i2c status changes. If we get a timeout, and see the status has in fact changed, we know we have this problem, and drop back to polling. Signed-off-by: David Daney Signed-off-by: Jan Glauber Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-octeon.c | 53 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index cb418140cbbe..aa5f01efd826 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -121,6 +121,8 @@ struct octeon_i2c { void __iomem *twsi_base; struct device *dev; bool hlc_enabled; + bool broken_irq_mode; + bool broken_irq_check; void (*int_enable)(struct octeon_i2c *); void (*int_disable)(struct octeon_i2c *); void (*hlc_int_enable)(struct octeon_i2c *); @@ -375,10 +377,32 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) long time_left; bool first = 1; + /* + * Some chip revisions don't assert the irq in the interrupt + * controller. So we must poll for the IFLG change. + */ + if (i2c->broken_irq_mode) { + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + i2c->int_enable(i2c); time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first), i2c->adap.timeout); i2c->int_disable(i2c); + + if (i2c->broken_irq_check && !time_left && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + if (!time_left) return -ETIMEDOUT; @@ -492,15 +516,37 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) bool first = 1; int time_left; + /* + * Some cn38xx boards don't assert the irq in the interrupt + * controller. So we must poll for the valid bit change. + */ + if (i2c->broken_irq_mode) { + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_valid(i2c) && + time_before64(get_jiffies_64(), end)) + usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT); + + return octeon_i2c_hlc_test_valid(i2c) ? 0 : -ETIMEDOUT; + } + i2c->hlc_int_enable(i2c); time_left = wait_event_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c, &first), i2c->adap.timeout); i2c->hlc_int_disable(i2c); - if (!time_left) { + if (!time_left) octeon_i2c_hlc_int_clear(i2c); - return -ETIMEDOUT; + + if (i2c->broken_irq_check && !time_left && + octeon_i2c_hlc_test_valid(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; } + + if (!time_left) + return -ETIMEDOUT; return 0; } @@ -1143,6 +1189,9 @@ static int octeon_i2c_probe(struct platform_device *pdev) goto out; } + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) + i2c->broken_irq_check = true; + result = octeon_i2c_init_lowlevel(i2c); if (result) { dev_err(i2c->dev, "init low level failed\n"); -- cgit From 05872b8039101867e7312c6c3a560b887b2d9079 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 22 Apr 2016 15:19:51 +0200 Subject: i2c: mv64xxx: enable the driver on ARCH_MVEBU The new ARM64 Marvell Armada 7K/8K SoC family is using the same I2C controller as the 32-bits Marvell EBU SoCs, so this commit allows mv64xxx to be enabled when ARCH_MVEBU=y. Signed-off-by: Thomas Petazzoni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index faa8e6821fea..29664338aab1 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -663,7 +663,7 @@ config I2C_MT65XX config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" - depends on MV64X60 || PLAT_ORION || ARCH_SUNXI + depends on MV64X60 || PLAT_ORION || ARCH_SUNXI || ARCH_MVEBU help If you say yes to this option, support will be included for the built-in I2C interface on the Marvell 64xxx line of host bridges. -- cgit From 9f4d6f164229d4221272186597d9393f654adbb9 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 22 Apr 2016 15:19:52 +0200 Subject: i2c: mv64xxx: handle probe deferral for the clock If a clock is registered by a platform driver and not by the OF_CLK_DECLARE() mechanism, it might show up after the first attempt to probe i2c-mv64xxx. In order to solve this, we need to handle -EPROBE_PREFER as a special return value of devm_clk_get(), and return the same error code from probe(). This gives us three situations: - There is no reference to a clock in the DT. In this case, devm_clk_get() returns an error that is not -EPROBE_DEFER (something like -ENODEV), and we continue the probing without enabling the clock. - There is a reference to the clock in the DT, and the clock is ready. devm_clk_get() returns a valid reference to the clock, and we prepare/enable it. - There is a reference to the clock in the DT, but the clock is not ready. devm_clk_get() returns -EPROBE_DEFER, and we exit from probe() with the same error code so that probe() is tried again later. This is needed for Marvell Armada 7K/8K, where the clock driver is a platform driver. Signed-off-by: Thomas Petazzoni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 43207f52e5a3..4c6282a78109 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -910,6 +910,8 @@ mv64xxx_i2c_probe(struct platform_device *pd) #if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ drv_data->clk = devm_clk_get(&pd->dev, NULL); + if (IS_ERR(drv_data->clk) && PTR_ERR(drv_data->clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; if (!IS_ERR(drv_data->clk)) { clk_prepare(drv_data->clk); clk_enable(drv_data->clk); -- cgit From 70719350ca250a8397b74f416a45df3d1868a1d2 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 22 Apr 2016 15:19:53 +0200 Subject: i2c: mv64xxx: use clk_{prepare_enable,disable_unprepare} Instead of separately calling clk_prepare()/clk_enable(), use clk_prepare_enable(), and instead of calling clk_disable()/clk_unprepare(), use clk_disable_unprepare(). Those handy shortcuts have been introduced specifically to simplify the numerous call sites were both functions were called in sequence. Signed-off-by: Thomas Petazzoni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 4c6282a78109..e87db0344708 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -912,10 +912,8 @@ mv64xxx_i2c_probe(struct platform_device *pd) drv_data->clk = devm_clk_get(&pd->dev, NULL); if (IS_ERR(drv_data->clk) && PTR_ERR(drv_data->clk) == -EPROBE_DEFER) return -EPROBE_DEFER; - if (!IS_ERR(drv_data->clk)) { - clk_prepare(drv_data->clk); - clk_enable(drv_data->clk); - } + if (!IS_ERR(drv_data->clk)) + clk_prepare_enable(drv_data->clk); #endif if (pdata) { drv_data->freq_m = pdata->freq_m; @@ -968,10 +966,8 @@ exit_reset: exit_clk: #if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ - if (!IS_ERR(drv_data->clk)) { - clk_disable(drv_data->clk); - clk_unprepare(drv_data->clk); - } + if (!IS_ERR(drv_data->clk)) + clk_disable_unprepare(drv_data->clk); #endif return rc; } @@ -987,10 +983,8 @@ mv64xxx_i2c_remove(struct platform_device *dev) reset_control_assert(drv_data->rstc); #if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ - if (!IS_ERR(drv_data->clk)) { - clk_disable(drv_data->clk); - clk_unprepare(drv_data->clk); - } + if (!IS_ERR(drv_data->clk)) + clk_disable_unprepare(drv_data->clk); #endif return 0; -- cgit From f3a36fbdd359608bf73d674533cc419bf7e65aae Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 22 Apr 2016 15:19:54 +0200 Subject: i2c: mv64xxx: remove CONFIG_HAVE_CLK conditionals When clock support was added to the i2c-mv64xxx, not all clk functions had stubs when for !CONFIG_HAVE_CLK configurations. However, nowadays, both "struct clk" and all the clock framework functions have stubs when CONFIG_HAVE_CLK is not enabled, so it no longer makes sense to carry such compile-time conditionals in the driver. This commit was compile tested on both ARM64 (which has both CONFIG_OF=y and CONFIG_HAVE_CLK=y) and PowerPC c2k_defconfig (which has CONFIG_OF=y, CONFIG_HAVE_CLK disabled, and the i2c-mv64xxx driver enabled). The only non-trivial change is in the mv64xxx_of_config() function, which was returning -ENODEV unconditionally if CONFIG_HAVE_CLK was disabled. Simply removing this condition works fine because the first test done by the function is to verify if drv_data->clk points to a valid clock, and if it doesn't, we return -ENODEV. When CONFIG_HAVE_CLK is disabled, devm_clk_get() unconditionally returns NULL, so mv64xxx_of_config() will return -ENODEV when no clock is provided, which is the intended behavior. Signed-off-by: Thomas Petazzoni Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index e87db0344708..b4dec0841bc2 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -134,9 +134,7 @@ struct mv64xxx_i2c_data { int rc; u32 freq_m; u32 freq_n; -#if defined(CONFIG_HAVE_CLK) struct clk *clk; -#endif wait_queue_head_t waitq; spinlock_t lock; struct i2c_msg *msg; @@ -757,7 +755,6 @@ static const struct of_device_id mv64xxx_i2c_of_match_table[] = { MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table); #ifdef CONFIG_OF -#ifdef CONFIG_HAVE_CLK static int mv64xxx_calc_freq(struct mv64xxx_i2c_data *drv_data, const int tclk, const int n, const int m) @@ -791,25 +788,20 @@ mv64xxx_find_baud_factors(struct mv64xxx_i2c_data *drv_data, return false; return true; } -#endif /* CONFIG_HAVE_CLK */ static int mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, struct device *dev) { - /* CLK is mandatory when using DT to describe the i2c bus. We - * need to know tclk in order to calculate bus clock - * factors. - */ -#if !defined(CONFIG_HAVE_CLK) - /* Have OF but no CLK */ - return -ENODEV; -#else const struct of_device_id *device; struct device_node *np = dev->of_node; u32 bus_freq, tclk; int rc = 0; + /* CLK is mandatory when using DT to describe the i2c bus. We + * need to know tclk in order to calculate bus clock + * factors. + */ if (IS_ERR(drv_data->clk)) { rc = -ENODEV; goto out; @@ -869,7 +861,6 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data, out: return rc; -#endif } #else /* CONFIG_OF */ static int @@ -907,14 +898,13 @@ mv64xxx_i2c_probe(struct platform_device *pd) init_waitqueue_head(&drv_data->waitq); spin_lock_init(&drv_data->lock); -#if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ drv_data->clk = devm_clk_get(&pd->dev, NULL); if (IS_ERR(drv_data->clk) && PTR_ERR(drv_data->clk) == -EPROBE_DEFER) return -EPROBE_DEFER; if (!IS_ERR(drv_data->clk)) clk_prepare_enable(drv_data->clk); -#endif + if (pdata) { drv_data->freq_m = pdata->freq_m; drv_data->freq_n = pdata->freq_n; @@ -964,11 +954,10 @@ exit_reset: if (!IS_ERR_OR_NULL(drv_data->rstc)) reset_control_assert(drv_data->rstc); exit_clk: -#if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ if (!IS_ERR(drv_data->clk)) clk_disable_unprepare(drv_data->clk); -#endif + return rc; } @@ -981,11 +970,9 @@ mv64xxx_i2c_remove(struct platform_device *dev) free_irq(drv_data->irq, drv_data); if (!IS_ERR_OR_NULL(drv_data->rstc)) reset_control_assert(drv_data->rstc); -#if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ if (!IS_ERR(drv_data->clk)) clk_disable_unprepare(drv_data->clk); -#endif return 0; } -- cgit From 8a350183010a5557287a10c2a13b1a4cbaa7b130 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 21 Apr 2016 15:12:44 +0900 Subject: i2c: uniphier: add "\n" at the end of error log Just in case. Signed-off-by: Masahiro Yamada Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-uniphier-f.c | 2 +- drivers/i2c/busses/i2c-uniphier.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c index 213ba55e17c3..aeead0d27d10 100644 --- a/drivers/i2c/busses/i2c-uniphier-f.c +++ b/drivers/i2c/busses/i2c-uniphier-f.c @@ -524,7 +524,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "failed to get IRQ number"); + dev_err(dev, "failed to get IRQ number\n"); return irq; } diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c index 89eaa8a7e1e0..475a5eb514e2 100644 --- a/drivers/i2c/busses/i2c-uniphier.c +++ b/drivers/i2c/busses/i2c-uniphier.c @@ -381,7 +381,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "failed to get IRQ number"); + dev_err(dev, "failed to get IRQ number\n"); return irq; } -- cgit From 8320f495cf441d593f7cd4f30e6b63455be71a2c Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:27 +0200 Subject: i2c: allow adapter drivers to override the adapter locking Add i2c_lock_bus() and i2c_unlock_bus(), which call the new lock_bus and unlock_bus ops in the adapter. These funcs/ops take an additional flags argument that indicates for what purpose the adapter is locked. There are two flags, I2C_LOCK_ROOT_ADAPTER and I2C_LOCK_SEGMENT, but they are both implemented the same. For now. Locking the root adapter means that the whole bus is locked, locking the segment means that only the current bus segment is locked (i.e. i2c traffic on the parent side of a mux is still allowed even if the child side of the mux is locked). Also support a trylock_bus op (but no function to call it, as it is not expected to be needed outside of the i2c core). Implement i2c_lock_adapter/i2c_unlock_adapter in terms of the new locking scheme (i.e. lock with the I2C_LOCK_ROOT_ADAPTER flag). Locking the root adapter and locking the segment is the same thing for all root adapters (e.g. in the normal case of a simple topology with no i2c muxes). The two locking variants are also the same for traditional muxes (aka parent-locked muxes). These muxes traverse the tree, locking each level as they go until they reach the root. This patch is preparatory for a later patch in the series introducing mux-locked muxes, which behave differently depending on the requested locking. Since all current users are using i2c_lock_adapter, which is a wrapper for I2C_LOCK_ROOT_ADAPTER, we only need to annotate the calls that will not need to lock the root adapter for mux-locked muxes. I.e. the instances that needs to use I2C_LOCK_SEGMENT instead of i2c_lock_adapter/I2C_LOCK_ROOT_ADAPTER. Those instances are in the i2c_transfer and i2c_smbus_xfer functions, so that mux-locked muxes can single out normal i2c accesses to its slave side and adjust the locking for those accesses. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 41 +++++++++++++++++++++++++++-------------- include/linux/i2c.h | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 4979728f7fb2..7ef5bd085476 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -954,10 +954,13 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) } /** - * i2c_lock_adapter - Get exclusive access to an I2C bus segment + * i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT + * locks only this branch in the adapter tree */ -void i2c_lock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, + unsigned int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -966,27 +969,32 @@ void i2c_lock_adapter(struct i2c_adapter *adapter) else rt_mutex_lock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_lock_adapter); /** - * i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment + * i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT + * trylocks only this branch in the adapter tree */ -static int i2c_trylock_adapter(struct i2c_adapter *adapter) +static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, + unsigned int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); if (parent) - return i2c_trylock_adapter(parent); + return parent->trylock_bus(parent, flags); else return rt_mutex_trylock(&adapter->bus_lock); } /** - * i2c_unlock_adapter - Release exclusive access to an I2C bus segment + * i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT + * unlocks only this branch in the adapter tree */ -void i2c_unlock_adapter(struct i2c_adapter *adapter) +static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, + unsigned int flags) { struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); @@ -995,7 +1003,6 @@ void i2c_unlock_adapter(struct i2c_adapter *adapter) else rt_mutex_unlock(&adapter->bus_lock); } -EXPORT_SYMBOL_GPL(i2c_unlock_adapter); static void i2c_dev_set_name(struct i2c_adapter *adap, struct i2c_client *client) @@ -1541,6 +1548,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) return -EINVAL; } + if (!adap->lock_bus) { + adap->lock_bus = i2c_adapter_lock_bus; + adap->trylock_bus = i2c_adapter_trylock_bus; + adap->unlock_bus = i2c_adapter_unlock_bus; + } + rt_mutex_init(&adap->bus_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); @@ -2310,16 +2323,16 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = i2c_trylock_adapter(adap); + ret = adap->trylock_bus(adap, I2C_LOCK_SEGMENT); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { - i2c_lock_adapter(adap); + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); } ret = __i2c_transfer(adap, msgs, num); - i2c_unlock_adapter(adap); + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); return ret; } else { @@ -3094,7 +3107,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; if (adapter->algo->smbus_xfer) { - i2c_lock_adapter(adapter); + i2c_lock_bus(adapter, I2C_LOCK_SEGMENT); /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; @@ -3108,7 +3121,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, orig_jiffies + adapter->timeout)) break; } - i2c_unlock_adapter(adapter); + i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT); if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) goto trace; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index c30833b7b073..50934d6e1050 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -538,6 +538,10 @@ struct i2c_adapter { struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; + + void (*lock_bus)(struct i2c_adapter *, unsigned int flags); + int (*trylock_bus)(struct i2c_adapter *, unsigned int flags); + void (*unlock_bus)(struct i2c_adapter *, unsigned int flags); }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) @@ -567,8 +571,44 @@ i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter) int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *)); /* Adapter locking functions, exported for shared pin cases */ -void i2c_lock_adapter(struct i2c_adapter *); -void i2c_unlock_adapter(struct i2c_adapter *); +#define I2C_LOCK_ROOT_ADAPTER BIT(0) +#define I2C_LOCK_SEGMENT BIT(1) + +/** + * i2c_lock_bus - Get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT + * locks only this branch in the adapter tree + */ +static inline void +i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + adapter->lock_bus(adapter, flags); +} + +/** + * i2c_unlock_bus - Release exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT + * unlocks only this branch in the adapter tree + */ +static inline void +i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + adapter->unlock_bus(adapter, flags); +} + +static inline void +i2c_lock_adapter(struct i2c_adapter *adapter) +{ + i2c_lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER); +} + +static inline void +i2c_unlock_adapter(struct i2c_adapter *adapter) +{ + i2c_unlock_bus(adapter, I2C_LOCK_ROOT_ADAPTER); +} /*flags for the client struct: */ #define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */ -- cgit From fa96f0cb9b37a1a296d18f7bde63b0910852303e Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:28 +0200 Subject: i2c: muxes always lock the parent adapter Instead of checking for i2c parent adapters for every lock/unlock, simply override the locking for muxes to always lock/unlock the parent adapter directly. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 21 +++------------------ drivers/i2c/i2c-mux.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 7ef5bd085476..afdee66002db 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -962,12 +962,7 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr) static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, unsigned int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - i2c_lock_adapter(parent); - else - rt_mutex_lock(&adapter->bus_lock); + rt_mutex_lock(&adapter->bus_lock); } /** @@ -979,12 +974,7 @@ static void i2c_adapter_lock_bus(struct i2c_adapter *adapter, static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - return parent->trylock_bus(parent, flags); - else - return rt_mutex_trylock(&adapter->bus_lock); + return rt_mutex_trylock(&adapter->bus_lock); } /** @@ -996,12 +986,7 @@ static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter, static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) { - struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); - - if (parent) - i2c_unlock_adapter(parent); - else - rt_mutex_unlock(&adapter->bus_lock); + rt_mutex_unlock(&adapter->bus_lock); } static void i2c_dev_set_name(struct i2c_adapter *adap, diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 25e9336b0e6e..5fa8af715e24 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -98,6 +98,33 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +static void i2c_parent_lock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + parent->lock_bus(parent, flags); +} + +static int i2c_parent_trylock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + return parent->trylock_bus(parent, flags); +} + +static void i2c_parent_unlock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + parent->unlock_bus(parent, flags); +} + struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, struct device *dev, int max_adapters, int sizeof_priv, u32 flags, @@ -165,6 +192,9 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, priv->adap.retries = parent->retries; priv->adap.timeout = parent->timeout; priv->adap.quirks = parent->quirks; + priv->adap.lock_bus = i2c_parent_lock_bus; + priv->adap.trylock_bus = i2c_parent_trylock_bus; + priv->adap.unlock_bus = i2c_parent_unlock_bus; /* Sanity check on class */ if (i2c_mux_parent_classes(parent) & class) -- cgit From 6ef91fcca8a8ba3df9810a4cc6cd6a9d3f21bf45 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:29 +0200 Subject: i2c: mux: relax locking of the top i2c adapter during mux-locked muxing With a i2c topology like the following GPIO ---| ------ BAT1 | v / I2C -----+----------+---- MUX | \ EEPROM ------ BAT2 there is a locking problem with the GPIO controller since it is a client on the same i2c bus that it muxes. Transfers to the mux clients (e.g. BAT1) will lock the whole i2c bus prior to attempting to switch the mux to the correct i2c segment. In the above case, the GPIO device is an I/O expander with an i2c interface, and since the GPIO subsystem knows nothing (and rightfully so) about the lockless needs of the i2c mux code, this results in a deadlock when the GPIO driver issues i2c transfers to modify the mux. So, observing that while it is needed to have the i2c bus locked during the actual MUX update in order to avoid random garbage on the slave side, it is not strictly a must to have it locked over the whole sequence of a full select-transfer-deselect mux client operation. The mux itself needs to be locked, so transfers to clients behind the mux are serialized, and the mux needs to be stable during all i2c traffic (otherwise individual mux slave segments might see garbage, or worse). Introduce this new locking concept as "mux-locked" muxes, and call the pre-existing mux locking scheme "parent-locked". Modify the i2c mux locking so that muxes that are "mux-locked" locks only the muxes on the parent adapter instead of the whole i2c bus when there is a transfer to the slave side of the mux. This lock serializes transfers to the slave side of the muxes on the parent adapter. Add code to i2c-mux-gpio and i2c-mux-pinctrl that checks if all involved gpio/pinctrl devices have a parent that is an i2c adapter in the same adapter tree that is muxed, and request a "mux-locked mux" if that is the case. Modify the select-transfer-deselect code for "mux-locked" muxes so that each of the select-transfer-deselect ops locks the mux parent adapter individually. Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 1 + drivers/i2c/i2c-mux.c | 152 +++++++++++++++++++++++++++++++++--- drivers/i2c/muxes/i2c-mux-gpio.c | 18 +++++ drivers/i2c/muxes/i2c-mux-pinctrl.c | 38 +++++++++ include/linux/i2c-mux.h | 8 ++ include/linux/i2c.h | 1 + 6 files changed, 205 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index afdee66002db..9da446162529 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1540,6 +1540,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) } rt_mutex_init(&adap->bus_lock); + rt_mutex_init(&adap->mux_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 5fa8af715e24..8eee98634cda 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -35,6 +35,25 @@ struct i2c_mux_priv { u32 chan_id; }; +static int __i2c_mux_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Switch to the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = __i2c_transfer(parent, msgs, num); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + static int i2c_mux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { @@ -47,7 +66,29 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap, ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) - ret = __i2c_transfer(parent, msgs, num); + ret = i2c_transfer(parent, msgs, num); + if (muxc->deselect) + muxc->deselect(muxc, priv->chan_id); + + return ret; +} + +static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap, + u16 addr, unsigned short flags, + char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_mux_priv *priv = adap->algo_data; + struct i2c_mux_core *muxc = priv->muxc; + struct i2c_adapter *parent = muxc->parent; + int ret; + + /* Select the right mux port and perform the transfer. */ + + ret = muxc->select(muxc, priv->chan_id); + if (ret >= 0) + ret = parent->algo->smbus_xfer(parent, addr, flags, + read_write, command, size, data); if (muxc->deselect) muxc->deselect(muxc, priv->chan_id); @@ -68,8 +109,8 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap, ret = muxc->select(muxc, priv->chan_id); if (ret >= 0) - ret = parent->algo->smbus_xfer(parent, addr, flags, - read_write, command, size, data); + ret = i2c_smbus_xfer(parent, addr, flags, + read_write, command, size, data); if (muxc->deselect) muxc->deselect(muxc, priv->chan_id); @@ -98,13 +139,50 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent) return class; } +static void i2c_mux_lock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + rt_mutex_lock(&parent->mux_lock); + if (!(flags & I2C_LOCK_ROOT_ADAPTER)) + return; + i2c_lock_bus(parent, flags); +} + +static int i2c_mux_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + if (!rt_mutex_trylock(&parent->mux_lock)) + return 0; /* mux_lock not locked, failure */ + if (!(flags & I2C_LOCK_ROOT_ADAPTER)) + return 1; /* we only want mux_lock, success */ + if (parent->trylock_bus(parent, flags)) + return 1; /* parent locked too, success */ + rt_mutex_unlock(&parent->mux_lock); + return 0; /* parent not locked, failure */ +} + +static void i2c_mux_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_mux_priv *priv = adapter->algo_data; + struct i2c_adapter *parent = priv->muxc->parent; + + if (flags & I2C_LOCK_ROOT_ADAPTER) + i2c_unlock_bus(parent, flags); + rt_mutex_unlock(&parent->mux_lock); +} + static void i2c_parent_lock_bus(struct i2c_adapter *adapter, unsigned int flags) { struct i2c_mux_priv *priv = adapter->algo_data; struct i2c_adapter *parent = priv->muxc->parent; - parent->lock_bus(parent, flags); + rt_mutex_lock(&parent->mux_lock); + i2c_lock_bus(parent, flags); } static int i2c_parent_trylock_bus(struct i2c_adapter *adapter, @@ -113,7 +191,12 @@ static int i2c_parent_trylock_bus(struct i2c_adapter *adapter, struct i2c_mux_priv *priv = adapter->algo_data; struct i2c_adapter *parent = priv->muxc->parent; - return parent->trylock_bus(parent, flags); + if (!rt_mutex_trylock(&parent->mux_lock)) + return 0; /* mux_lock not locked, failure */ + if (parent->trylock_bus(parent, flags)) + return 1; /* parent locked too, success */ + rt_mutex_unlock(&parent->mux_lock); + return 0; /* parent not locked, failure */ } static void i2c_parent_unlock_bus(struct i2c_adapter *adapter, @@ -122,9 +205,36 @@ static void i2c_parent_unlock_bus(struct i2c_adapter *adapter, struct i2c_mux_priv *priv = adapter->algo_data; struct i2c_adapter *parent = priv->muxc->parent; - parent->unlock_bus(parent, flags); + i2c_unlock_bus(parent, flags); + rt_mutex_unlock(&parent->mux_lock); } +struct i2c_adapter *i2c_root_adapter(struct device *dev) +{ + struct device *i2c; + struct i2c_adapter *i2c_root; + + /* + * Walk up the device tree to find an i2c adapter, indicating + * that this is an i2c client device. Check all ancestors to + * handle mfd devices etc. + */ + for (i2c = dev; i2c; i2c = i2c->parent) { + if (i2c->type == &i2c_adapter_type) + break; + } + if (!i2c) + return NULL; + + /* Continue up the tree to find the root i2c adapter */ + i2c_root = to_i2c_adapter(i2c); + while (i2c_parent_is_i2c_adapter(i2c_root)) + i2c_root = i2c_parent_is_i2c_adapter(i2c_root); + + return i2c_root; +} +EXPORT_SYMBOL_GPL(i2c_root_adapter); + struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, struct device *dev, int max_adapters, int sizeof_priv, u32 flags, @@ -143,6 +253,8 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, muxc->parent = parent; muxc->dev = dev; + if (flags & I2C_MUX_LOCKED) + muxc->mux_locked = true; muxc->select = select; muxc->deselect = deselect; muxc->max_adapters = max_adapters; @@ -176,10 +288,18 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, /* Need to do algo dynamically because we don't know ahead * of time what sort of physical adapter we'll be dealing with. */ - if (parent->algo->master_xfer) - priv->algo.master_xfer = i2c_mux_master_xfer; - if (parent->algo->smbus_xfer) - priv->algo.smbus_xfer = i2c_mux_smbus_xfer; + if (parent->algo->master_xfer) { + if (muxc->mux_locked) + priv->algo.master_xfer = i2c_mux_master_xfer; + else + priv->algo.master_xfer = __i2c_mux_master_xfer; + } + if (parent->algo->smbus_xfer) { + if (muxc->mux_locked) + priv->algo.smbus_xfer = i2c_mux_smbus_xfer; + else + priv->algo.smbus_xfer = __i2c_mux_smbus_xfer; + } priv->algo.functionality = i2c_mux_functionality; /* Now fill out new adapter structure */ @@ -192,9 +312,15 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, priv->adap.retries = parent->retries; priv->adap.timeout = parent->timeout; priv->adap.quirks = parent->quirks; - priv->adap.lock_bus = i2c_parent_lock_bus; - priv->adap.trylock_bus = i2c_parent_trylock_bus; - priv->adap.unlock_bus = i2c_parent_unlock_bus; + if (muxc->mux_locked) { + priv->adap.lock_bus = i2c_mux_lock_bus; + priv->adap.trylock_bus = i2c_mux_trylock_bus; + priv->adap.unlock_bus = i2c_mux_unlock_bus; + } else { + priv->adap.lock_bus = i2c_parent_lock_bus; + priv->adap.trylock_bus = i2c_parent_trylock_bus; + priv->adap.unlock_bus = i2c_parent_unlock_bus; + } /* Sanity check on class */ if (i2c_mux_parent_classes(parent) & class) diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index f6270ee934f9..e5cf26eefa97 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -15,6 +15,7 @@ #include #include #include +#include "../../gpio/gpiolib.h" #include struct gpiomux { @@ -137,6 +138,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) struct i2c_mux_core *muxc; struct gpiomux *mux; struct i2c_adapter *parent; + struct i2c_adapter *root; unsigned initial_state, gpio_base; int i, ret; @@ -184,6 +186,9 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, muxc); + root = i2c_root_adapter(&parent->dev); + + muxc->mux_locked = true; mux->gpio_base = gpio_base; if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { @@ -194,6 +199,9 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) } for (i = 0; i < mux->data.n_gpios; i++) { + struct device *gpio_dev; + struct gpio_desc *gpio_desc; + ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio"); if (ret) { dev_err(&pdev->dev, "Failed to request GPIO %d\n", @@ -210,8 +218,18 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) i++; /* gpio_request above succeeded, so must free */ goto err_request_gpio; } + + if (!muxc->mux_locked) + continue; + + gpio_desc = gpio_to_desc(gpio_base + mux->data.gpios[i]); + gpio_dev = &gpio_desc->gdev->dev; + muxc->mux_locked = i2c_root_adapter(gpio_dev) == root; } + if (muxc->mux_locked) + dev_info(&pdev->dev, "mux-locked i2c mux\n"); + for (i = 0; i < mux->data.n_values; i++) { u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index f4e62f4a50cc..35bb775e1b74 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -24,6 +24,7 @@ #include #include #include +#include "../../pinctrl/core.h" struct i2c_mux_pinctrl { struct i2c_mux_pinctrl_platform_data *pdata; @@ -120,10 +121,31 @@ static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, } #endif +static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( + struct pinctrl_state *state) +{ + struct i2c_adapter *root = NULL; + struct pinctrl_setting *setting; + struct i2c_adapter *pin_root; + + list_for_each_entry(setting, &state->settings, node) { + pin_root = i2c_root_adapter(setting->pctldev->dev); + if (!pin_root) + return NULL; + if (!root) + root = pin_root; + else if (root != pin_root) + return NULL; + } + + return root; +} + static int i2c_mux_pinctrl_probe(struct platform_device *pdev) { struct i2c_mux_core *muxc; struct i2c_mux_pinctrl *mux; + struct i2c_adapter *root; int i, ret; mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); @@ -202,6 +224,22 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) goto err; } + root = i2c_root_adapter(&muxc->parent->dev); + + muxc->mux_locked = true; + for (i = 0; i < mux->pdata->bus_count; i++) { + if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { + muxc->mux_locked = false; + break; + } + } + if (muxc->mux_locked && mux->pdata->pinctrl_state_idle && + root != i2c_mux_pinctrl_root_adapter(mux->state_idle)) + muxc->mux_locked = false; + + if (muxc->mux_locked) + dev_info(&pdev->dev, "mux-locked i2c mux\n"); + for (i = 0; i < mux->pdata->bus_count; i++) { u32 bus = mux->pdata->base_bus_num ? (mux->pdata->base_bus_num + i) : 0; diff --git a/include/linux/i2c-mux.h b/include/linux/i2c-mux.h index 2fa93fe1345e..d4c1d12f900d 100644 --- a/include/linux/i2c-mux.h +++ b/include/linux/i2c-mux.h @@ -27,9 +27,12 @@ #ifdef __KERNEL__ +#include + struct i2c_mux_core { struct i2c_adapter *parent; struct device *dev; + bool mux_locked; void *priv; @@ -47,11 +50,16 @@ struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent, int (*select)(struct i2c_mux_core *, u32), int (*deselect)(struct i2c_mux_core *, u32)); +/* flags for i2c_mux_alloc */ +#define I2C_MUX_LOCKED BIT(0) + static inline void *i2c_mux_priv(struct i2c_mux_core *muxc) { return muxc->priv; } +struct i2c_adapter *i2c_root_adapter(struct device *dev); + /* * Called to create an i2c bus on a multiplexed bus segment. * The chan_id parameter is passed to the select and deselect diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 50934d6e1050..96a25ae14494 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -524,6 +524,7 @@ struct i2c_adapter { /* data fields that are valid for all devices */ struct rt_mutex bus_lock; + struct rt_mutex mux_lock; int timeout; /* in jiffies */ int retries; -- cgit From 2254d24aff3ab472dca287aef0123e8f0e06a14a Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:30 +0200 Subject: i2c: mux: document i2c muxes and elaborate on parent-/mux-locked muxes Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- Documentation/i2c/i2c-topology | 370 +++++++++++++++++++++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 371 insertions(+) create mode 100644 Documentation/i2c/i2c-topology diff --git a/Documentation/i2c/i2c-topology b/Documentation/i2c/i2c-topology new file mode 100644 index 000000000000..27bfd682808d --- /dev/null +++ b/Documentation/i2c/i2c-topology @@ -0,0 +1,370 @@ +I2C topology +============ + +There are a couple of reasons for building more complex i2c topologies +than a straight-forward i2c bus with one adapter and one or more devices. + +1. A mux may be needed on the bus to prevent address collisions. + +2. The bus may be accessible from some external bus master, and arbitration + may be needed to determine if it is ok to access the bus. + +3. A device (particularly RF tuners) may want to avoid the digital noise + from the i2c bus, at least most of the time, and sits behind a gate + that has to be operated before the device can be accessed. + +Etc + +These constructs are represented as i2c adapter trees by Linux, where +each adapter has a parent adapter (except the root adapter) and zero or +more child adapters. The root adapter is the actual adapter that issues +i2c transfers, and all adapters with a parent are part of an "i2c-mux" +object (quoted, since it can also be an arbitrator or a gate). + +Depending of the particular mux driver, something happens when there is +an i2c transfer on one of its child adapters. The mux driver can +obviously operate a mux, but it can also do arbitration with an external +bus master or open a gate. The mux driver has two operations for this, +select and deselect. select is called before the transfer and (the +optional) deselect is called after the transfer. + + +Locking +======= + +There are two variants of locking available to i2c muxes, they can be +mux-locked or parent-locked muxes. As is evident from below, it can be +useful to know if a mux is mux-locked or if it is parent-locked. The +following list was correct at the time of writing: + +In drivers/i2c/muxes/ +i2c-arb-gpio-challenge Parent-locked +i2c-mux-gpio Normally parent-locked, mux-locked iff + all involved gpio pins are controlled by the + same i2c root adapter that they mux. +i2c-mux-pca9541 Parent-locked +i2c-mux-pca954x Parent-locked +i2c-mux-pinctrl Normally parent-locked, mux-locked iff + all involved pinctrl devices are controlled + by the same i2c root adapter that they mux. +i2c-mux-reg Parent-locked + +In drivers/iio/ +imu/inv_mpu6050/ Parent-locked + +In drivers/media/ +dvb-frontends/m88ds3103 Parent-locked +dvb-frontends/rtl2830 Parent-locked +dvb-frontends/rtl2832 Parent-locked +dvb-frontends/si2168 Parent-locked +usb/cx231xx/ Parent-locked + + +Mux-locked muxes +---------------- + +Mux-locked muxes does not lock the entire parent adapter during the +full select-transfer-deselect transaction, only the muxes on the parent +adapter are locked. Mux-locked muxes are mostly interesting if the +select and/or deselect operations must use i2c transfers to complete +their tasks. Since the parent adapter is not fully locked during the +full transaction, unrelated i2c transfers may interleave the different +stages of the transaction. This has the benefit that the mux driver +may be easier and cleaner to implement, but it has some caveats. + +ML1. If you build a topology with a mux-locked mux being the parent + of a parent-locked mux, this might break the expectation from the + parent-locked mux that the root adapter is locked during the + transaction. + +ML2. It is not safe to build arbitrary topologies with two (or more) + mux-locked muxes that are not siblings, when there are address + collisions between the devices on the child adapters of these + non-sibling muxes. + + I.e. the select-transfer-deselect transaction targeting e.g. device + address 0x42 behind mux-one may be interleaved with a similar + operation targeting device address 0x42 behind mux-two. The + intension with such a topology would in this hypothetical example + be that mux-one and mux-two should not be selected simultaneously, + but mux-locked muxes do not guarantee that in all topologies. + +ML3. A mux-locked mux cannot be used by a driver for auto-closing + gates/muxes, i.e. something that closes automatically after a given + number (one, in most cases) of i2c transfers. Unrelated i2c transfers + may creep in and close prematurely. + +ML4. If any non-i2c operation in the mux driver changes the i2c mux state, + the driver has to lock the root adapter during that operation. + Otherwise garbage may appear on the bus as seen from devices + behind the mux, when an unrelated i2c transfer is in flight during + the non-i2c mux-changing operation. + + +Mux-locked Example +------------------ + + .----------. .--------. + .--------. | mux- |-----| dev D1 | + | root |--+--| locked | '--------' + '--------' | | mux M1 |--. .--------. + | '----------' '--| dev D2 | + | .--------. '--------' + '--| dev D3 | + '--------' + +When there is an access to D1, this happens: + + 1. Someone issues an i2c-transfer to D1. + 2. M1 locks muxes on its parent (the root adapter in this case). + 3. M1 calls ->select to ready the mux. + 4. M1 (presumably) does some i2c-transfers as part of its select. + These transfers are normal i2c-transfers that locks the parent + adapter. + 5. M1 feeds the i2c-transfer from step 1 to its parent adapter as a + normal i2c-transfer that locks the parent adapter. + 6. M1 calls ->deselect, if it has one. + 7. Same rules as in step 4, but for ->deselect. + 8. M1 unlocks muxes on its parent. + +This means that accesses to D2 are lockout out for the full duration +of the entire operation. But accesses to D3 are possibly interleaved +at any point. + + +Parent-locked muxes +------------------- + +Parent-locked muxes lock the parent adapter during the full select- +transfer-deselect transaction. The implication is that the mux driver +has to ensure that any and all i2c transfers through that parent +adapter during the transaction are unlocked i2c transfers (using e.g. +__i2c_transfer), or a deadlock will follow. There are a couple of +caveats. + +PL1. If you build a topology with a parent-locked mux being the child + of another mux, this might break a possible assumption from the + child mux that the root adapter is unused between its select op + and the actual transfer (e.g. if the child mux is auto-closing + and the parent mux issus i2c-transfers as part of its select). + This is especially the case if the parent mux is mux-locked, but + it may also happen if the parent mux is parent-locked. + +PL2. If select/deselect calls out to other subsystems such as gpio, + pinctrl, regmap or iio, it is essential that any i2c transfers + caused by these subsystems are unlocked. This can be convoluted to + accomplish, maybe even impossible if an acceptably clean solution + is sought. + + +Parent-locked Example +--------------------- + + .----------. .--------. + .--------. | parent- |-----| dev D1 | + | root |--+--| locked | '--------' + '--------' | | mux M1 |--. .--------. + | '----------' '--| dev D2 | + | .--------. '--------' + '--| dev D3 | + '--------' + +When there is an access to D1, this happens: + + 1. Someone issues an i2c-transfer to D1. + 2. M1 locks muxes on its parent (the root adapter in this case). + 3. M1 locks its parent adapter. + 4. M1 calls ->select to ready the mux. + 5. If M1 does any i2c-transfers (on this root adapter) as part of + its select, those transfers must be unlocked i2c-transfers so + that they do not deadlock the root adapter. + 6. M1 feeds the i2c-transfer from step 1 to the root adapter as an + unlocked i2c-transfer, so that it does not deadlock the parent + adapter. + 7. M1 calls ->deselect, if it has one. + 8. Same rules as in step 5, but for ->deselect. + 9. M1 unlocks its parent adapter. +10. M1 unlocks muxes on its parent. + + +This means that accesses to both D2 and D3 are locked out for the full +duration of the entire operation. + + +Complex Examples +================ + +Parent-locked mux as parent of parent-locked mux +------------------------------------------------ + +This is a useful topology, but it can be bad. + + .----------. .----------. .--------. + .--------. | parent- |-----| parent- |-----| dev D1 | + | root |--+--| locked | | locked | '--------' + '--------' | | mux M1 |--. | mux M2 |--. .--------. + | '----------' | '----------' '--| dev D2 | + | .--------. | .--------. '--------' + '--| dev D4 | '--| dev D3 | + '--------' '--------' + +When any device is accessed, all other devices are locked out for +the full duration of the operation (both muxes lock their parent, +and specifically when M2 requests its parent to lock, M1 passes +the buck to the root adapter). + +This topology is bad if M2 is an auto-closing mux and M1->select +issues any unlocked i2c transfers on the root adapter that may leak +through and be seen by the M2 adapter, thus closing M2 prematurely. + + +Mux-locked mux as parent of mux-locked mux +------------------------------------------ + +This is a good topology. + + .----------. .----------. .--------. + .--------. | mux- |-----| mux- |-----| dev D1 | + | root |--+--| locked | | locked | '--------' + '--------' | | mux M1 |--. | mux M2 |--. .--------. + | '----------' | '----------' '--| dev D2 | + | .--------. | .--------. '--------' + '--| dev D4 | '--| dev D3 | + '--------' '--------' + +When device D1 is accessed, accesses to D2 are locked out for the +full duration of the operation (muxes on the top child adapter of M1 +are locked). But accesses to D3 and D4 are possibly interleaved at +any point. Accesses to D3 locks out D1 and D2, but accesses to D4 +are still possibly interleaved. + + +Mux-locked mux as parent of parent-locked mux +--------------------------------------------- + +This is probably a bad topology. + + .----------. .----------. .--------. + .--------. | mux- |-----| parent- |-----| dev D1 | + | root |--+--| locked | | locked | '--------' + '--------' | | mux M1 |--. | mux M2 |--. .--------. + | '----------' | '----------' '--| dev D2 | + | .--------. | .--------. '--------' + '--| dev D4 | '--| dev D3 | + '--------' '--------' + +When device D1 is accessed, accesses to D2 and D3 are locked out +for the full duration of the operation (M1 locks child muxes on the +root adapter). But accesses to D4 are possibly interleaved at any +point. + +This kind of topology is generally not suitable and should probably +be avoided. The reason is that M2 probably assumes that there will +be no i2c transfers during its calls to ->select and ->deselect, and +if there are, any such transfers might appear on the slave side of M2 +as partial i2c transfers, i.e. garbage or worse. This might cause +device lockups and/or other problems. + +The topology is especially troublesome if M2 is an auto-closing +mux. In that case, any interleaved accesses to D4 might close M2 +prematurely, as might any i2c-transfers part of M1->select. + +But if M2 is not making the above stated assumption, and if M2 is not +auto-closing, the topology is fine. + + +Parent-locked mux as parent of mux-locked mux +--------------------------------------------- + +This is a good topology. + + .----------. .----------. .--------. + .--------. | parent- |-----| mux- |-----| dev D1 | + | root |--+--| locked | | locked | '--------' + '--------' | | mux M1 |--. | mux M2 |--. .--------. + | '----------' | '----------' '--| dev D2 | + | .--------. | .--------. '--------' + '--| dev D4 | '--| dev D3 | + '--------' '--------' + +When D1 is accessed, accesses to D2 are locked out for the full +duration of the operation (muxes on the top child adapter of M1 +are locked). Accesses to D3 and D4 are possibly interleaved at +any point, just as is expected for mux-locked muxes. + +When D3 or D4 are accessed, everything else is locked out. For D3 +accesses, M1 locks the root adapter. For D4 accesses, the root +adapter is locked directly. + + +Two mux-locked sibling muxes +---------------------------- + +This is a good topology. + + .--------. + .----------. .--| dev D1 | + | mux- |--' '--------' + .--| locked | .--------. + | | mux M1 |-----| dev D2 | + | '----------' '--------' + | .----------. .--------. + .--------. | | mux- |-----| dev D3 | + | root |--+--| locked | '--------' + '--------' | | mux M2 |--. .--------. + | '----------' '--| dev D4 | + | .--------. '--------' + '--| dev D5 | + '--------' + +When D1 is accessed, accesses to D2, D3 and D4 are locked out. But +accesses to D5 may be interleaved at any time. + + +Two parent-locked sibling muxes +------------------------------- + +This is a good topology. + + .--------. + .----------. .--| dev D1 | + | parent- |--' '--------' + .--| locked | .--------. + | | mux M1 |-----| dev D2 | + | '----------' '--------' + | .----------. .--------. + .--------. | | parent- |-----| dev D3 | + | root |--+--| locked | '--------' + '--------' | | mux M2 |--. .--------. + | '----------' '--| dev D4 | + | .--------. '--------' + '--| dev D5 | + '--------' + +When any device is accessed, accesses to all other devices are locked +out. + + +Mux-locked and parent-locked sibling muxes +------------------------------------------ + +This is a good topology. + + .--------. + .----------. .--| dev D1 | + | mux- |--' '--------' + .--| locked | .--------. + | | mux M1 |-----| dev D2 | + | '----------' '--------' + | .----------. .--------. + .--------. | | parent- |-----| dev D3 | + | root |--+--| locked | '--------' + '--------' | | mux M2 |--. .--------. + | '----------' '--| dev D4 | + | .--------. '--------' + '--| dev D5 | + '--------' + +When D1 or D2 are accessed, accesses to D3 and D4 are locked out while +accesses to D5 may interleave. When D3 or D4 are accessed, accesses to +all other devices are locked out. diff --git a/MAINTAINERS b/MAINTAINERS index 61a323a6b2cf..1a8b11f920e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5275,6 +5275,7 @@ I2C MUXES M: Peter Rosin L: linux-i2c@vger.kernel.org S: Maintained +F: Documentation/i2c/i2c-topology F: Documentation/i2c/muxes/ F: Documentation/devicetree/bindings/i2c/i2c-mux* F: drivers/i2c/i2c-mux.c -- cgit From 1ffcfaf19597ad26797aac3ab42d8ba6e548ef0a Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:31 +0200 Subject: iio: imu: inv_mpu6050: change the i2c gate to be mux-locked The root i2c adapter lock is then no longer held by the i2c mux during accesses behind the i2c gate, and such accesses need to take that lock just like any other ordinary i2c accesses do. So, declare the i2c gate mux-locked, and zap the code that makes the unlocked i2c accesses and just use ordinary regmap_write accesses. This also happens to fix the deadlock described in http://patchwork.ozlabs.org/patch/584776/ authored by Adriana Reus and submitted by Daniel Baluta ----------8<---------- iio: imu: inv_mpu6050: Fix deadlock between i2c adapter lock and mpu lock This deadlock occurs if the accel/gyro and the sensor on the auxiliary I2C (in my setup it's an ak8975) are working at the same time. Scenario: T1 T2 ==== ==== inv_mpu6050_read_fifo aux sensor op (eg. ak8975_read_raw) | | mutex_lock(&indio_dev->mlock) i2c_transfer | | i2c transaction i2c adapter lock | | i2c adapter lock i2c_mux_master_xfer | inv_mpu6050_select_bypass | mutex_lock(&indio_dev->mlock) When we operate on an mpu sensor the order of locking is mpu lock followed by the i2c adapter lock. However, when we operate the auxiliary sensor the order of locking is the other way around. ... ----------8<---------- The reason this patch fixes the deadlock is that T2 does not grab the i2c adapter lock until the very end (and grabs the newfangled i2c mux lock where it previously grabbed the i2c adapter lock). Acked-by: Jonathan Cameron Acked-by: Daniel Baluta Tested-by: Crestez Dan Leonard Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- Documentation/i2c/i2c-topology | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 52 ++++++------------------------- 2 files changed, 11 insertions(+), 43 deletions(-) diff --git a/Documentation/i2c/i2c-topology b/Documentation/i2c/i2c-topology index 27bfd682808d..69b008518454 100644 --- a/Documentation/i2c/i2c-topology +++ b/Documentation/i2c/i2c-topology @@ -50,7 +50,7 @@ i2c-mux-pinctrl Normally parent-locked, mux-locked iff i2c-mux-reg Parent-locked In drivers/iio/ -imu/inv_mpu6050/ Parent-locked +imu/inv_mpu6050/ Mux-locked In drivers/media/ dvb-frontends/m88ds3103 Parent-locked diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 3a078df84224..e25f7ef7f0ea 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -24,45 +24,16 @@ static const struct regmap_config inv_mpu_regmap_config = { .val_bits = 8, }; -/* - * The i2c read/write needs to happen in unlocked mode. As the parent - * adapter is common. If we use locked versions, it will fail as - * the mux adapter will lock the parent i2c adapter, while calling - * select/deselect functions. - */ -static int inv_mpu6050_write_reg_unlocked(struct i2c_client *client, - u8 reg, u8 d) -{ - int ret; - u8 buf[2] = {reg, d}; - struct i2c_msg msg[1] = { - { - .addr = client->addr, - .flags = 0, - .len = sizeof(buf), - .buf = buf, - } - }; - - ret = __i2c_transfer(client->adapter, msg, 1); - if (ret != 1) - return ret; - - return 0; -} - static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = i2c_mux_priv(muxc); - struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); + struct iio_dev *indio_dev = i2c_mux_priv(muxc); struct inv_mpu6050_state *st = iio_priv(indio_dev); int ret = 0; /* Use the same mutex which was used everywhere to protect power-op */ mutex_lock(&indio_dev->mlock); if (!st->powerup_count) { - ret = inv_mpu6050_write_reg_unlocked(client, - st->reg->pwr_mgmt_1, 0); + ret = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); if (ret) goto write_error; @@ -71,10 +42,9 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) } if (!ret) { st->powerup_count++; - ret = inv_mpu6050_write_reg_unlocked(client, - st->reg->int_pin_cfg, - INV_MPU6050_INT_PIN_CFG | - INV_MPU6050_BIT_BYPASS_EN); + ret = regmap_write(st->map, st->reg->int_pin_cfg, + INV_MPU6050_INT_PIN_CFG | + INV_MPU6050_BIT_BYPASS_EN); } write_error: mutex_unlock(&indio_dev->mlock); @@ -84,18 +54,16 @@ write_error: static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) { - struct i2c_client *client = i2c_mux_priv(muxc); - struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); + struct iio_dev *indio_dev = i2c_mux_priv(muxc); struct inv_mpu6050_state *st = iio_priv(indio_dev); mutex_lock(&indio_dev->mlock); /* It doesn't really mattter, if any of the calls fails */ - inv_mpu6050_write_reg_unlocked(client, st->reg->int_pin_cfg, - INV_MPU6050_INT_PIN_CFG); + regmap_write(st->map, st->reg->int_pin_cfg, INV_MPU6050_INT_PIN_CFG); st->powerup_count--; if (!st->powerup_count) - inv_mpu6050_write_reg_unlocked(client, st->reg->pwr_mgmt_1, - INV_MPU6050_BIT_SLEEP); + regmap_write(st->map, st->reg->pwr_mgmt_1, + INV_MPU6050_BIT_SLEEP); mutex_unlock(&indio_dev->mlock); return 0; @@ -134,7 +102,7 @@ static int inv_mpu_probe(struct i2c_client *client, st = iio_priv(dev_get_drvdata(&client->dev)); st->muxc = i2c_mux_alloc(client->adapter, &client->dev, - 1, 0, 0, + 1, 0, I2C_MUX_LOCKED, inv_mpu6050_select_bypass, inv_mpu6050_deselect_bypass); if (!st->muxc) { -- cgit From e6d7ffcdf15f40fc1cff7a3c2fd2720ce3d53e49 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 4 May 2016 22:15:32 +0200 Subject: [media] si2168: change the i2c gate to be mux-locked The root i2c adapter lock is then no longer held by the i2c mux during accesses behind the i2c gate, and such accesses need to take that lock just like any other ordinary i2c accesses do. So, declare the i2c gate mux-locked, and zap the code that makes the i2c accesses unlocked. But add a mutex so that firmware commands are still serialized. Signed-off-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- Documentation/i2c/i2c-topology | 2 +- drivers/media/dvb-frontends/si2168.c | 83 ++++++++----------------------- drivers/media/dvb-frontends/si2168_priv.h | 1 + 3 files changed, 22 insertions(+), 64 deletions(-) diff --git a/Documentation/i2c/i2c-topology b/Documentation/i2c/i2c-topology index 69b008518454..5e40802f0be2 100644 --- a/Documentation/i2c/i2c-topology +++ b/Documentation/i2c/i2c-topology @@ -56,7 +56,7 @@ In drivers/media/ dvb-frontends/m88ds3103 Parent-locked dvb-frontends/rtl2830 Parent-locked dvb-frontends/rtl2832 Parent-locked -dvb-frontends/si2168 Parent-locked +dvb-frontends/si2168 Mux-locked usb/cx231xx/ Parent-locked diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 5583827c386e..108a069fa1ae 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -18,53 +18,23 @@ static const struct dvb_frontend_ops si2168_ops; -/* Own I2C adapter locking is needed because of I2C gate logic. */ -static int si2168_i2c_master_send_unlocked(const struct i2c_client *client, - const char *buf, int count) -{ - int ret; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = count, - .buf = (char *)buf, - }; - - ret = __i2c_transfer(client->adapter, &msg, 1); - return (ret == 1) ? count : ret; -} - -static int si2168_i2c_master_recv_unlocked(const struct i2c_client *client, - char *buf, int count) -{ - int ret; - struct i2c_msg msg = { - .addr = client->addr, - .flags = I2C_M_RD, - .len = count, - .buf = buf, - }; - - ret = __i2c_transfer(client->adapter, &msg, 1); - return (ret == 1) ? count : ret; -} - /* execute firmware command */ -static int si2168_cmd_execute_unlocked(struct i2c_client *client, - struct si2168_cmd *cmd) +static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) { + struct si2168_dev *dev = i2c_get_clientdata(client); int ret; unsigned long timeout; + mutex_lock(&dev->i2c_mutex); + if (cmd->wlen) { /* write cmd and args for firmware */ - ret = si2168_i2c_master_send_unlocked(client, cmd->args, - cmd->wlen); + ret = i2c_master_send(client, cmd->args, cmd->wlen); if (ret < 0) { - goto err; + goto err_mutex_unlock; } else if (ret != cmd->wlen) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } } @@ -73,13 +43,12 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client, #define TIMEOUT 70 timeout = jiffies + msecs_to_jiffies(TIMEOUT); while (!time_after(jiffies, timeout)) { - ret = si2168_i2c_master_recv_unlocked(client, cmd->args, - cmd->rlen); + ret = i2c_master_recv(client, cmd->args, cmd->rlen); if (ret < 0) { - goto err; + goto err_mutex_unlock; } else if (ret != cmd->rlen) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } /* firmware ready? */ @@ -94,32 +63,23 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client, /* error bit set? */ if ((cmd->args[0] >> 6) & 0x01) { ret = -EREMOTEIO; - goto err; + goto err_mutex_unlock; } if (!((cmd->args[0] >> 7) & 0x01)) { ret = -ETIMEDOUT; - goto err; + goto err_mutex_unlock; } } + mutex_unlock(&dev->i2c_mutex); return 0; -err: +err_mutex_unlock: + mutex_unlock(&dev->i2c_mutex); dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) -{ - int ret; - - i2c_lock_adapter(client->adapter); - ret = si2168_cmd_execute_unlocked(client, cmd); - i2c_unlock_adapter(client->adapter); - - return ret; -} - static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct i2c_client *client = fe->demodulator_priv; @@ -610,11 +570,6 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe, return 0; } -/* - * I2C gate logic - * We must use unlocked I2C I/O because I2C adapter lock is already taken - * by the caller (usually tuner driver). - */ static int si2168_select(struct i2c_mux_core *muxc, u32 chan) { struct i2c_client *client = i2c_mux_priv(muxc); @@ -625,7 +580,7 @@ static int si2168_select(struct i2c_mux_core *muxc, u32 chan) memcpy(cmd.args, "\xc0\x0d\x01", 3); cmd.wlen = 3; cmd.rlen = 0; - ret = si2168_cmd_execute_unlocked(client, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -645,7 +600,7 @@ static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan) memcpy(cmd.args, "\xc0\x0d\x00", 3); cmd.wlen = 3; cmd.rlen = 0; - ret = si2168_cmd_execute_unlocked(client, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -708,9 +663,11 @@ static int si2168_probe(struct i2c_client *client, goto err; } + mutex_init(&dev->i2c_mutex); + /* create mux i2c adapter for tuner */ dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, - 1, 0, 0, + 1, 0, I2C_MUX_LOCKED, si2168_select, si2168_deselect); if (!dev->muxc) { ret = -ENOMEM; diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 165bf1412063..8a1f36d2014d 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -29,6 +29,7 @@ /* state struct */ struct si2168_dev { + struct mutex i2c_mutex; struct i2c_mux_core *muxc; struct dvb_frontend fe; enum fe_delivery_system delivery_system; -- cgit From 1cf79db28ef53aeaa66a825e8f788d19fdd8648f Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:33 +0200 Subject: [media] rtl2832: change the i2c gate to be mux-locked The root i2c adapter lock is then no longer held by the i2c mux during accesses behind the i2c gate, and such accesses need to take that lock just like any other ordinary i2c accesses do. So, declare the i2c gate mux-locked, and zap the regmap overrides that makes the i2c accesses unlocked and use plain old regmap accesses. This also removes the need for the regmap wrappers used by rtl2832_sdr, so deconvolute the code further and provide the regmap handle directly instead of the wrapper functions. Tested-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- Documentation/i2c/i2c-topology | 2 +- drivers/media/dvb-frontends/rtl2832.c | 190 ++++-------------------------- drivers/media/dvb-frontends/rtl2832.h | 4 +- drivers/media/dvb-frontends/rtl2832_sdr.c | 13 +- drivers/media/dvb-frontends/rtl2832_sdr.h | 5 +- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 5 +- 6 files changed, 37 insertions(+), 182 deletions(-) diff --git a/Documentation/i2c/i2c-topology b/Documentation/i2c/i2c-topology index 5e40802f0be2..e0aefeece551 100644 --- a/Documentation/i2c/i2c-topology +++ b/Documentation/i2c/i2c-topology @@ -55,7 +55,7 @@ imu/inv_mpu6050/ Mux-locked In drivers/media/ dvb-frontends/m88ds3103 Parent-locked dvb-frontends/rtl2830 Parent-locked -dvb-frontends/rtl2832 Parent-locked +dvb-frontends/rtl2832 Mux-locked dvb-frontends/si2168 Mux-locked usb/cx231xx/ Parent-locked diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 1b23788797b5..957523f07f61 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -153,43 +153,6 @@ static const struct rtl2832_reg_entry registers[] = { [DVBT_REG_4MSEL] = {0x013, 0, 0}, }; -/* Our regmap is bypassing I2C adapter lock, thus we do it! */ -static int rtl2832_bulk_write(struct i2c_client *client, unsigned int reg, - const void *val, size_t val_count) -{ - struct rtl2832_dev *dev = i2c_get_clientdata(client); - int ret; - - i2c_lock_adapter(client->adapter); - ret = regmap_bulk_write(dev->regmap, reg, val, val_count); - i2c_unlock_adapter(client->adapter); - return ret; -} - -static int rtl2832_update_bits(struct i2c_client *client, unsigned int reg, - unsigned int mask, unsigned int val) -{ - struct rtl2832_dev *dev = i2c_get_clientdata(client); - int ret; - - i2c_lock_adapter(client->adapter); - ret = regmap_update_bits(dev->regmap, reg, mask, val); - i2c_unlock_adapter(client->adapter); - return ret; -} - -static int rtl2832_bulk_read(struct i2c_client *client, unsigned int reg, - void *val, size_t val_count) -{ - struct rtl2832_dev *dev = i2c_get_clientdata(client); - int ret; - - i2c_lock_adapter(client->adapter); - ret = regmap_bulk_read(dev->regmap, reg, val, val_count); - i2c_unlock_adapter(client->adapter); - return ret; -} - static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val) { struct i2c_client *client = dev->client; @@ -204,7 +167,7 @@ static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val) len = (msb >> 3) + 1; mask = REG_MASK(msb - lsb); - ret = rtl2832_bulk_read(client, reg_start_addr, reading, len); + ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len); if (ret) goto err; @@ -234,7 +197,7 @@ static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val) len = (msb >> 3) + 1; mask = REG_MASK(msb - lsb); - ret = rtl2832_bulk_read(client, reg_start_addr, reading, len); + ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len); if (ret) goto err; @@ -248,7 +211,7 @@ static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val) for (i = 0; i < len; i++) writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff; - ret = rtl2832_bulk_write(client, reg_start_addr, writing, len); + ret = regmap_bulk_write(dev->regmap, reg_start_addr, writing, len); if (ret) goto err; @@ -525,7 +488,8 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) } for (j = 0; j < sizeof(bw_params[0]); j++) { - ret = rtl2832_bulk_write(client, 0x11c + j, &bw_params[i][j], 1); + ret = regmap_bulk_write(dev->regmap, + 0x11c + j, &bw_params[i][j], 1); if (ret) goto err; } @@ -581,11 +545,11 @@ static int rtl2832_get_frontend(struct dvb_frontend *fe, if (dev->sleeping) return 0; - ret = rtl2832_bulk_read(client, 0x33c, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x33c, buf, 2); if (ret) goto err; - ret = rtl2832_bulk_read(client, 0x351, &buf[2], 1); + ret = regmap_bulk_read(dev->regmap, 0x351, &buf[2], 1); if (ret) goto err; @@ -716,7 +680,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) /* signal strength */ if (dev->fe_status & FE_HAS_SIGNAL) { /* read digital AGC */ - ret = rtl2832_bulk_read(client, 0x305, &u8tmp, 1); + ret = regmap_bulk_read(dev->regmap, 0x305, &u8tmp, 1); if (ret) goto err; @@ -742,7 +706,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) {87659938, 87659938, 87885178, 88241743}, }; - ret = rtl2832_bulk_read(client, 0x33c, &u8tmp, 1); + ret = regmap_bulk_read(dev->regmap, 0x33c, &u8tmp, 1); if (ret) goto err; @@ -754,7 +718,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) if (hierarchy > HIERARCHY_NUM - 1) goto err; - ret = rtl2832_bulk_read(client, 0x40c, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x40c, buf, 2); if (ret) goto err; @@ -775,7 +739,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) /* BER */ if (dev->fe_status & FE_HAS_LOCK) { - ret = rtl2832_bulk_read(client, 0x34e, buf, 2); + ret = regmap_bulk_read(dev->regmap, 0x34e, buf, 2); if (ret) goto err; @@ -825,8 +789,6 @@ static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) /* * I2C gate/mux/repeater logic - * We must use unlocked __i2c_transfer() here (through regmap) because of I2C - * adapter lock is already taken by tuner driver. * There is delay mechanism to avoid unneeded I2C gate open / close. Gate close * is delayed here a little bit in order to see if there is sequence of I2C * messages sent to same I2C bus. @@ -838,7 +800,7 @@ static void rtl2832_i2c_gate_work(struct work_struct *work) int ret; /* close gate */ - ret = rtl2832_update_bits(dev->client, 0x101, 0x08, 0x00); + ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x00); if (ret) goto err; @@ -856,10 +818,7 @@ static int rtl2832_select(struct i2c_mux_core *muxc, u32 chan_id) /* terminate possible gate closing */ cancel_delayed_work(&dev->i2c_gate_work); - /* - * I2C adapter lock is already taken and due to that we will use - * regmap_update_bits() which does not lock again I2C adapter. - */ + /* open gate */ ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08); if (ret) goto err; @@ -931,94 +890,6 @@ static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg) return false; } -/* - * We implement own I2C access routines for regmap in order to get manual access - * to I2C adapter lock, which is needed for I2C mux adapter. - */ -static int rtl2832_regmap_read(void *context, const void *reg_buf, - size_t reg_size, void *val_buf, size_t val_size) -{ - struct i2c_client *client = context; - int ret; - struct i2c_msg msg[2] = { - { - .addr = client->addr, - .flags = 0, - .len = reg_size, - .buf = (u8 *)reg_buf, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = val_size, - .buf = val_buf, - } - }; - - ret = __i2c_transfer(client->adapter, msg, 2); - if (ret != 2) { - dev_warn(&client->dev, "i2c reg read failed %d reg %02x\n", - ret, *(u8 *)reg_buf); - if (ret >= 0) - ret = -EREMOTEIO; - return ret; - } - return 0; -} - -static int rtl2832_regmap_write(void *context, const void *data, size_t count) -{ - struct i2c_client *client = context; - int ret; - struct i2c_msg msg[1] = { - { - .addr = client->addr, - .flags = 0, - .len = count, - .buf = (u8 *)data, - } - }; - - ret = __i2c_transfer(client->adapter, msg, 1); - if (ret != 1) { - dev_warn(&client->dev, "i2c reg write failed %d reg %02x\n", - ret, *(u8 *)data); - if (ret >= 0) - ret = -EREMOTEIO; - return ret; - } - return 0; -} - -static int rtl2832_regmap_gather_write(void *context, const void *reg, - size_t reg_len, const void *val, - size_t val_len) -{ - struct i2c_client *client = context; - int ret; - u8 buf[256]; - struct i2c_msg msg[1] = { - { - .addr = client->addr, - .flags = 0, - .len = 1 + val_len, - .buf = buf, - } - }; - - buf[0] = *(u8 const *)reg; - memcpy(&buf[1], val, val_len); - - ret = __i2c_transfer(client->adapter, msg, 1); - if (ret != 1) { - dev_warn(&client->dev, "i2c reg write failed %d reg %02x\n", - ret, *(u8 const *)reg); - if (ret >= 0) - ret = -EREMOTEIO; - return ret; - } - return 0; -} - /* * FIXME: Hack. Implement own regmap locking in order to silence lockdep * recursive lock warning. That happens when regmap I2C client calls I2C mux @@ -1072,29 +943,29 @@ static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable) ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); if (ret) goto err; - ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2); + ret = regmap_bulk_write(dev->regmap, 0x10c, "\x5f\xff", 2); if (ret) goto err; ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1); if (ret) goto err; - ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1); + ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x18", 1); if (ret) goto err; - ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3); + ret = regmap_bulk_write(dev->regmap, 0x192, "\x7f\xf7\xff", 3); if (ret) goto err; } else { - ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3); + ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3); if (ret) goto err; - ret = rtl2832_bulk_write(client, 0x0bc, "\x08", 1); + ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x08", 1); if (ret) goto err; ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x0); if (ret) goto err; - ret = rtl2832_bulk_write(client, 0x10c, "\x00\x00", 2); + ret = regmap_bulk_write(dev->regmap, 0x10c, "\x00\x00", 2); if (ret) goto err; ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); @@ -1123,7 +994,7 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) else u8tmp = 0x00; - ret = rtl2832_update_bits(client, 0x061, 0xc0, u8tmp); + ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp); if (ret) goto err; @@ -1158,14 +1029,14 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, buf[1] = (dev->filters >> 8) & 0xff; buf[2] = (dev->filters >> 16) & 0xff; buf[3] = (dev->filters >> 24) & 0xff; - ret = rtl2832_bulk_write(client, 0x062, buf, 4); + ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4); if (ret) goto err; /* add PID */ buf[0] = (pid >> 8) & 0xff; buf[1] = (pid >> 0) & 0xff; - ret = rtl2832_bulk_write(client, 0x066 + 2 * index, buf, 2); + ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2); if (ret) goto err; @@ -1183,12 +1054,6 @@ static int rtl2832_probe(struct i2c_client *client, struct rtl2832_dev *dev; int ret; u8 tmp; - static const struct regmap_bus regmap_bus = { - .read = rtl2832_regmap_read, - .write = rtl2832_regmap_write, - .gather_write = rtl2832_regmap_gather_write, - .val_format_endian_default = REGMAP_ENDIAN_NATIVE, - }; static const struct regmap_range_cfg regmap_range_cfg[] = { { .selector_reg = 0x00, @@ -1228,20 +1093,19 @@ static int rtl2832_probe(struct i2c_client *client, dev->regmap_config.ranges = regmap_range_cfg, dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg), dev->regmap_config.cache_type = REGCACHE_NONE, - dev->regmap = regmap_init(&client->dev, ®map_bus, client, - &dev->regmap_config); + dev->regmap = regmap_init_i2c(client, &dev->regmap_config); if (IS_ERR(dev->regmap)) { ret = PTR_ERR(dev->regmap); goto err_kfree; } /* check if the demod is there */ - ret = rtl2832_bulk_read(client, 0x000, &tmp, 1); + ret = regmap_bulk_read(dev->regmap, 0x000, &tmp, 1); if (ret) goto err_regmap_exit; /* create muxed i2c adapter for demod tuner bus */ - dev->muxc = i2c_mux_alloc(i2c, &i2c->dev, 1, 0, 0, + dev->muxc = i2c_mux_alloc(i2c, &i2c->dev, 1, 0, I2C_MUX_LOCKED, rtl2832_select, rtl2832_deselect); if (!dev->muxc) { ret = -ENOMEM; @@ -1262,9 +1126,7 @@ static int rtl2832_probe(struct i2c_client *client, pdata->slave_ts_ctrl = rtl2832_slave_ts_ctrl; pdata->pid_filter = rtl2832_pid_filter; pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl; - pdata->bulk_read = rtl2832_bulk_read; - pdata->bulk_write = rtl2832_bulk_write; - pdata->update_bits = rtl2832_update_bits; + pdata->regmap = dev->regmap; dev_info(&client->dev, "Realtek RTL2832 successfully attached\n"); return 0; diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h index 6390af64cf45..03c0de039fa9 100644 --- a/drivers/media/dvb-frontends/rtl2832.h +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -57,9 +57,7 @@ struct rtl2832_platform_data { int (*pid_filter)(struct dvb_frontend *, u8, u16, int); int (*pid_filter_ctrl)(struct dvb_frontend *, int); /* private: Register access for SDR module use only */ - int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t); - int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t); - int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int); + struct regmap *regmap; }; #endif /* RTL2832_H */ diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index b860f02a4e55..6a6b1debe277 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -35,6 +35,7 @@ #include #include #include +#include static bool rtl2832_sdr_emulated_fmt; module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644); @@ -169,9 +170,9 @@ static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_dev *dev, u16 reg, { struct platform_device *pdev = dev->pdev; struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct i2c_client *client = pdata->i2c_client; + struct regmap *regmap = pdata->regmap; - return pdata->bulk_write(client, reg, val, len); + return regmap_bulk_write(regmap, reg, val, len); } #if 0 @@ -181,9 +182,9 @@ static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_dev *dev, u16 reg, u8 *val, { struct platform_device *pdev = dev->pdev; struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct i2c_client *client = pdata->i2c_client; + struct regmap *regmap = pdata->regmap; - return pdata->bulk_read(client, reg, val, len); + return regmap_bulk_read(regmap, reg, val, len); } #endif @@ -199,9 +200,9 @@ static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_dev *dev, u16 reg, { struct platform_device *pdev = dev->pdev; struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct i2c_client *client = pdata->i2c_client; + struct regmap *regmap = pdata->regmap; - return pdata->update_bits(client, reg, mask, val); + return regmap_update_bits(regmap, reg, mask, val); } /* Private functions */ diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.h b/drivers/media/dvb-frontends/rtl2832_sdr.h index 342ea84860df..d8fc7e7212e3 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.h +++ b/drivers/media/dvb-frontends/rtl2832_sdr.h @@ -56,10 +56,7 @@ struct rtl2832_sdr_platform_data { #define RTL2832_SDR_TUNER_R828D 0x2b u8 tuner; - struct i2c_client *i2c_client; - int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t); - int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t); - int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int); + struct regmap *regmap; struct dvb_frontend *dvb_frontend; struct v4l2_subdev *v4l2_subdev; struct dvb_usb_device *dvb_usb_device; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index fa72642d41f3..eb7af8cb8aca 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1333,10 +1333,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) case TUNER_RTL2832_R828D: pdata.clk = dev->rtl2832_platform_data.clk; pdata.tuner = dev->tuner; - pdata.i2c_client = dev->i2c_client_demod; - pdata.bulk_read = dev->rtl2832_platform_data.bulk_read; - pdata.bulk_write = dev->rtl2832_platform_data.bulk_write; - pdata.update_bits = dev->rtl2832_platform_data.update_bits; + pdata.regmap = dev->rtl2832_platform_data.regmap; pdata.dvb_frontend = adap->fe[0]; pdata.dvb_usb_device = d; pdata.v4l2_subdev = subdev; -- cgit From e01fc42d78ab953f6de4ff9273cb9d6d9ed3dd37 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:34 +0200 Subject: [media] rtl2832_sdr: get rid of empty regmap wrappers Tested-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/media/dvb-frontends/rtl2832_sdr.c | 302 +++++++++++++----------------- 1 file changed, 132 insertions(+), 170 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 6a6b1debe277..47a480a7d46c 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -120,6 +120,7 @@ struct rtl2832_sdr_dev { unsigned long flags; struct platform_device *pdev; + struct regmap *regmap; struct video_device vdev; struct v4l2_device v4l2_dev; @@ -164,47 +165,6 @@ struct rtl2832_sdr_dev { unsigned long jiffies_next; }; -/* write multiple registers */ -static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_dev *dev, u16 reg, - const u8 *val, int len) -{ - struct platform_device *pdev = dev->pdev; - struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct regmap *regmap = pdata->regmap; - - return regmap_bulk_write(regmap, reg, val, len); -} - -#if 0 -/* read multiple registers */ -static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_dev *dev, u16 reg, u8 *val, - int len) -{ - struct platform_device *pdev = dev->pdev; - struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct regmap *regmap = pdata->regmap; - - return regmap_bulk_read(regmap, reg, val, len); -} -#endif - -/* write single register */ -static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_dev *dev, u16 reg, u8 val) -{ - return rtl2832_sdr_wr_regs(dev, reg, &val, 1); -} - -/* write single register with mask */ -static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_dev *dev, u16 reg, - u8 val, u8 mask) -{ - struct platform_device *pdev = dev->pdev; - struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; - struct regmap *regmap = pdata->regmap; - - return regmap_update_bits(regmap, reg, mask, val); -} - /* Private functions */ static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf( struct rtl2832_sdr_dev *dev) @@ -559,11 +519,11 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) f_sr = dev->f_adc; - ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x00\x00", 2); + ret = regmap_bulk_write(dev->regmap, 0x13e, "\x00\x00", 2); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x00\x00\x00\x00", 4); + ret = regmap_bulk_write(dev->regmap, 0x115, "\x00\x00\x00\x00", 4); if (ret) goto err; @@ -589,7 +549,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) buf[1] = (u32tmp >> 8) & 0xff; buf[2] = (u32tmp >> 0) & 0xff; - ret = rtl2832_sdr_wr_regs(dev, 0x119, buf, 3); + ret = regmap_bulk_write(dev->regmap, 0x119, buf, 3); if (ret) goto err; @@ -603,15 +563,15 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) u8tmp2 = 0xcd; /* enable ADC I, ADC Q */ } - ret = rtl2832_sdr_wr_reg(dev, 0x1b1, u8tmp1); + ret = regmap_write(dev->regmap, 0x1b1, u8tmp1); if (ret) goto err; - ret = rtl2832_sdr_wr_reg(dev, 0x008, u8tmp2); + ret = regmap_write(dev->regmap, 0x008, u8tmp2); if (ret) goto err; - ret = rtl2832_sdr_wr_reg(dev, 0x006, 0x80); + ret = regmap_write(dev->regmap, 0x006, 0x80); if (ret) goto err; @@ -622,168 +582,169 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) buf[1] = (u32tmp >> 16) & 0xff; buf[2] = (u32tmp >> 8) & 0xff; buf[3] = (u32tmp >> 0) & 0xff; - ret = rtl2832_sdr_wr_regs(dev, 0x19f, buf, 4); + ret = regmap_bulk_write(dev->regmap, 0x19f, buf, 4); if (ret) goto err; /* low-pass filter */ - ret = rtl2832_sdr_wr_regs(dev, 0x11c, - "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", - 20); + ret = regmap_bulk_write(dev->regmap, 0x11c, + "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", + 20); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2); + ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2); if (ret) goto err; /* mode */ - ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x05", 1); + ret = regmap_write(dev->regmap, 0x019, 0x05); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6); + ret = regmap_bulk_write(dev->regmap, 0x01a, + "\x1b\x16\x0d\x06\x01\xff", 6); if (ret) goto err; /* FSM */ - ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\xf0\x0f", 3); + ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\xf0\x0f", 3); if (ret) goto err; /* PID filter */ - ret = rtl2832_sdr_wr_regs(dev, 0x061, "\x60", 1); + ret = regmap_write(dev->regmap, 0x061, 0x60); if (ret) goto err; /* used RF tuner based settings */ switch (pdata->tuner) { case RTL2832_SDR_TUNER_E4000: - ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x30", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xd0", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x18", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xd4", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x14", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xec", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x83", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x010, "\x49", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x87", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x85", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x013, "\x02", 1); + ret = regmap_write(dev->regmap, 0x112, 0x5a); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x103, 0x5a); + ret = regmap_write(dev->regmap, 0x1c7, 0x30); + ret = regmap_write(dev->regmap, 0x104, 0xd0); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x18); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x80); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x011, 0xd4); + ret = regmap_write(dev->regmap, 0x1e5, 0xf0); + ret = regmap_write(dev->regmap, 0x1d9, 0x00); + ret = regmap_write(dev->regmap, 0x1db, 0x00); + ret = regmap_write(dev->regmap, 0x1dd, 0x14); + ret = regmap_write(dev->regmap, 0x1de, 0xec); + ret = regmap_write(dev->regmap, 0x1d8, 0x0c); + ret = regmap_write(dev->regmap, 0x1e6, 0x02); + ret = regmap_write(dev->regmap, 0x1d7, 0x09); + ret = regmap_write(dev->regmap, 0x00d, 0x83); + ret = regmap_write(dev->regmap, 0x010, 0x49); + ret = regmap_write(dev->regmap, 0x00d, 0x87); + ret = regmap_write(dev->regmap, 0x00d, 0x85); + ret = regmap_write(dev->regmap, 0x013, 0x02); break; case RTL2832_SDR_TUNER_FC0012: case RTL2832_SDR_TUNER_FC0013: - ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xbf", 2); - ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x11", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xef", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1); + ret = regmap_write(dev->regmap, 0x112, 0x5a); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x103, 0x5a); + ret = regmap_write(dev->regmap, 0x1c7, 0x2c); + ret = regmap_write(dev->regmap, 0x104, 0xcc); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x16); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x80); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xbf", 2); + ret = regmap_write(dev->regmap, 0x1e5, 0xf0); + ret = regmap_write(dev->regmap, 0x1d9, 0x00); + ret = regmap_write(dev->regmap, 0x1db, 0x00); + ret = regmap_write(dev->regmap, 0x1dd, 0x11); + ret = regmap_write(dev->regmap, 0x1de, 0xef); + ret = regmap_write(dev->regmap, 0x1d8, 0x0c); + ret = regmap_write(dev->regmap, 0x1e6, 0x02); + ret = regmap_write(dev->regmap, 0x1d7, 0x09); break; case RTL2832_SDR_TUNER_R820T: case RTL2832_SDR_TUNER_R828D: - ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x01", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x24", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x14", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xf4", 1); + ret = regmap_write(dev->regmap, 0x112, 0x5a); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x115, 0x01); + ret = regmap_write(dev->regmap, 0x103, 0x80); + ret = regmap_write(dev->regmap, 0x1c7, 0x24); + ret = regmap_write(dev->regmap, 0x104, 0xcc); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x14); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x80); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x011, 0xf4); break; case RTL2832_SDR_TUNER_FC2580: - ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x39", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x9c", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xf4", 2); + ret = regmap_write(dev->regmap, 0x112, 0x39); + ret = regmap_write(dev->regmap, 0x102, 0x40); + ret = regmap_write(dev->regmap, 0x103, 0x5a); + ret = regmap_write(dev->regmap, 0x1c7, 0x2c); + ret = regmap_write(dev->regmap, 0x104, 0xcc); + ret = regmap_write(dev->regmap, 0x105, 0xbe); + ret = regmap_write(dev->regmap, 0x1c8, 0x16); + ret = regmap_write(dev->regmap, 0x106, 0x35); + ret = regmap_write(dev->regmap, 0x1c9, 0x21); + ret = regmap_write(dev->regmap, 0x1ca, 0x21); + ret = regmap_write(dev->regmap, 0x1cb, 0x00); + ret = regmap_write(dev->regmap, 0x107, 0x40); + ret = regmap_write(dev->regmap, 0x1cd, 0x10); + ret = regmap_write(dev->regmap, 0x1ce, 0x10); + ret = regmap_write(dev->regmap, 0x108, 0x80); + ret = regmap_write(dev->regmap, 0x109, 0x7f); + ret = regmap_write(dev->regmap, 0x10a, 0x9c); + ret = regmap_write(dev->regmap, 0x10b, 0x7f); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_write(dev->regmap, 0x00e, 0xfc); + ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xf4", 2); break; default: dev_notice(&pdev->dev, "Unsupported tuner\n"); } /* software reset */ - ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x04, 0x04); + ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x04); if (ret) goto err; - ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x00, 0x04); + ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x00); if (ret) goto err; err: @@ -798,29 +759,29 @@ static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_dev *dev) dev_dbg(&pdev->dev, "\n"); /* PID filter */ - ret = rtl2832_sdr_wr_regs(dev, 0x061, "\xe0", 1); + ret = regmap_write(dev->regmap, 0x061, 0xe0); if (ret) goto err; /* mode */ - ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x20", 1); + ret = regmap_write(dev->regmap, 0x019, 0x20); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2); + ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2); if (ret) goto err; /* FSM */ - ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\x0f\xff", 3); + ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x40\x00", 2); + ret = regmap_bulk_write(dev->regmap, 0x13e, "\x40\x00", 2); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x06\x3f\xce\xcc", 4); + ret = regmap_bulk_write(dev->regmap, 0x115, "\x06\x3f\xce\xcc", 4); if (ret) goto err; err: @@ -1400,6 +1361,7 @@ static int rtl2832_sdr_probe(struct platform_device *pdev) subdev = pdata->v4l2_subdev; dev->v4l2_subdev = pdata->v4l2_subdev; dev->pdev = pdev; + dev->regmap = pdata->regmap; dev->udev = pdata->dvb_usb_device->udev; dev->f_adc = bands_adc[0].rangelow; dev->f_tuner = bands_fm[0].rangelow; -- cgit From 3f30e40b96b085ff25babaa8d6bc0b75c8fb32b2 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 4 May 2016 22:15:35 +0200 Subject: [media] rtl2832: regmap is aware of lockdep, drop local locking hack Tested-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/media/dvb-frontends/rtl2832.c | 30 ------------------------------ drivers/media/dvb-frontends/rtl2832_priv.h | 1 - 2 files changed, 31 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 957523f07f61..bfb6beedd40b 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -890,32 +890,6 @@ static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg) return false; } -/* - * FIXME: Hack. Implement own regmap locking in order to silence lockdep - * recursive lock warning. That happens when regmap I2C client calls I2C mux - * adapter, which leads demod I2C repeater enable via demod regmap. Operation - * takes two regmap locks recursively - but those are different regmap instances - * in a two different I2C drivers, so it is not deadlock. Proper fix is to make - * regmap aware of lockdep. - */ -static void rtl2832_regmap_lock(void *__dev) -{ - struct rtl2832_dev *dev = __dev; - struct i2c_client *client = dev->client; - - dev_dbg(&client->dev, "\n"); - mutex_lock(&dev->regmap_mutex); -} - -static void rtl2832_regmap_unlock(void *__dev) -{ - struct rtl2832_dev *dev = __dev; - struct i2c_client *client = dev->client; - - dev_dbg(&client->dev, "\n"); - mutex_unlock(&dev->regmap_mutex); -} - static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client) { struct rtl2832_dev *dev = i2c_get_clientdata(client); @@ -1082,12 +1056,8 @@ static int rtl2832_probe(struct i2c_client *client, dev->sleeping = true; INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work); /* create regmap */ - mutex_init(&dev->regmap_mutex); dev->regmap_config.reg_bits = 8, dev->regmap_config.val_bits = 8, - dev->regmap_config.lock = rtl2832_regmap_lock, - dev->regmap_config.unlock = rtl2832_regmap_unlock, - dev->regmap_config.lock_arg = dev, dev->regmap_config.volatile_reg = rtl2832_volatile_reg, dev->regmap_config.max_register = 5 * 0x100, dev->regmap_config.ranges = regmap_range_cfg, diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index d8f97d14f6fd..c1a8a69e9015 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -33,7 +33,6 @@ struct rtl2832_dev { struct rtl2832_platform_data *pdata; struct i2c_client *client; - struct mutex regmap_mutex; struct regmap_config regmap_config; struct regmap *regmap; struct i2c_mux_core *muxc; -- cgit From 21e9efd92bb5e205b6243166b0fd38d3a5be8235 Mon Sep 17 00:00:00 2001 From: Shardar Shariff Md Date: Mon, 25 Apr 2016 19:08:36 +0530 Subject: i2c: tegra: disable clock before returning error Disable clock before returning error in tegra_i2c_init() as its leaves i2c clock ON in case of error and never turns off again as it will have unbalanced clock enable/disable Signed-off-by: Shardar Shariff Md Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index d764d64e9d2c..445398c314a3 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -483,19 +483,20 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) if (time_after(jiffies, timeout)) { dev_warn(i2c_dev->dev, "timeout waiting for config load\n"); - return -ETIMEDOUT; + err = -ETIMEDOUT; + goto err; } msleep(1); } } - tegra_i2c_clock_disable(i2c_dev); - if (i2c_dev->irq_disabled) { i2c_dev->irq_disabled = 0; enable_irq(i2c_dev->irq); } +err: + tegra_i2c_clock_disable(i2c_dev); return err; } -- cgit From ef51d3ff0da445a2de8f836f0e81881465a18200 Mon Sep 17 00:00:00 2001 From: Michele Curti Date: Wed, 27 Apr 2016 21:37:51 +0200 Subject: i2c: algo-bit: declare i2c_bit_quirk_no_clk_stretch as static i2c_bit_quirk_no_clk_stretch is used in i2c-algo-bit.c only, so declare it as static. Signed-off-by: Michele Curti Signed-off-by: Wolfram Sang --- drivers/i2c/algos/i2c-algo-bit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 9d233bbde5e1..a8e89df665b9 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -617,7 +617,7 @@ const struct i2c_algorithm i2c_bit_algo = { }; EXPORT_SYMBOL(i2c_bit_algo); -const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = { +static const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = { .flags = I2C_AQ_NO_CLK_STRETCH, }; -- cgit From 3a9ddaf4dca87f86f70b88d7710da20abfc9aca1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 12 May 2016 11:25:19 +0200 Subject: i2c: only check scl functions when using generic recovery A custom recovery function doesn't need these pointers to be populated because it may work differently internally. Signed-off-by: Wolfram Sang Tested-by: Peter Griffin --- drivers/i2c/i2c-core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 9da446162529..af11b658984d 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1594,10 +1594,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap) bri->get_scl = get_scl_gpio_value; bri->set_scl = set_scl_gpio_value; - } else if (!bri->set_scl || !bri->get_scl) { + } else if (bri->recover_bus == i2c_generic_scl_recovery) { /* Generic SCL recovery */ - dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n"); - adap->bus_recovery_info = NULL; + if (!bri->set_scl || !bri->get_scl) { + dev_err(&adap->dev, "No {get|set}_scl() found, not using recovery\n"); + adap->bus_recovery_info = NULL; + } } } -- cgit From 40027b2fffc2dcef20080847b0913d210ef3ced0 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 11 May 2016 17:21:59 +0100 Subject: i2c: st: Implement bus clear >From I2C specifications: http://www.nxp.com/documents/user_manual/UM10204.pdf Chapter 3.1.16, when the i2c device held the SDA line low, the master should send 9 clocks pulses to try to recover. Signed-off-by: Frederic Pillon Signed-off-by: Peter Griffin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-st.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index b3c182fea165..944ec4205084 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -337,10 +337,42 @@ static void st_i2c_hw_config(struct st_i2c_dev *i2c_dev) writel_relaxed(val, i2c_dev->base + SSC_NOISE_SUPP_WIDTH_DATAOUT); } +static int st_i2c_recover_bus(struct i2c_adapter *i2c_adap) +{ + struct st_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + u32 ctl; + + dev_dbg(i2c_dev->dev, "Trying to recover bus\n"); + + /* + * SSP IP is dual role SPI/I2C to generate 9 clock pulses + * we switch to SPI node, 9 bit words and write a 0. This + * has been validate with a oscilloscope and is easier + * than switching to GPIO mode. + */ + + /* Disable interrupts */ + writel_relaxed(0, i2c_dev->base + SSC_IEN); + + st_i2c_hw_config(i2c_dev); + + ctl = SSC_CTL_EN | SSC_CTL_MS | SSC_CTL_EN_RX_FIFO | SSC_CTL_EN_TX_FIFO; + st_i2c_set_bits(i2c_dev->base + SSC_CTL, ctl); + + st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_I2CM); + usleep_range(8000, 10000); + + writel_relaxed(0, i2c_dev->base + SSC_TBUF); + usleep_range(2000, 4000); + st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_I2CM); + + return 0; +} + static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev) { u32 sta; - int i; + int i, ret; for (i = 0; i < 10; i++) { sta = readl_relaxed(i2c_dev->base + SSC_STA); @@ -352,6 +384,12 @@ static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev) dev_err(i2c_dev->dev, "bus not free (status = 0x%08x)\n", sta); + ret = i2c_recover_bus(&i2c_dev->adap); + if (ret) { + dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret); + return ret; + } + return -EBUSY; } @@ -743,6 +781,10 @@ static struct i2c_algorithm st_i2c_algo = { .functionality = st_i2c_func, }; +static struct i2c_bus_recovery_info st_i2c_recovery_info = { + .recover_bus = st_i2c_recover_bus, +}; + static int st_i2c_of_get_deglitch(struct device_node *np, struct st_i2c_dev *i2c_dev) { @@ -825,6 +867,7 @@ static int st_i2c_probe(struct platform_device *pdev) adap->timeout = 2 * HZ; adap->retries = 0; adap->algo = &st_i2c_algo; + adap->bus_recovery_info = &st_i2c_recovery_info; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; -- cgit From 73e8b0528346e88a0624f2d9821f382cd6256677 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Sat, 14 May 2016 14:17:08 +0200 Subject: i2c: rcar: add DMA support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it possible to transfer i2c message buffers via DMA. Start/Stop/Sending_Slave_Address and some data is still handled using the old state machine, it is sending the bulk of the data that is done via DMA. The first byte of a transmission and the last two bytes of reception are sent/received using PIO. This is needed for the HW to have access to the first byte before DMA transmit and to be able to set the STOP condition for DMA reception. Signed-off-by: Niklas Söderlund Acked-by: Rob Herring Tested-by: Wolfram Sang [wsa: fixed a checkpatch warning] Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c-rcar.txt | 3 + drivers/i2c/busses/i2c-rcar.c | 233 ++++++++++++++++++++- 2 files changed, 232 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-rcar.txt b/Documentation/devicetree/bindings/i2c/i2c-rcar.txt index cf8bfc956cdc..5f0cb502b1db 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rcar.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-rcar.txt @@ -19,6 +19,9 @@ Optional properties: - clock-frequency: desired I2C bus clock frequency in Hz. The absence of this property indicates the default frequency 100 kHz. - clocks: clock specifier. +- dmas: Must contain a list of two references to DMA specifiers, one for + transmission, and one for reception. +- dma-names: Must contain a list of two DMA names, "tx" and "rx". - i2c-scl-falling-time-ns: see i2c.txt - i2c-scl-internal-delay-ns: see i2c.txt diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 68ecb5630ad5..9aca1b4e2d8d 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -21,6 +21,8 @@ */ #include #include +#include +#include #include #include #include @@ -43,6 +45,8 @@ #define ICSAR 0x1C /* slave address */ #define ICMAR 0x20 /* master address */ #define ICRXTX 0x24 /* data port */ +#define ICDMAER 0x3c /* DMA enable */ +#define ICFBSCR 0x38 /* first bit setup cycle */ /* ICSCR */ #define SDBS (1 << 3) /* slave data buffer select */ @@ -78,6 +82,16 @@ #define MDR (1 << 1) #define MAT (1 << 0) /* slave addr xfer done */ +/* ICDMAER */ +#define RSDMAE (1 << 3) /* DMA Slave Received Enable */ +#define TSDMAE (1 << 2) /* DMA Slave Transmitted Enable */ +#define RMDMAE (1 << 1) /* DMA Master Received Enable */ +#define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */ + +/* ICFBSCR */ +#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */ +#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */ + #define RCAR_BUS_PHASE_START (MDBS | MIE | ESG) #define RCAR_BUS_PHASE_DATA (MDBS | MIE) @@ -120,6 +134,12 @@ struct rcar_i2c_priv { u32 flags; enum rcar_i2c_type devtype; struct i2c_client *slave; + + struct resource *res; + struct dma_chan *dma_tx; + struct dma_chan *dma_rx; + struct scatterlist sg; + enum dma_data_direction dma_direction; }; #define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent) @@ -287,6 +307,118 @@ static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv) /* * interrupt functions */ +static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) +{ + struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE + ? priv->dma_rx : priv->dma_tx; + + /* Disable DMA Master Received/Transmitted */ + rcar_i2c_write(priv, ICDMAER, 0); + + /* Reset default delay */ + rcar_i2c_write(priv, ICFBSCR, TCYC06); + + dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg), + priv->msg->len, priv->dma_direction); + + priv->dma_direction = DMA_NONE; +} + +static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv) +{ + if (priv->dma_direction == DMA_NONE) + return; + else if (priv->dma_direction == DMA_FROM_DEVICE) + dmaengine_terminate_all(priv->dma_rx); + else if (priv->dma_direction == DMA_TO_DEVICE) + dmaengine_terminate_all(priv->dma_tx); + + rcar_i2c_dma_unmap(priv); +} + +static void rcar_i2c_dma_callback(void *data) +{ + struct rcar_i2c_priv *priv = data; + + priv->pos += sg_dma_len(&priv->sg); + + rcar_i2c_dma_unmap(priv); +} + +static void rcar_i2c_dma(struct rcar_i2c_priv *priv) +{ + struct device *dev = rcar_i2c_priv_to_dev(priv); + struct i2c_msg *msg = priv->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 ? priv->dma_rx : priv->dma_tx; + struct dma_async_tx_descriptor *txdesc; + dma_addr_t dma_addr; + dma_cookie_t cookie; + unsigned char *buf; + int len; + + /* Do not use DMA if it's not available or for messages < 8 bytes */ + if (IS_ERR(chan) || msg->len < 8) + return; + + if (read) { + /* + * The last two bytes needs to be fetched using PIO in + * order for the STOP phase to work. + */ + buf = priv->msg->buf; + len = priv->msg->len - 2; + } else { + /* + * First byte in message was sent using PIO. + */ + buf = priv->msg->buf + 1; + len = priv->msg->len - 1; + } + + dma_addr = dma_map_single(chan->device->dev, buf, len, dir); + if (dma_mapping_error(dev, dma_addr)) { + dev_dbg(dev, "dma map failed, using PIO\n"); + return; + } + + sg_dma_len(&priv->sg) = len; + sg_dma_address(&priv->sg) = dma_addr; + + priv->dma_direction = dir; + + txdesc = dmaengine_prep_slave_sg(chan, &priv->sg, 1, + read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) { + dev_dbg(dev, "dma prep slave sg failed, using PIO\n"); + rcar_i2c_cleanup_dma(priv); + return; + } + + txdesc->callback = rcar_i2c_dma_callback; + txdesc->callback_param = priv; + + cookie = dmaengine_submit(txdesc); + if (dma_submit_error(cookie)) { + dev_dbg(dev, "submitting dma failed, using PIO\n"); + rcar_i2c_cleanup_dma(priv); + return; + } + + /* Set delay for DMA operations */ + rcar_i2c_write(priv, ICFBSCR, TCYC17); + + /* Enable DMA Master Received/Transmitted */ + if (read) + rcar_i2c_write(priv, ICDMAER, RMDMAE); + else + rcar_i2c_write(priv, ICDMAER, TMDMAE); + + dma_async_issue_pending(chan); +} + static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) { struct i2c_msg *msg = priv->msg; @@ -306,6 +438,12 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]); priv->pos++; + /* + * Try to use DMA to transmit the rest of the data if + * address transfer pashe just finished. + */ + if (msr & MAT) + rcar_i2c_dma(priv); } else { /* * The last data was pushed to ICRXTX on _PREV_ empty irq. @@ -340,7 +478,11 @@ static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr) return; if (msr & MAT) { - /* Address transfer phase finished, but no data at this point. */ + /* + * Address transfer phase finished, but no data at this point. + * Try to use DMA to receive data. + */ + rcar_i2c_dma(priv); } else if (priv->pos < msg->len) { /* get received data */ msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX); @@ -472,6 +614,81 @@ out: return IRQ_HANDLED; } +static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev, + enum dma_transfer_direction dir, + dma_addr_t port_addr) +{ + struct dma_chan *chan; + struct dma_slave_config cfg; + char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; + int ret; + + chan = dma_request_slave_channel_reason(dev, chan_name); + if (IS_ERR(chan)) { + ret = PTR_ERR(chan); + dev_dbg(dev, "request_channel failed for %s (%d)\n", + chan_name, ret); + return chan; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = port_addr; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } else { + cfg.src_addr = port_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_dbg(dev, "slave_config failed for %s (%d)\n", + chan_name, ret); + dma_release_channel(chan); + return ERR_PTR(ret); + } + + dev_dbg(dev, "got DMA channel for %s\n", chan_name); + return chan; +} + +static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv, + struct i2c_msg *msg) +{ + struct device *dev = rcar_i2c_priv_to_dev(priv); + bool read; + struct dma_chan *chan; + enum dma_transfer_direction dir; + + read = msg->flags & I2C_M_RD; + + chan = read ? priv->dma_rx : priv->dma_tx; + if (PTR_ERR(chan) != -EPROBE_DEFER) + return; + + dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + chan = rcar_i2c_request_dma_chan(dev, dir, priv->res->start + ICRXTX); + + if (read) + priv->dma_rx = chan; + else + priv->dma_tx = chan; +} + +static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv) +{ + if (!IS_ERR(priv->dma_tx)) { + dma_release_channel(priv->dma_tx); + priv->dma_tx = ERR_PTR(-EPROBE_DEFER); + } + + if (!IS_ERR(priv->dma_rx)) { + dma_release_channel(priv->dma_rx); + priv->dma_rx = ERR_PTR(-EPROBE_DEFER); + } +} + static int rcar_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) @@ -493,6 +710,7 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; goto out; } + rcar_i2c_request_dma(priv, msgs + i); } /* init first message */ @@ -504,6 +722,7 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE, num * adap->timeout); if (!time_left) { + rcar_i2c_cleanup_dma(priv); rcar_i2c_init(priv); ret = -ETIMEDOUT; } else if (priv->flags & ID_NACK) { @@ -591,7 +810,6 @@ static int rcar_i2c_probe(struct platform_device *pdev) { struct rcar_i2c_priv *priv; struct i2c_adapter *adap; - struct resource *res; struct device *dev = &pdev->dev; struct i2c_timings i2c_t; int irq, ret; @@ -606,8 +824,9 @@ static int rcar_i2c_probe(struct platform_device *pdev) return PTR_ERR(priv->clk); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->io = devm_ioremap_resource(dev, res); + priv->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + priv->io = devm_ioremap_resource(dev, priv->res); if (IS_ERR(priv->io)) return PTR_ERR(priv->io); @@ -626,6 +845,11 @@ static int rcar_i2c_probe(struct platform_device *pdev) i2c_parse_fw_timings(dev, &i2c_t, false); + /* Init DMA */ + sg_init_table(&priv->sg, 1); + priv->dma_direction = DMA_NONE; + priv->dma_rx = priv->dma_tx = ERR_PTR(-EPROBE_DEFER); + pm_runtime_enable(dev); pm_runtime_get_sync(dev); ret = rcar_i2c_clock_calculate(priv, &i2c_t); @@ -673,6 +897,7 @@ static int rcar_i2c_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; i2c_del_adapter(&priv->adap); + rcar_i2c_release_dma(priv); if (priv->flags & ID_P_PM_BLOCKED) pm_runtime_put(dev); pm_runtime_disable(dev); -- cgit