From 47d506d1a28fd10a9fb1f33df5622d88fae72095 Mon Sep 17 00:00:00 2001 From: Tali Perry Date: Wed, 25 May 2022 11:23:38 +0800 Subject: i2c: npcm: Remove own slave addresses 2:10 NPCM can support up to 10 own slave addresses. In practice, only one address is actually being used. In order to access addresses 2 and above, need to switch register banks. The switch needs spinlock. To avoid using spinlock for this useless feature removed support of SA >= 2. Also fix returned slave event enum. Remove some comment since the bank selection is not required. The bank selection is not required since the supported slave addresses are reduced. Fixes: 56a1485b102e ("i2c: npcm7xx: Add Nuvoton NPCM I2C controller driver") Signed-off-by: Tali Perry Signed-off-by: Tyrone Ting Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-npcm7xx.c | 41 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 25 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 5960ccde6574..b5cc83e51029 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -123,11 +123,11 @@ enum i2c_addr { * Since the addr regs are sprinkled all over the address space, * use this array to get the address or each register. */ -#define I2C_NUM_OWN_ADDR 10 +#define I2C_NUM_OWN_ADDR 2 +#define I2C_NUM_OWN_ADDR_SUPPORTED 2 + static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = { - NPCM_I2CADDR1, NPCM_I2CADDR2, NPCM_I2CADDR3, NPCM_I2CADDR4, - NPCM_I2CADDR5, NPCM_I2CADDR6, NPCM_I2CADDR7, NPCM_I2CADDR8, - NPCM_I2CADDR9, NPCM_I2CADDR10, + NPCM_I2CADDR1, NPCM_I2CADDR2, }; #endif @@ -392,14 +392,10 @@ static void npcm_i2c_disable(struct npcm_i2c *bus) #if IS_ENABLED(CONFIG_I2C_SLAVE) int i; - /* select bank 0 for I2C addresses */ - npcm_i2c_select_bank(bus, I2C_BANK_0); - /* Slave addresses removal */ - for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR; i++) + for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR_SUPPORTED; i++) iowrite8(0, bus->reg + npcm_i2caddr[i]); - npcm_i2c_select_bank(bus, I2C_BANK_1); #endif /* Disable module */ i2cctl2 = ioread8(bus->reg + NPCM_I2CCTL2); @@ -604,8 +600,7 @@ static int npcm_i2c_slave_enable(struct npcm_i2c *bus, enum i2c_addr addr_type, i2cctl1 &= ~NPCM_I2CCTL1_GCMEN; iowrite8(i2cctl1, bus->reg + NPCM_I2CCTL1); return 0; - } - if (addr_type == I2C_ARP_ADDR) { + } else if (addr_type == I2C_ARP_ADDR) { i2cctl3 = ioread8(bus->reg + NPCM_I2CCTL3); if (enable) i2cctl3 |= I2CCTL3_ARPMEN; @@ -614,16 +609,16 @@ static int npcm_i2c_slave_enable(struct npcm_i2c *bus, enum i2c_addr addr_type, iowrite8(i2cctl3, bus->reg + NPCM_I2CCTL3); return 0; } + if (addr_type > I2C_SLAVE_ADDR2 && addr_type <= I2C_SLAVE_ADDR10) + dev_err(bus->dev, "try to enable more than 2 SA not supported\n"); + if (addr_type >= I2C_ARP_ADDR) return -EFAULT; - /* select bank 0 for address 3 to 10 */ - if (addr_type > I2C_SLAVE_ADDR2) - npcm_i2c_select_bank(bus, I2C_BANK_0); + /* Set and enable the address */ iowrite8(sa_reg, bus->reg + npcm_i2caddr[addr_type]); npcm_i2c_slave_int_enable(bus, enable); - if (addr_type > I2C_SLAVE_ADDR2) - npcm_i2c_select_bank(bus, I2C_BANK_1); + return 0; } #endif @@ -846,15 +841,11 @@ static u8 npcm_i2c_get_slave_addr(struct npcm_i2c *bus, enum i2c_addr addr_type) { u8 slave_add; - /* select bank 0 for address 3 to 10 */ - if (addr_type > I2C_SLAVE_ADDR2) - npcm_i2c_select_bank(bus, I2C_BANK_0); + if (addr_type > I2C_SLAVE_ADDR2 && addr_type <= I2C_SLAVE_ADDR10) + dev_err(bus->dev, "get slave: try to use more than 2 SA not supported\n"); slave_add = ioread8(bus->reg + npcm_i2caddr[(int)addr_type]); - if (addr_type > I2C_SLAVE_ADDR2) - npcm_i2c_select_bank(bus, I2C_BANK_1); - return slave_add; } @@ -864,12 +855,12 @@ static int npcm_i2c_remove_slave_addr(struct npcm_i2c *bus, u8 slave_add) /* Set the enable bit */ slave_add |= 0x80; - npcm_i2c_select_bank(bus, I2C_BANK_0); - for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR; i++) { + + for (i = I2C_SLAVE_ADDR1; i < I2C_NUM_OWN_ADDR_SUPPORTED; i++) { if (ioread8(bus->reg + npcm_i2caddr[i]) == slave_add) iowrite8(0, bus->reg + npcm_i2caddr[i]); } - npcm_i2c_select_bank(bus, I2C_BANK_1); + return 0; } -- cgit v1.2.3 From d7aa1b149b8fc04d802879cf4662010aa4a42deb Mon Sep 17 00:00:00 2001 From: Tali Perry Date: Wed, 25 May 2022 11:23:39 +0800 Subject: i2c: npcm: Correct slave role behavior Correct the slave transaction logic to be compatible with the generic slave backend driver. Fixes: 56a1485b102e ("i2c: npcm7xx: Add Nuvoton NPCM I2C controller driver") Signed-off-by: Tali Perry Signed-off-by: Tyrone Ting Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-npcm7xx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index b5cc83e51029..b4d218c6c8fb 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -915,11 +915,15 @@ static int npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus) for (i = 0; i < I2C_HW_FIFO_SIZE; i++) { if (bus->slv_wr_size >= I2C_HW_FIFO_SIZE) break; - i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value); + if (bus->state == I2C_SLAVE_MATCH) { + i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value); + bus->state = I2C_OPER_STARTED; + } else { + i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value); + } ind = (bus->slv_wr_ind + bus->slv_wr_size) % I2C_HW_FIFO_SIZE; bus->slv_wr_buf[ind] = value; bus->slv_wr_size++; - i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value); } return I2C_HW_FIFO_SIZE - ret; } @@ -967,7 +971,6 @@ static void npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite, if (nwrite == 0) return; - bus->state = I2C_OPER_STARTED; bus->operation = I2C_WRITE_OPER; /* get the next buffer */ -- cgit v1.2.3 From bbc38ed53a02a759d8e5c01e834eca49304a2315 Mon Sep 17 00:00:00 2001 From: Tyrone Ting Date: Wed, 25 May 2022 11:23:40 +0800 Subject: i2c: npcm: Support NPCM845 Add NPCM8XX I2C support. The NPCM8XX uses a similar i2c module as NPCM7XX. The internal HW FIFO is larger in NPCM8XX. Signed-off-by: Tyrone Ting Acked-by: Tomer Maimon Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 8 +-- drivers/i2c/busses/Makefile | 2 +- drivers/i2c/busses/i2c-npcm7xx.c | 114 ++++++++++++++++++++++++--------------- 3 files changed, 76 insertions(+), 48 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index a1bae59208e3..b1d7069dd377 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -838,13 +838,13 @@ config I2C_NOMADIK I2C interface from ST-Ericsson's Nomadik and Ux500 architectures, as well as the STA2X11 PCIe I/O HUB. -config I2C_NPCM7XX +config I2C_NPCM tristate "Nuvoton I2C Controller" - depends on ARCH_NPCM7XX || COMPILE_TEST + depends on ARCH_NPCM || COMPILE_TEST help If you say yes to this option, support will be included for the - Nuvoton I2C controller, which is available on the NPCM7xx BMC - controller. + Nuvoton I2C controller, which is available on the NPCM BMC + controllers. Driver can also support slave mode (select I2C_SLAVE). config I2C_OCORES diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 479f60e4ee3d..b0a10e5d9ee9 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -84,7 +84,7 @@ obj-$(CONFIG_I2C_MT7621) += i2c-mt7621.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MXS) += i2c-mxs.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o -obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o +obj-$(CONFIG_I2C_NPCM) += i2c-npcm7xx.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_OWL) += i2c-owl.o diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index b4d218c6c8fb..86fd942ea648 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -91,7 +92,6 @@ enum i2c_addr { /* init register and default value required to enable module */ #define NPCM_I2CSEGCTL 0xE4 -#define NPCM_I2CSEGCTL_INIT_VAL 0x0333F000 /* Common regs */ #define NPCM_I2CSDA 0x00 @@ -226,8 +226,7 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = { #define NPCM_I2CFIF_CTS_CLR_FIFO BIT(6) #define NPCM_I2CFIF_CTS_SLVRSTR BIT(7) -/* NPCM_I2CTXF_CTL reg fields */ -#define NPCM_I2CTXF_CTL_TX_THR GENMASK(4, 0) +/* NPCM_I2CTXF_CTL reg field */ #define NPCM_I2CTXF_CTL_THR_TXIE BIT(6) /* NPCM_I2CT_OUT reg fields */ @@ -236,22 +235,18 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = { #define NPCM_I2CT_OUT_T_OUTST BIT(7) /* NPCM_I2CTXF_STS reg fields */ -#define NPCM_I2CTXF_STS_TX_BYTES GENMASK(4, 0) #define NPCM_I2CTXF_STS_TX_THST BIT(6) /* NPCM_I2CRXF_STS reg fields */ -#define NPCM_I2CRXF_STS_RX_BYTES GENMASK(4, 0) #define NPCM_I2CRXF_STS_RX_THST BIT(6) /* NPCM_I2CFIF_CTL reg fields */ #define NPCM_I2CFIF_CTL_FIFO_EN BIT(4) /* NPCM_I2CRXF_CTL reg fields */ -#define NPCM_I2CRXF_CTL_RX_THR GENMASK(4, 0) -#define NPCM_I2CRXF_CTL_LAST_PEC BIT(5) #define NPCM_I2CRXF_CTL_THR_RXIE BIT(6) -#define I2C_HW_FIFO_SIZE 16 +#define MAX_I2C_HW_FIFO_SIZE 32 /* I2C_VER reg fields */ #define I2C_VER_VERSION GENMASK(6, 0) @@ -268,11 +263,36 @@ static const int npcm_i2caddr[I2C_NUM_OWN_ADDR] = { #define I2C_FREQ_MIN_HZ 10000 #define I2C_FREQ_MAX_HZ I2C_MAX_FAST_MODE_PLUS_FREQ +struct npcm_i2c_data { + u8 fifo_size; + u32 segctl_init_val; + u8 txf_sts_tx_bytes; + u8 rxf_sts_rx_bytes; + u8 rxf_ctl_last_pec; +}; + +static const struct npcm_i2c_data npxm7xx_i2c_data = { + .fifo_size = 16, + .segctl_init_val = 0x0333F000, + .txf_sts_tx_bytes = GENMASK(4, 0), + .rxf_sts_rx_bytes = GENMASK(4, 0), + .rxf_ctl_last_pec = BIT(5), +}; + +static const struct npcm_i2c_data npxm8xx_i2c_data = { + .fifo_size = 32, + .segctl_init_val = 0x9333F000, + .txf_sts_tx_bytes = GENMASK(5, 0), + .rxf_sts_rx_bytes = GENMASK(5, 0), + .rxf_ctl_last_pec = BIT(7), +}; + /* Status of one I2C module */ struct npcm_i2c { struct i2c_adapter adap; struct device *dev; unsigned char __iomem *reg; + const struct npcm_i2c_data *data; spinlock_t lock; /* IRQ synchronization */ struct completion cmd_complete; int cmd_err; @@ -305,8 +325,8 @@ struct npcm_i2c { int slv_rd_ind; int slv_wr_size; int slv_wr_ind; - u8 slv_rd_buf[I2C_HW_FIFO_SIZE]; - u8 slv_wr_buf[I2C_HW_FIFO_SIZE]; + u8 slv_rd_buf[MAX_I2C_HW_FIFO_SIZE]; + u8 slv_wr_buf[MAX_I2C_HW_FIFO_SIZE]; #endif struct dentry *debugfs; /* debugfs device directory */ u64 ber_cnt; @@ -439,7 +459,7 @@ static inline bool npcm_i2c_tx_fifo_empty(struct npcm_i2c *bus) tx_fifo_sts = ioread8(bus->reg + NPCM_I2CTXF_STS); /* check if TX FIFO is not empty */ - if ((tx_fifo_sts & NPCM_I2CTXF_STS_TX_BYTES) == 0) + if ((tx_fifo_sts & bus->data->txf_sts_tx_bytes) == 0) return false; /* check if TX FIFO status bit is set: */ @@ -452,7 +472,7 @@ static inline bool npcm_i2c_rx_fifo_full(struct npcm_i2c *bus) rx_fifo_sts = ioread8(bus->reg + NPCM_I2CRXF_STS); /* check if RX FIFO is not empty: */ - if ((rx_fifo_sts & NPCM_I2CRXF_STS_RX_BYTES) == 0) + if ((rx_fifo_sts & bus->data->rxf_sts_rx_bytes) == 0) return false; /* check if rx fifo full status is set: */ @@ -739,11 +759,11 @@ static void npcm_i2c_callback(struct npcm_i2c *bus, static u8 npcm_i2c_fifo_usage(struct npcm_i2c *bus) { if (bus->operation == I2C_WRITE_OPER) - return FIELD_GET(NPCM_I2CTXF_STS_TX_BYTES, - ioread8(bus->reg + NPCM_I2CTXF_STS)); + return (bus->data->txf_sts_tx_bytes & + ioread8(bus->reg + NPCM_I2CTXF_STS)); if (bus->operation == I2C_READ_OPER) - return FIELD_GET(NPCM_I2CRXF_STS_RX_BYTES, - ioread8(bus->reg + NPCM_I2CRXF_STS)); + return (bus->data->rxf_sts_rx_bytes & + ioread8(bus->reg + NPCM_I2CRXF_STS)); return 0; } @@ -755,13 +775,13 @@ static void npcm_i2c_write_to_fifo_master(struct npcm_i2c *bus, u16 max_bytes) * Fill the FIFO, while the FIFO is not full and there are more bytes * to write */ - size_free_fifo = I2C_HW_FIFO_SIZE - npcm_i2c_fifo_usage(bus); + size_free_fifo = bus->data->fifo_size - npcm_i2c_fifo_usage(bus); while (max_bytes-- && size_free_fifo) { if (bus->wr_ind < bus->wr_size) npcm_i2c_wr_byte(bus, bus->wr_buf[bus->wr_ind++]); else npcm_i2c_wr_byte(bus, 0xFF); - size_free_fifo = I2C_HW_FIFO_SIZE - npcm_i2c_fifo_usage(bus); + size_free_fifo = bus->data->fifo_size - npcm_i2c_fifo_usage(bus); } } @@ -782,11 +802,11 @@ static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite) /* configure RX FIFO */ if (nread > 0) { - rxf_ctl = min_t(int, nread, I2C_HW_FIFO_SIZE); + rxf_ctl = min_t(int, nread, bus->data->fifo_size); /* set LAST bit. if LAST is set next FIFO packet is nacked */ - if (nread <= I2C_HW_FIFO_SIZE) - rxf_ctl |= NPCM_I2CRXF_CTL_LAST_PEC; + if (nread <= bus->data->fifo_size) + rxf_ctl |= bus->data->rxf_ctl_last_pec; /* * if we are about to read the first byte in blk rd mode, @@ -804,9 +824,9 @@ static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite) /* configure TX FIFO */ if (nwrite > 0) { - if (nwrite > I2C_HW_FIFO_SIZE) + if (nwrite > bus->data->fifo_size) /* data to send is more then FIFO size. */ - iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CTXF_CTL); + iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CTXF_CTL); else iowrite8(nwrite, bus->reg + NPCM_I2CTXF_CTL); @@ -873,13 +893,13 @@ static void npcm_i2c_write_fifo_slave(struct npcm_i2c *bus, u16 max_bytes) npcm_i2c_clear_fifo_int(bus); npcm_i2c_clear_tx_fifo(bus); iowrite8(0, bus->reg + NPCM_I2CTXF_CTL); - while (max_bytes-- && I2C_HW_FIFO_SIZE != npcm_i2c_fifo_usage(bus)) { + while (max_bytes-- && bus->data->fifo_size != npcm_i2c_fifo_usage(bus)) { if (bus->slv_wr_size <= 0) break; - bus->slv_wr_ind = bus->slv_wr_ind % I2C_HW_FIFO_SIZE; + bus->slv_wr_ind = bus->slv_wr_ind & (bus->data->fifo_size - 1); npcm_i2c_wr_byte(bus, bus->slv_wr_buf[bus->slv_wr_ind]); bus->slv_wr_ind++; - bus->slv_wr_ind = bus->slv_wr_ind % I2C_HW_FIFO_SIZE; + bus->slv_wr_ind = bus->slv_wr_ind & (bus->data->fifo_size - 1); bus->slv_wr_size--; } } @@ -894,7 +914,7 @@ static void npcm_i2c_read_fifo_slave(struct npcm_i2c *bus, u8 bytes_in_fifo) while (bytes_in_fifo--) { data = npcm_i2c_rd_byte(bus); - bus->slv_rd_ind = bus->slv_rd_ind % I2C_HW_FIFO_SIZE; + bus->slv_rd_ind = bus->slv_rd_ind & (bus->data->fifo_size - 1); bus->slv_rd_buf[bus->slv_rd_ind] = data; bus->slv_rd_ind++; @@ -912,8 +932,8 @@ static int npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus) int ret = bus->slv_wr_ind; /* fill a cyclic buffer */ - for (i = 0; i < I2C_HW_FIFO_SIZE; i++) { - if (bus->slv_wr_size >= I2C_HW_FIFO_SIZE) + for (i = 0; i < bus->data->fifo_size; i++) { + if (bus->slv_wr_size >= bus->data->fifo_size) break; if (bus->state == I2C_SLAVE_MATCH) { i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value); @@ -921,11 +941,11 @@ static int npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus) } else { i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value); } - ind = (bus->slv_wr_ind + bus->slv_wr_size) % I2C_HW_FIFO_SIZE; + ind = (bus->slv_wr_ind + bus->slv_wr_size) & (bus->data->fifo_size - 1); bus->slv_wr_buf[ind] = value; bus->slv_wr_size++; } - return I2C_HW_FIFO_SIZE - ret; + return bus->data->fifo_size - ret; } static void npcm_i2c_slave_send_rd_buf(struct npcm_i2c *bus) @@ -960,7 +980,7 @@ static void npcm_i2c_slave_receive(struct npcm_i2c *bus, u16 nread, bus->slv_rd_ind = 0; iowrite8(0, bus->reg + NPCM_I2CTXF_CTL); - iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CRXF_CTL); + iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CRXF_CTL); npcm_i2c_clear_tx_fifo(bus); npcm_i2c_clear_rx_fifo(bus); } @@ -993,12 +1013,12 @@ static void npcm_i2c_slave_wr_buf_sync(struct npcm_i2c *bus) { int left_in_fifo; - left_in_fifo = FIELD_GET(NPCM_I2CTXF_STS_TX_BYTES, - ioread8(bus->reg + NPCM_I2CTXF_STS)); + left_in_fifo = bus->data->txf_sts_tx_bytes & + ioread8(bus->reg + NPCM_I2CTXF_STS); /* fifo already full: */ - if (left_in_fifo >= I2C_HW_FIFO_SIZE || - bus->slv_wr_size >= I2C_HW_FIFO_SIZE) + if (left_in_fifo >= bus->data->fifo_size || + bus->slv_wr_size >= bus->data->fifo_size) return; /* update the wr fifo index back to the untransmitted bytes: */ @@ -1006,7 +1026,7 @@ static void npcm_i2c_slave_wr_buf_sync(struct npcm_i2c *bus) bus->slv_wr_size = bus->slv_wr_size + left_in_fifo; if (bus->slv_wr_ind < 0) - bus->slv_wr_ind += I2C_HW_FIFO_SIZE; + bus->slv_wr_ind += bus->data->fifo_size; } static void npcm_i2c_slave_rd_wr(struct npcm_i2c *bus) @@ -1152,7 +1172,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus) npcm_i2c_clear_rx_fifo(bus); npcm_i2c_clear_tx_fifo(bus); iowrite8(0, bus->reg + NPCM_I2CTXF_CTL); - iowrite8(I2C_HW_FIFO_SIZE, bus->reg + NPCM_I2CRXF_CTL); + iowrite8(bus->data->fifo_size, bus->reg + NPCM_I2CRXF_CTL); if (NPCM_I2CST_XMIT & i2cst) { bus->operation = I2C_WRITE_OPER; } else { @@ -1313,8 +1333,8 @@ static void npcm_i2c_master_fifo_read(struct npcm_i2c *bus) * read == FIFO Size + C (where C < FIFO Size)then first read C bytes * and in the next int we read rest of the data. */ - if (rcount < (2 * I2C_HW_FIFO_SIZE) && rcount > I2C_HW_FIFO_SIZE) - fifo_bytes = rcount - I2C_HW_FIFO_SIZE; + if (rcount < (2 * bus->data->fifo_size) && rcount > bus->data->fifo_size) + fifo_bytes = rcount - bus->data->fifo_size; if (rcount <= fifo_bytes) { /* last bytes are about to be read - end of tx */ @@ -2193,7 +2213,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, * It cannot be cleared without resetting the module. */ else if (bus->cmd_err && - (NPCM_I2CRXF_CTL_LAST_PEC & ioread8(bus->reg + NPCM_I2CRXF_CTL))) + (bus->data->rxf_ctl_last_pec & ioread8(bus->reg + NPCM_I2CRXF_CTL))) npcm_i2c_reset(bus); /* after any xfer, successful or not, stall and EOB must be disabled */ @@ -2262,6 +2282,7 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; static struct regmap *gcr_regmap; + struct device *dev = &pdev->dev; struct i2c_adapter *adap; struct npcm_i2c *bus; struct clk *i2c_clk; @@ -2274,6 +2295,12 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev) bus->dev = &pdev->dev; + bus->data = of_device_get_match_data(dev); + if (!bus->data) { + dev_err(dev, "OF data missing\n"); + return -EINVAL; + } + bus->num = of_alias_get_id(pdev->dev.of_node, "i2c"); /* core clk must be acquired to calculate module timing settings */ i2c_clk = devm_clk_get(&pdev->dev, NULL); @@ -2287,7 +2314,7 @@ static int npcm_i2c_probe_bus(struct platform_device *pdev) if (IS_ERR(gcr_regmap)) return PTR_ERR(gcr_regmap); - regmap_write(gcr_regmap, NPCM_I2CSEGCTL, NPCM_I2CSEGCTL_INIT_VAL); + regmap_write(gcr_regmap, NPCM_I2CSEGCTL, bus->data->segctl_init_val); bus->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bus->reg)) @@ -2349,7 +2376,8 @@ static int npcm_i2c_remove_bus(struct platform_device *pdev) } static const struct of_device_id npcm_i2c_bus_of_table[] = { - { .compatible = "nuvoton,npcm750-i2c", }, + { .compatible = "nuvoton,npcm750-i2c", .data = &npxm7xx_i2c_data }, + { .compatible = "nuvoton,npcm845-i2c", .data = &npxm8xx_i2c_data }, {} }; MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table); -- cgit v1.2.3 From ffad0a354b0c844656f0ff44df4cd9233865a9cf Mon Sep 17 00:00:00 2001 From: Tyrone Ting Date: Wed, 25 May 2022 11:23:41 +0800 Subject: i2c: npcm: Capitalize the one-line comment Make the one-line comments capital in the driver to get the comment style consistent. Fixes: 56a1485b102e ("i2c: npcm7xx: Add Nuvoton NPCM I2C controller driver") Signed-off-by: Tyrone Ting Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-npcm7xx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 86fd942ea648..7d863edc14e4 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -680,7 +680,7 @@ static void npcm_i2c_reset(struct npcm_i2c *bus) } #endif - /* clear status bits for spurious interrupts */ + /* Clear status bits for spurious interrupts */ npcm_i2c_clear_master_status(bus); bus->state = I2C_IDLE; @@ -1252,7 +1252,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus) } /* SDAST */ /* - * if irq is not one of the above, make sure EOB is disabled and all + * If irq is not one of the above, make sure EOB is disabled and all * status bits are cleared. */ if (ret == IRQ_NONE) { @@ -1506,7 +1506,7 @@ static void npcm_i2c_irq_handle_nack(struct npcm_i2c *bus) npcm_i2c_clear_master_status(bus); readx_poll_timeout_atomic(ioread8, bus->reg + NPCM_I2CCST, val, !(val & NPCM_I2CCST_BUSY), 10, 200); - /* verify no status bits are still set after bus is released */ + /* Verify no status bits are still set after bus is released */ npcm_i2c_clear_master_status(bus); } bus->state = I2C_IDLE; @@ -1974,7 +1974,7 @@ static int npcm_i2c_init_module(struct npcm_i2c *bus, enum i2c_mode mode, npcm_i2c_reset(bus); - /* check HW is OK: SDA and SCL should be high at this point. */ + /* Check HW is OK: SDA and SCL should be high at this point. */ if ((npcm_i2c_get_SDA(&bus->adap) == 0) || (npcm_i2c_get_SCL(&bus->adap) == 0)) { dev_err(bus->dev, "I2C%d init fail: lines are low\n", bus->num); dev_err(bus->dev, "SDA=%d SCL=%d\n", npcm_i2c_get_SDA(&bus->adap), @@ -2034,7 +2034,7 @@ static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id) return IRQ_HANDLED; } #endif - /* clear status bits for spurious interrupts */ + /* Clear status bits for spurious interrupts */ npcm_i2c_clear_master_status(bus); return IRQ_HANDLED; @@ -2216,7 +2216,7 @@ static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, (bus->data->rxf_ctl_last_pec & ioread8(bus->reg + NPCM_I2CRXF_CTL))) npcm_i2c_reset(bus); - /* after any xfer, successful or not, stall and EOB must be disabled */ + /* After any xfer, successful or not, stall and EOB must be disabled */ npcm_i2c_stall_after_start(bus, false); npcm_i2c_eob_int(bus, false); -- cgit v1.2.3 From f24bc86adec6e2ba10a0da21fcfff1be10e77922 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 2 Jun 2022 19:52:58 +0300 Subject: i2c: qcom-cci: simplify access to bus data structure Trivial non-functional change, which adds an alias to an extensively used data location. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Loic Poulain Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qcom-cci.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 5c7cc862f08f..774ca30fbab6 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -541,6 +541,7 @@ static int cci_probe(struct platform_device *pdev) return -ENOENT; for_each_available_child_of_node(dev->of_node, child) { + struct cci_master *master; u32 idx; ret = of_property_read_u32(child, "reg", &idx); @@ -555,27 +556,27 @@ static int cci_probe(struct platform_device *pdev) continue; } - cci->master[idx].adap.quirks = &cci->data->quirks; - cci->master[idx].adap.algo = &cci_algo; - cci->master[idx].adap.dev.parent = dev; - cci->master[idx].adap.dev.of_node = of_node_get(child); - cci->master[idx].master = idx; - cci->master[idx].cci = cci; + master = &cci->master[idx]; + master->adap.quirks = &cci->data->quirks; + master->adap.algo = &cci_algo; + master->adap.dev.parent = dev; + master->adap.dev.of_node = of_node_get(child); + master->master = idx; + master->cci = cci; - i2c_set_adapdata(&cci->master[idx].adap, &cci->master[idx]); - snprintf(cci->master[idx].adap.name, - sizeof(cci->master[idx].adap.name), "Qualcomm-CCI"); + i2c_set_adapdata(&master->adap, master); + snprintf(master->adap.name, sizeof(master->adap.name), "Qualcomm-CCI"); - cci->master[idx].mode = I2C_MODE_STANDARD; + master->mode = I2C_MODE_STANDARD; ret = of_property_read_u32(child, "clock-frequency", &val); if (!ret) { if (val == I2C_MAX_FAST_MODE_FREQ) - cci->master[idx].mode = I2C_MODE_FAST; + master->mode = I2C_MODE_FAST; else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ) - cci->master[idx].mode = I2C_MODE_FAST_PLUS; + master->mode = I2C_MODE_FAST_PLUS; } - init_completion(&cci->master[idx].irq_complete); + init_completion(&master->irq_complete); } /* Memory */ -- cgit v1.2.3 From 3d43273d7d1e1a5374d531e901d3c537b4c97bbf Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 25 May 2022 22:09:14 -0300 Subject: i2c: mxs: Silence a clang warning Change the of_device_get_match_data() cast to (uintptr_t) to silence the following clang warning: drivers/i2c/busses/i2c-mxs.c:802:18: warning: cast to smaller integer type 'enum mxs_i2c_devtype' from 'const void *' [-Wvoid-pointer-to-enum-cast] Reported-by: kernel test robot Fixes: c32abd8b5691 ("i2c: mxs: Remove unneeded platform_device_id") Signed-off-by: Fabio Estevam Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mxs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 864a3f1bd4e1..68f67d084c63 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -799,7 +799,7 @@ static int mxs_i2c_probe(struct platform_device *pdev) if (!i2c) return -ENOMEM; - i2c->dev_type = (enum mxs_i2c_devtype)of_device_get_match_data(&pdev->dev); + i2c->dev_type = (uintptr_t)of_device_get_match_data(&pdev->dev); i2c->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(i2c->regs)) -- cgit v1.2.3 From 99ad11e06be8ad29abee1ec3b8b22dfe77bbabec Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 26 May 2022 15:12:13 +0200 Subject: i2c: dummy: Drop no-op remove function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A remove callback that just returns 0 is equivalent to no callback at all as can be seen in i2c_device_remove(). So simplify accordingly. Signed-off-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index d43db2c3876e..8ae47e0bbd67 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1023,15 +1023,9 @@ static int dummy_probe(struct i2c_client *client, return 0; } -static int dummy_remove(struct i2c_client *client) -{ - return 0; -} - static struct i2c_driver dummy_driver = { .driver.name = "dummy", .probe = dummy_probe, - .remove = dummy_remove, .id_table = dummy_id, }; -- cgit v1.2.3 From e749e4fc900a579720bea9de05174e78f80802b1 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Tue, 14 Jun 2022 11:48:55 +0530 Subject: i2c: xiic: Fix the type check for xiic_wakeup Fix the coverity warning mixed_enum_type: enumerated type mixed with another type We are passing an enum in the xiic_wakeup lets change the function parameters to reflect that. Signed-off-by: Shubhrajyoti Datta Acked-by: Michal Simek [wsa: fixed $subject with proper prefix] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-xiic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 9a1c3f8b7048..b3fe6b2aa3ca 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -367,7 +367,7 @@ static void xiic_fill_tx_fifo(struct xiic_i2c *i2c) } } -static void xiic_wakeup(struct xiic_i2c *i2c, int code) +static void xiic_wakeup(struct xiic_i2c *i2c, enum xilinx_i2c_state code) { i2c->tx_msg = NULL; i2c->rx_msg = NULL; @@ -383,7 +383,7 @@ static irqreturn_t xiic_process(int irq, void *dev_id) u32 clr = 0; int xfer_more = 0; int wakeup_req = 0; - int wakeup_code = 0; + enum xilinx_i2c_state wakeup_code = STATE_DONE; int ret; /* Get the interrupt Status from the IPIF. There is no clearing of -- cgit v1.2.3 From 09b343038e3470e4d0da45f0ee09fb42107e5314 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Fri, 25 Mar 2022 13:06:25 -0500 Subject: i2c: mv64xxx: Remove shutdown method from driver When I attempt to shut down (or reboot) my R8 based NTC CHIP with this i2c driver I get the following error: "i2c i2c-0: mv64xxx: I2C bus locked, block: 1, time_left: 0". Reboots are successful but shutdowns freeze. If I comment out the shutdown routine the device both reboots and shuts down successfully without receiving this error (however it does receive a warning of missing atomic_xfer). It appears that very few i2c drivers have a shutdown method, I assume because these devices are often used to communicate with PMICs (such as in my case with the R8 based NTC CHIP). I'm proposing we simply remove this method so long as it doesn't cause trouble for others downstream. I'll work on an atomic_xfer method and submit that in a different patch. Signed-off-by: Chris Morgan Acked-by: Gregory CLEMENT Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 5c8e94b6cdb5..424c53e4c513 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -1047,14 +1047,6 @@ mv64xxx_i2c_remove(struct platform_device *pd) return 0; } -static void -mv64xxx_i2c_shutdown(struct platform_device *pd) -{ - pm_runtime_disable(&pd->dev); - if (!pm_runtime_status_suspended(&pd->dev)) - mv64xxx_i2c_runtime_suspend(&pd->dev); -} - static const struct dev_pm_ops mv64xxx_i2c_pm_ops = { SET_RUNTIME_PM_OPS(mv64xxx_i2c_runtime_suspend, mv64xxx_i2c_runtime_resume, NULL) @@ -1065,7 +1057,6 @@ static const struct dev_pm_ops mv64xxx_i2c_pm_ops = { static struct platform_driver mv64xxx_i2c_driver = { .probe = mv64xxx_i2c_probe, .remove = mv64xxx_i2c_remove, - .shutdown = mv64xxx_i2c_shutdown, .driver = { .name = MV64XXX_I2C_CTLR_NAME, .pm = &mv64xxx_i2c_pm_ops, -- cgit v1.2.3 From 544a8d75f3d6e60e160cd92dc56321484598a993 Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Wed, 30 Mar 2022 12:16:57 -0500 Subject: i2c: mv64xxx: Add atomic_xfer method to driver Add an atomic_xfer method to the driver so that it behaves correctly when controlling a PMIC that is responsible for device shutdown. The atomic_xfer method added is similar to the one from the i2c-rk3x driver. When running an atomic_xfer a bool flag in the driver data is set, the interrupt is not unmasked on transfer start, and the IRQ handler is manually invoked while waiting for pending transfers to complete. Signed-off-by: Chris Morgan Acked-by: Gregory CLEMENT Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 52 +++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 424c53e4c513..103a05ecc3d6 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -150,6 +150,7 @@ struct mv64xxx_i2c_data { /* Clk div is 2 to the power n, not 2 to the power n + 1 */ bool clk_n_base_0; struct i2c_bus_recovery_info rinfo; + bool atomic; }; static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { @@ -179,7 +180,10 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, u32 dir = 0; drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK | - MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN; + MV64XXX_I2C_REG_CONTROL_TWSIEN; + + if (!drv_data->atomic) + drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_INTEN; if (msg->flags & I2C_M_RD) dir = 1; @@ -409,7 +413,8 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) case MV64XXX_I2C_ACTION_RCV_DATA_STOP: drv_data->msg->buf[drv_data->byte_posn++] = readl(drv_data->reg_base + drv_data->reg_offsets.data); - drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; + if (!drv_data->atomic) + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP, drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block = 0; @@ -427,7 +432,8 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) drv_data->rc = -EIO; fallthrough; case MV64XXX_I2C_ACTION_SEND_STOP: - drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; + if (!drv_data->atomic) + drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP, drv_data->reg_base + drv_data->reg_offsets.control); drv_data->block = 0; @@ -575,6 +581,17 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) spin_unlock_irqrestore(&drv_data->lock, flags); } +static void mv64xxx_i2c_wait_polling(struct mv64xxx_i2c_data *drv_data) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), drv_data->adapter.timeout); + + while (READ_ONCE(drv_data->block) && + ktime_compare(ktime_get(), timeout) < 0) { + udelay(5); + mv64xxx_i2c_intr(0, drv_data); + } +} + static int mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, int is_last) @@ -590,7 +607,11 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, mv64xxx_i2c_send_start(drv_data); spin_unlock_irqrestore(&drv_data->lock, flags); - mv64xxx_i2c_wait_for_completion(drv_data); + if (!drv_data->atomic) + mv64xxx_i2c_wait_for_completion(drv_data); + else + mv64xxx_i2c_wait_polling(drv_data); + return drv_data->rc; } @@ -717,7 +738,7 @@ mv64xxx_i2c_functionality(struct i2c_adapter *adap) } static int -mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +mv64xxx_i2c_xfer_core(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); int rc, ret = num; @@ -730,7 +751,7 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) drv_data->msgs = msgs; drv_data->num_msgs = num; - if (mv64xxx_i2c_can_offload(drv_data)) + if (mv64xxx_i2c_can_offload(drv_data) && !drv_data->atomic) rc = mv64xxx_i2c_offload_xfer(drv_data); else rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[0], num == 1); @@ -747,8 +768,27 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) return ret; } +static int +mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); + + drv_data->atomic = 0; + return mv64xxx_i2c_xfer_core(adap, msgs, num); +} + +static int mv64xxx_i2c_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); + + drv_data->atomic = 1; + return mv64xxx_i2c_xfer_core(adap, msgs, num); +} + static const struct i2c_algorithm mv64xxx_i2c_algo = { .master_xfer = mv64xxx_i2c_xfer, + .master_xfer_atomic = mv64xxx_i2c_xfer_atomic, .functionality = mv64xxx_i2c_functionality, }; -- cgit v1.2.3 From 4c0ad47b8da5898115fc17c5bb0e538348531a39 Mon Sep 17 00:00:00 2001 From: Matti Lehtimäki Date: Sun, 22 May 2022 18:27:58 +0200 Subject: i2c: qcom-cci: add msm8974 compatible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MSM8974 CCI is the same as MSM8916 except it has two masters. Signed-off-by: Matti Lehtimäki Signed-off-by: Luca Weiss Reviewed-by: Loic Poulain Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qcom-cci.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 774ca30fbab6..ea48e6a9cfca 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -726,6 +726,40 @@ static const struct cci_data cci_v1_data = { }, }; +static const struct cci_data cci_v1_5_data = { + .num_masters = 2, + .queue_size = { 64, 16 }, + .quirks = { + .max_write_len = 10, + .max_read_len = 12, + }, + .cci_clk_rate = 19200000, + .params[I2C_MODE_STANDARD] = { + .thigh = 78, + .tlow = 114, + .tsu_sto = 28, + .tsu_sta = 28, + .thd_dat = 10, + .thd_sta = 77, + .tbuf = 118, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 1 + }, + .params[I2C_MODE_FAST] = { + .thigh = 20, + .tlow = 28, + .tsu_sto = 21, + .tsu_sta = 21, + .thd_dat = 13, + .thd_sta = 18, + .tbuf = 32, + .scl_stretch_en = 0, + .trdhld = 6, + .tsp = 3 + }, +}; + static const struct cci_data cci_v2_data = { .num_masters = 2, .queue_size = { 64, 16 }, @@ -774,6 +808,7 @@ static const struct cci_data cci_v2_data = { static const struct of_device_id cci_dt_match[] = { { .compatible = "qcom,msm8916-cci", .data = &cci_v1_data}, + { .compatible = "qcom,msm8974-cci", .data = &cci_v1_5_data}, { .compatible = "qcom,msm8996-cci", .data = &cci_v2_data}, { .compatible = "qcom,sdm845-cci", .data = &cci_v2_data}, { .compatible = "qcom,sm8250-cci", .data = &cci_v2_data}, -- cgit v1.2.3 From 4dc1372ffb82c5f08ada7b9cb75a04dcfe49cd96 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 10 Jun 2022 15:51:06 +0800 Subject: i2c: hisi: use HZ_PER_KHZ macro in units.h HZ macros has been centralized in units.h since [1]. Use it to avoid duplicated definition. [1] commit e2c77032fcbe ("units: add the HZ macros") Signed-off-by: Yicong Yang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-hisi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index acf394812061..76c3d8f6fc3c 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -15,6 +15,7 @@ #include #include #include +#include #define HISI_I2C_FRAME_CTRL 0x0000 #define HISI_I2C_FRAME_CTRL_SPEED_MODE GENMASK(1, 0) @@ -80,8 +81,6 @@ #define HISI_I2C_TX_F_AE_THRESH 1 #define HISI_I2C_RX_F_AF_THRESH 60 -#define HZ_PER_KHZ 1000 - #define NSEC_TO_CYCLES(ns, clk_rate_khz) \ DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC) -- cgit v1.2.3 From e4c72c06c367758a14f227c847f9d623f1994ecf Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Fri, 27 Dec 2019 09:34:32 +0000 Subject: i2c: Fix a potential use after free Free the adap structure only after we are done using it. This patch just moves the put_device() down a bit to avoid the use after free. Fixes: 611e12ea0f12 ("i2c: core: manage i2c bus device refcount in i2c_[get|put]_adapter") Signed-off-by: Xu Wang [wsa: added comment to the code, added Fixes tag] Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 8ae47e0bbd67..10f35f942066 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -2461,8 +2461,9 @@ void i2c_put_adapter(struct i2c_adapter *adap) if (!adap) return; - put_device(&adap->dev); module_put(adap->owner); + /* Should be last, otherwise we risk use-after-free with 'adap' */ + put_device(&adap->dev); } EXPORT_SYMBOL(i2c_put_adapter); -- cgit v1.2.3 From 24fff66f1f26d618d3b16c36603c10175e3d660f Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 30 Jun 2022 10:41:54 +0300 Subject: i2c: i801: Add support for Intel Meteor Lake-P Add SMBus PCI ID on Intel Meteor Lake-P. Signed-off-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801.rst | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 3 +++ 3 files changed, 5 insertions(+) (limited to 'drivers/i2c') diff --git a/Documentation/i2c/busses/i2c-i801.rst b/Documentation/i2c/busses/i2c-i801.rst index cad59170b2ad..ab9e850e8fe0 100644 --- a/Documentation/i2c/busses/i2c-i801.rst +++ b/Documentation/i2c/busses/i2c-i801.rst @@ -46,6 +46,7 @@ Supported adapters: * Intel Emmitsburg (PCH) * Intel Alder Lake (PCH) * Intel Raptor Lake (PCH) + * Intel Meteor Lake (SOC) Datasheets: Publicly available at the Intel website diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index b1d7069dd377..3f6d03073079 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -156,6 +156,7 @@ config I2C_I801 Emmitsburg (PCH) Alder Lake (PCH) Raptor Lake (PCH) + Meteor Lake (SOC) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index ff706349bdfb..9e5b87e107ba 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -76,6 +76,7 @@ * Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes * Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes * Raptor Lake-S (PCH) 0x7a23 32 hard yes yes yes + * Meteor Lake-P (SOC) 0x7e22 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -231,6 +232,7 @@ #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4 #define PCI_DEVICE_ID_INTEL_RAPTOR_LAKE_S_SMBUS 0x7a23 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3 +#define PCI_DEVICE_ID_INTEL_METEOR_LAKE_P_SMBUS 0x7e22 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2 #define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22 @@ -1049,6 +1051,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_M_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, { PCI_DEVICE_DATA(INTEL, RAPTOR_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, + { PCI_DEVICE_DATA(INTEL, METEOR_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, { 0, } }; -- cgit v1.2.3 From 64a6f1c4987e027339818b9d142257e7ede6dfe0 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 6 Jul 2022 15:13:13 +0100 Subject: i2c: add support for microchip fpga i2c controllers Add Microchip CoreI2C i2c controller support. This driver supports the "hard" i2c controller on the Microchip PolarFire SoC & the basic feature set for "soft" i2c controller implemtations in the FPGA fabric. Co-developed-by: Daire McNamara Signed-off-by: Daire McNamara Signed-off-by: Conor Dooley Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 11 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-microchip-corei2c.c | 480 +++++++++++++++++++++++++++++ 3 files changed, 492 insertions(+) create mode 100644 drivers/i2c/busses/i2c-microchip-corei2c.c (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 3f6d03073079..f77f51b273a4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -782,6 +782,17 @@ config I2C_MESON If you say yes to this option, support will be included for the I2C interface on the Amlogic Meson family of SoCs. +config I2C_MICROCHIP_CORE + tristate "Microchip FPGA I2C controller" + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on OF + help + If you say yes to this option, support will be included for the + I2C interface on Microchip FPGAs. + + This driver can also be built as a module. If so, the module will be + called i2c-microchip-core. + config I2C_MPC tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" depends on PPC diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index b0a10e5d9ee9..29d729b6a5b7 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o obj-$(CONFIG_I2C_MESON) += i2c-meson.o +obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o obj-$(CONFIG_I2C_MT7621) += i2c-mt7621.o diff --git a/drivers/i2c/busses/i2c-microchip-corei2c.c b/drivers/i2c/busses/i2c-microchip-corei2c.c new file mode 100644 index 000000000000..6df0f1c33278 --- /dev/null +++ b/drivers/i2c/busses/i2c-microchip-corei2c.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip CoreI2C I2C controller driver + * + * Copyright (c) 2018-2022 Microchip Corporation. All rights reserved. + * + * Author: Daire McNamara + * Author: Conor Dooley + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CORE_I2C_CTRL (0x00) +#define CTRL_CR0 BIT(0) +#define CTRL_CR1 BIT(1) +#define CTRL_AA BIT(2) +#define CTRL_SI BIT(3) +#define CTRL_STO BIT(4) +#define CTRL_STA BIT(5) +#define CTRL_ENS1 BIT(6) +#define CTRL_CR2 BIT(7) + +#define STATUS_BUS_ERROR (0x00) +#define STATUS_M_START_SENT (0x08) +#define STATUS_M_REPEATED_START_SENT (0x10) +#define STATUS_M_SLAW_ACK (0x18) +#define STATUS_M_SLAW_NACK (0x20) +#define STATUS_M_TX_DATA_ACK (0x28) +#define STATUS_M_TX_DATA_NACK (0x30) +#define STATUS_M_ARB_LOST (0x38) +#define STATUS_M_SLAR_ACK (0x40) +#define STATUS_M_SLAR_NACK (0x48) +#define STATUS_M_RX_DATA_ACKED (0x50) +#define STATUS_M_RX_DATA_NACKED (0x58) +#define STATUS_S_SLAW_ACKED (0x60) +#define STATUS_S_ARB_LOST_SLAW_ACKED (0x68) +#define STATUS_S_GENERAL_CALL_ACKED (0x70) +#define STATUS_S_ARB_LOST_GENERAL_CALL_ACKED (0x78) +#define STATUS_S_RX_DATA_ACKED (0x80) +#define STATUS_S_RX_DATA_NACKED (0x88) +#define STATUS_S_GENERAL_CALL_RX_DATA_ACKED (0x90) +#define STATUS_S_GENERAL_CALL_RX_DATA_NACKED (0x98) +#define STATUS_S_RX_STOP (0xA0) +#define STATUS_S_SLAR_ACKED (0xA8) +#define STATUS_S_ARB_LOST_SLAR_ACKED (0xB0) +#define STATUS_S_TX_DATA_ACK (0xB8) +#define STATUS_S_TX_DATA_NACK (0xC0) +#define STATUS_LAST_DATA_ACK (0xC8) +#define STATUS_M_SMB_MASTER_RESET (0xD0) +#define STATUS_S_SCL_LOW_TIMEOUT (0xD8) /* 25 ms */ +#define STATUS_NO_STATE_INFO (0xF8) + +#define CORE_I2C_STATUS (0x04) +#define CORE_I2C_DATA (0x08) +#define WRITE_BIT (0x0) +#define READ_BIT (0x1) +#define SLAVE_ADDR_SHIFT (1) +#define CORE_I2C_SLAVE0_ADDR (0x0c) +#define GENERAL_CALL_BIT (0x0) +#define CORE_I2C_SMBUS (0x10) +#define SMBALERT_INT_ENB (0x0) +#define SMBSUS_INT_ENB (0x1) +#define SMBUS_ENB (0x2) +#define SMBALERT_NI_STATUS (0x3) +#define SMBALERT_NO_CTRL (0x4) +#define SMBSUS_NI_STATUS (0x5) +#define SMBSUS_NO_CTRL (0x6) +#define SMBUS_RESET (0x7) +#define CORE_I2C_FREQ (0x14) +#define CORE_I2C_GLITCHREG (0x18) +#define CORE_I2C_SLAVE1_ADDR (0x1c) + +#define PCLK_DIV_960 (CTRL_CR2) +#define PCLK_DIV_256 (0) +#define PCLK_DIV_224 (CTRL_CR0) +#define PCLK_DIV_192 (CTRL_CR1) +#define PCLK_DIV_160 (CTRL_CR0 | CTRL_CR1) +#define PCLK_DIV_120 (CTRL_CR0 | CTRL_CR2) +#define PCLK_DIV_60 (CTRL_CR1 | CTRL_CR2) +#define BCLK_DIV_8 (CTRL_CR0 | CTRL_CR1 | CTRL_CR2) +#define CLK_MASK (CTRL_CR0 | CTRL_CR1 | CTRL_CR2) + +/** + * struct mchp_corei2c_dev - Microchip CoreI2C device private data + * + * @base: pointer to register struct + * @dev: device reference + * @i2c_clk: clock reference for i2c input clock + * @buf: pointer to msg buffer for easier use + * @msg_complete: xfer completion object + * @adapter: core i2c abstraction + * @msg_err: error code for completed message + * @bus_clk_rate: current i2c bus clock rate + * @isr_status: cached copy of local ISR status + * @msg_len: number of bytes transferred in msg + * @addr: address of the current slave + */ +struct mchp_corei2c_dev { + void __iomem *base; + struct device *dev; + struct clk *i2c_clk; + u8 *buf; + struct completion msg_complete; + struct i2c_adapter adapter; + int msg_err; + u32 bus_clk_rate; + u32 isr_status; + u16 msg_len; + u8 addr; +}; + +static void mchp_corei2c_core_disable(struct mchp_corei2c_dev *idev) +{ + u8 ctrl = readb(idev->base + CORE_I2C_CTRL); + + ctrl &= ~CTRL_ENS1; + writeb(ctrl, idev->base + CORE_I2C_CTRL); +} + +static void mchp_corei2c_core_enable(struct mchp_corei2c_dev *idev) +{ + u8 ctrl = readb(idev->base + CORE_I2C_CTRL); + + ctrl |= CTRL_ENS1; + writeb(ctrl, idev->base + CORE_I2C_CTRL); +} + +static void mchp_corei2c_reset(struct mchp_corei2c_dev *idev) +{ + mchp_corei2c_core_disable(idev); + mchp_corei2c_core_enable(idev); +} + +static inline void mchp_corei2c_stop(struct mchp_corei2c_dev *idev) +{ + u8 ctrl = readb(idev->base + CORE_I2C_CTRL); + + ctrl |= CTRL_STO; + writeb(ctrl, idev->base + CORE_I2C_CTRL); +} + +static inline int mchp_corei2c_set_divisor(u32 rate, + struct mchp_corei2c_dev *idev) +{ + u8 clkval, ctrl; + + if (rate >= 960) + clkval = PCLK_DIV_960; + else if (rate >= 256) + clkval = PCLK_DIV_256; + else if (rate >= 224) + clkval = PCLK_DIV_224; + else if (rate >= 192) + clkval = PCLK_DIV_192; + else if (rate >= 160) + clkval = PCLK_DIV_160; + else if (rate >= 120) + clkval = PCLK_DIV_120; + else if (rate >= 60) + clkval = PCLK_DIV_60; + else if (rate >= 8) + clkval = BCLK_DIV_8; + else + return -EINVAL; + + ctrl = readb(idev->base + CORE_I2C_CTRL); + ctrl &= ~CLK_MASK; + ctrl |= clkval; + writeb(ctrl, idev->base + CORE_I2C_CTRL); + + ctrl = readb(idev->base + CORE_I2C_CTRL); + if ((ctrl & CLK_MASK) != clkval) + return -EIO; + + return 0; +} + +static int mchp_corei2c_init(struct mchp_corei2c_dev *idev) +{ + u32 clk_rate = clk_get_rate(idev->i2c_clk); + u32 divisor = clk_rate / idev->bus_clk_rate; + int ret; + + ret = mchp_corei2c_set_divisor(divisor, idev); + if (ret) + return ret; + + mchp_corei2c_reset(idev); + + return 0; +} + +static void mchp_corei2c_empty_rx(struct mchp_corei2c_dev *idev) +{ + u8 ctrl; + + if (idev->msg_len > 0) { + *idev->buf++ = readb(idev->base + CORE_I2C_DATA); + idev->msg_len--; + } + + if (idev->msg_len == 0) { + ctrl = readb(idev->base + CORE_I2C_CTRL); + ctrl &= ~CTRL_AA; + writeb(ctrl, idev->base + CORE_I2C_CTRL); + } +} + +static int mchp_corei2c_fill_tx(struct mchp_corei2c_dev *idev) +{ + if (idev->msg_len > 0) + writeb(*idev->buf++, idev->base + CORE_I2C_DATA); + idev->msg_len--; + + return 0; +} + +static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev) +{ + u32 status = idev->isr_status; + u8 ctrl; + bool last_byte = false, finished = false; + + if (!idev->buf) + return IRQ_NONE; + + switch (status) { + case STATUS_M_START_SENT: + case STATUS_M_REPEATED_START_SENT: + ctrl = readb(idev->base + CORE_I2C_CTRL); + ctrl &= ~CTRL_STA; + writeb(idev->addr, idev->base + CORE_I2C_DATA); + writeb(ctrl, idev->base + CORE_I2C_CTRL); + if (idev->msg_len == 0) + finished = true; + break; + case STATUS_M_ARB_LOST: + idev->msg_err = -EAGAIN; + finished = true; + break; + case STATUS_M_SLAW_ACK: + case STATUS_M_TX_DATA_ACK: + if (idev->msg_len > 0) + mchp_corei2c_fill_tx(idev); + else + last_byte = true; + break; + case STATUS_M_TX_DATA_NACK: + case STATUS_M_SLAR_NACK: + case STATUS_M_SLAW_NACK: + idev->msg_err = -ENXIO; + last_byte = true; + break; + case STATUS_M_SLAR_ACK: + ctrl = readb(idev->base + CORE_I2C_CTRL); + if (idev->msg_len == 1u) { + ctrl &= ~CTRL_AA; + writeb(ctrl, idev->base + CORE_I2C_CTRL); + } else { + ctrl |= CTRL_AA; + writeb(ctrl, idev->base + CORE_I2C_CTRL); + } + if (idev->msg_len < 1u) + last_byte = true; + break; + case STATUS_M_RX_DATA_ACKED: + mchp_corei2c_empty_rx(idev); + break; + case STATUS_M_RX_DATA_NACKED: + mchp_corei2c_empty_rx(idev); + if (idev->msg_len == 0) + last_byte = true; + break; + default: + break; + } + + /* On the last byte to be transmitted, send STOP */ + if (last_byte) + mchp_corei2c_stop(idev); + + if (last_byte || finished) + complete(&idev->msg_complete); + + return IRQ_HANDLED; +} + +static irqreturn_t mchp_corei2c_isr(int irq, void *_dev) +{ + struct mchp_corei2c_dev *idev = _dev; + irqreturn_t ret = IRQ_NONE; + u8 ctrl; + + ctrl = readb(idev->base + CORE_I2C_CTRL); + if (ctrl & CTRL_SI) { + idev->isr_status = readb(idev->base + CORE_I2C_STATUS); + ret = mchp_corei2c_handle_isr(idev); + } + + ctrl = readb(idev->base + CORE_I2C_CTRL); + ctrl &= ~CTRL_SI; + writeb(ctrl, idev->base + CORE_I2C_CTRL); + + return ret; +} + +static int mchp_corei2c_xfer_msg(struct mchp_corei2c_dev *idev, + struct i2c_msg *msg) +{ + u8 ctrl; + unsigned long time_left; + + idev->addr = i2c_8bit_addr_from_msg(msg); + idev->msg_len = msg->len; + idev->buf = msg->buf; + idev->msg_err = 0; + + reinit_completion(&idev->msg_complete); + + mchp_corei2c_core_enable(idev); + + ctrl = readb(idev->base + CORE_I2C_CTRL); + ctrl |= CTRL_STA; + writeb(ctrl, idev->base + CORE_I2C_CTRL); + + time_left = wait_for_completion_timeout(&idev->msg_complete, + idev->adapter.timeout); + if (!time_left) + return -ETIMEDOUT; + + return idev->msg_err; +} + +static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap); + int i, ret; + + for (i = 0; i < num; i++) { + ret = mchp_corei2c_xfer_msg(idev, msgs++); + if (ret) + return ret; + } + + return num; +} + +static u32 mchp_corei2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm mchp_corei2c_algo = { + .master_xfer = mchp_corei2c_xfer, + .functionality = mchp_corei2c_func, +}; + +static int mchp_corei2c_probe(struct platform_device *pdev) +{ + struct mchp_corei2c_dev *idev; + struct resource *res; + int irq, ret; + + idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); + if (!idev) + return -ENOMEM; + + idev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(idev->base)) + return PTR_ERR(idev->base); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return dev_err_probe(&pdev->dev, -ENXIO, + "invalid IRQ %d for I2C controller\n", irq); + + idev->i2c_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(idev->i2c_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(idev->i2c_clk), + "missing clock\n"); + + idev->dev = &pdev->dev; + init_completion(&idev->msg_complete); + + ret = device_property_read_u32(idev->dev, "clock-frequency", + &idev->bus_clk_rate); + if (ret || !idev->bus_clk_rate) { + dev_info(&pdev->dev, "default to 100kHz\n"); + idev->bus_clk_rate = 100000; + } + + if (idev->bus_clk_rate > 400000) + return dev_err_probe(&pdev->dev, -EINVAL, + "clock-frequency too high: %d\n", + idev->bus_clk_rate); + + /* + * This driver supports both the hard peripherals & soft FPGA cores. + * The hard peripherals do not have shared IRQs, but we don't have + * control over what way the interrupts are wired for the soft cores. + */ + ret = devm_request_irq(&pdev->dev, irq, mchp_corei2c_isr, IRQF_SHARED, + pdev->name, idev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to claim irq %d\n", irq); + + ret = clk_prepare_enable(idev->i2c_clk); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to enable clock\n"); + + ret = mchp_corei2c_init(idev); + if (ret) { + clk_disable_unprepare(idev->i2c_clk); + return dev_err_probe(&pdev->dev, ret, "failed to program clock divider\n"); + } + + i2c_set_adapdata(&idev->adapter, idev); + snprintf(idev->adapter.name, sizeof(idev->adapter.name), + "Microchip I2C hw bus at %08lx", (unsigned long)res->start); + idev->adapter.owner = THIS_MODULE; + idev->adapter.algo = &mchp_corei2c_algo; + idev->adapter.dev.parent = &pdev->dev; + idev->adapter.dev.of_node = pdev->dev.of_node; + idev->adapter.timeout = HZ; + + platform_set_drvdata(pdev, idev); + + ret = i2c_add_adapter(&idev->adapter); + if (ret) { + clk_disable_unprepare(idev->i2c_clk); + return ret; + } + + dev_info(&pdev->dev, "registered CoreI2C bus driver\n"); + + return 0; +} + +static int mchp_corei2c_remove(struct platform_device *pdev) +{ + struct mchp_corei2c_dev *idev = platform_get_drvdata(pdev); + + clk_disable_unprepare(idev->i2c_clk); + i2c_del_adapter(&idev->adapter); + + return 0; +} + +static const struct of_device_id mchp_corei2c_of_match[] = { + { .compatible = "microchip,mpfs-i2c" }, + { .compatible = "microchip,corei2c-rtl-v7" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mchp_corei2c_of_match); + +static struct platform_driver mchp_corei2c_driver = { + .probe = mchp_corei2c_probe, + .remove = mchp_corei2c_remove, + .driver = { + .name = "microchip-corei2c", + .of_match_table = mchp_corei2c_of_match, + }, +}; + +module_platform_driver(mchp_corei2c_driver); + +MODULE_DESCRIPTION("Microchip CoreI2C bus driver"); +MODULE_AUTHOR("Daire McNamara "); +MODULE_AUTHOR("Conor Dooley "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d4d2f170dd3313f55ec83ef60b4508ffcbc59695 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Thu, 7 Jul 2022 09:44:02 +0200 Subject: i2c: stm32: add support for the STM32MP13 soc Add a new compatible for the stm32mp13. Fast Mode Plus control register address differ from the one for STM32MP15. Signed-off-by: Alain Volmat Reviewed-by: Pierre-Yves MORDRET Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-stm32f7.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 6d4aa64b195d..d1c59d83a65b 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -410,6 +410,12 @@ static const struct stm32f7_i2c_setup stm32mp15_setup = { .fmp_clr_offset = 0x40, }; +static const struct stm32f7_i2c_setup stm32mp13_setup = { + .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, + .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, + .fmp_clr_offset = 0x4, +}; + static inline void stm32f7_i2c_set_bits(void __iomem *reg, u32 mask) { writel_relaxed(readl_relaxed(reg) | mask, reg); @@ -2468,6 +2474,7 @@ static const struct dev_pm_ops stm32f7_i2c_pm_ops = { static const struct of_device_id stm32f7_i2c_match[] = { { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup}, { .compatible = "st,stm32mp15-i2c", .data = &stm32mp15_setup}, + { .compatible = "st,stm32mp13-i2c", .data = &stm32mp13_setup}, {}, }; MODULE_DEVICE_TABLE(of, stm32f7_i2c_match); -- cgit v1.2.3 From 9ae551ded5ba55f96a83cd0811f7ef8c2f329d0c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 Jul 2022 15:09:58 +0300 Subject: i2c: scmi: Replace open coded device_get_match_data() Replace open coded device_get_match_data() in acpi_smbus_cmi_add(). Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-scmi.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index 6746aa46d96c..79798fc7462a 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -30,7 +30,7 @@ struct acpi_smbus_cmi { u8 cap_info:1; u8 cap_read:1; u8 cap_write:1; - struct smbus_methods_t *methods; + const struct smbus_methods_t *methods; }; static const struct smbus_methods_t smbus_methods = { @@ -361,7 +361,6 @@ static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level, static int acpi_smbus_cmi_add(struct acpi_device *device) { struct acpi_smbus_cmi *smbus_cmi; - const struct acpi_device_id *id; int ret; smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL); @@ -369,6 +368,7 @@ static int acpi_smbus_cmi_add(struct acpi_device *device) return -ENOMEM; smbus_cmi->handle = device->handle; + smbus_cmi->methods = device_get_match_data(&device->dev); strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS); device->driver_data = smbus_cmi; @@ -376,11 +376,6 @@ static int acpi_smbus_cmi_add(struct acpi_device *device) smbus_cmi->cap_read = 0; smbus_cmi->cap_write = 0; - for (id = acpi_smbus_cmi_ids; id->id[0]; id++) - if (!strcmp(id->id, acpi_device_hid(device))) - smbus_cmi->methods = - (struct smbus_methods_t *) id->driver_data; - acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL); -- cgit v1.2.3 From 1f438d2318f4b8012b3153130cf6b0259c76c8d6 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 13 Jul 2022 12:11:36 +0300 Subject: i2c: mlxcpld: Add callback to notify probing completion Add notification to inform caller that driver probing has been completed. It allows to user, invoked platform device registration for "i2c-mlxcpld" driver, to be notified that bus adapter is available, and thus some devices could be connected to this bus. Signed-off-by: Vadim Pasternak Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mlxcpld.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c index 56aa424fd71d..363ea9fd66c4 100644 --- a/drivers/i2c/busses/i2c-mlxcpld.c +++ b/drivers/i2c/busses/i2c-mlxcpld.c @@ -560,6 +560,10 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev) if (err) goto mlxcpld_i2_probe_failed; + /* Notify caller when adapter is added. */ + if (pdata && pdata->completion_notify) + pdata->completion_notify(pdata->handle, mlxcpld_i2c_adapter.nr); + return 0; mlxcpld_i2_probe_failed: -- cgit v1.2.3 From e0ca796a151bd4de76efd71aade2b4805a95c93d Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Fri, 8 Jul 2022 11:03:50 +0100 Subject: i2c: Add Renesas RZ/V2M controller Yet another i2c controller from Renesas that is found on the RZ/V2M (r9a09g011) SoC. It can support only 100kHz and 400KHz operation. Signed-off-by: Phil Edworthy Reviewed-by: Biju Das Reviewed-by: Andy Shevchenko Reviewed-by: Philipp Zabel [wsa: removed superfluous class type and renamed a function] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-rzv2m.c | 532 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 543 insertions(+) create mode 100644 drivers/i2c/busses/i2c-rzv2m.c (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index f77f51b273a4..6a69721c4400 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -996,6 +996,16 @@ config I2C_RK3X This driver can also be built as a module. If so, the module will be called i2c-rk3x. +config I2C_RZV2M + tristate "Renesas RZ/V2M adapter" + depends on ARCH_RENESAS || COMPILE_TEST + help + If you say yes to this option, support will be included for the + Renesas RZ/V2M I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-rzv2m. + config I2C_S3C2410 tristate "S3C/Exynos I2C Driver" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || \ diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 29d729b6a5b7..c5cac15f075c 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o obj-$(CONFIG_I2C_QUP) += i2c-qup.o obj-$(CONFIG_I2C_RIIC) += i2c-riic.o obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o +obj-$(CONFIG_I2C_RZV2M) += i2c-rzv2m.o obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o diff --git a/drivers/i2c/busses/i2c-rzv2m.c b/drivers/i2c/busses/i2c-rzv2m.c new file mode 100644 index 000000000000..56d0faee5c46 --- /dev/null +++ b/drivers/i2c/busses/i2c-rzv2m.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Renesas RZ/V2M I2C unit + * + * Copyright (C) 2016-2022 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets */ +#define IICB0DAT 0x00 /* Data Register */ +#define IICB0CTL0 0x08 /* Control Register 0 */ +#define IICB0TRG 0x0C /* Trigger Register */ +#define IICB0STR0 0x10 /* Status Register 0 */ +#define IICB0CTL1 0x20 /* Control Register 1 */ +#define IICB0WL 0x24 /* Low Level Width Setting Reg */ +#define IICB0WH 0x28 /* How Level Width Setting Reg */ + +/* IICB0CTL0 */ +#define IICB0IICE BIT(7) /* I2C Enable */ +#define IICB0SLWT BIT(1) /* Interrupt Request Timing */ +#define IICB0SLAC BIT(0) /* Acknowledge */ + +/* IICB0TRG */ +#define IICB0WRET BIT(2) /* Quit Wait Trigger */ +#define IICB0STT BIT(1) /* Create Start Condition Trigger */ +#define IICB0SPT BIT(0) /* Create Stop Condition Trigger */ + +/* IICB0STR0 */ +#define IICB0SSAC BIT(8) /* Ack Flag */ +#define IICB0SSBS BIT(6) /* Bus Flag */ +#define IICB0SSSP BIT(4) /* Stop Condition Flag */ + +/* IICB0CTL1 */ +#define IICB0MDSC BIT(7) /* Bus Mode */ +#define IICB0SLSE BIT(1) /* Start condition output */ + +#define bit_setl(addr, val) writel(readl(addr) | (val), (addr)) +#define bit_clrl(addr, val) writel(readl(addr) & ~(val), (addr)) + +struct rzv2m_i2c_priv { + void __iomem *base; + struct i2c_adapter adap; + struct clk *clk; + int bus_mode; + struct completion msg_tia_done; + u32 iicb0wl; + u32 iicb0wh; +}; + +enum bcr_index { + RZV2M_I2C_100K = 0, + RZV2M_I2C_400K, +}; + +struct bitrate_config { + unsigned int percent_low; + unsigned int min_hold_time_ns; +}; + +static const struct bitrate_config bitrate_configs[] = { + [RZV2M_I2C_100K] = { 47, 3450 }, + [RZV2M_I2C_400K] = { 52, 900 }, +}; + +static irqreturn_t rzv2m_i2c_tia_irq_handler(int this_irq, void *dev_id) +{ + struct rzv2m_i2c_priv *priv = dev_id; + + complete(&priv->msg_tia_done); + + return IRQ_HANDLED; +} + +/* Calculate IICB0WL and IICB0WH */ +static int rzv2m_i2c_clock_calculate(struct device *dev, + struct rzv2m_i2c_priv *priv) +{ + const struct bitrate_config *config; + unsigned int hold_time_ns; + unsigned int total_pclks; + unsigned int trf_pclks; + unsigned long pclk_hz; + struct i2c_timings t; + u32 trf_ns; + + i2c_parse_fw_timings(dev, &t, true); + + pclk_hz = clk_get_rate(priv->clk); + total_pclks = pclk_hz / t.bus_freq_hz; + + trf_ns = t.scl_rise_ns + t.scl_fall_ns; + trf_pclks = mul_u64_u32_div(pclk_hz, trf_ns, NSEC_PER_SEC); + + /* Config setting */ + switch (t.bus_freq_hz) { + case I2C_MAX_FAST_MODE_FREQ: + priv->bus_mode = RZV2M_I2C_400K; + break; + case I2C_MAX_STANDARD_MODE_FREQ: + priv->bus_mode = RZV2M_I2C_100K; + break; + default: + dev_err(dev, "transfer speed is invalid\n"); + return -EINVAL; + } + config = &bitrate_configs[priv->bus_mode]; + + /* IICB0WL = (percent_low / Transfer clock) x PCLK */ + priv->iicb0wl = total_pclks * config->percent_low / 100; + if (priv->iicb0wl > (BIT(10) - 1)) + return -EINVAL; + + /* IICB0WH = ((percent_high / Transfer clock) x PCLK) - (tR + tF) */ + priv->iicb0wh = total_pclks - priv->iicb0wl - trf_pclks; + if (priv->iicb0wh > (BIT(10) - 1)) + return -EINVAL; + + /* + * Data hold time must be less than 0.9us in fast mode and + * 3.45us in standard mode. + * Data hold time = IICB0WL[9:2] / PCLK + */ + hold_time_ns = div64_ul((u64)(priv->iicb0wl >> 2) * NSEC_PER_SEC, pclk_hz); + if (hold_time_ns > config->min_hold_time_ns) { + dev_err(dev, "data hold time %dns is over %dns\n", + hold_time_ns, config->min_hold_time_ns); + return -EINVAL; + } + + return 0; +} + +static void rzv2m_i2c_init(struct rzv2m_i2c_priv *priv) +{ + u32 i2c_ctl0; + u32 i2c_ctl1; + + /* i2c disable */ + writel(0, priv->base + IICB0CTL0); + + /* IICB0CTL1 setting */ + i2c_ctl1 = IICB0SLSE; + if (priv->bus_mode == RZV2M_I2C_400K) + i2c_ctl1 |= IICB0MDSC; + writel(i2c_ctl1, priv->base + IICB0CTL1); + + /* IICB0WL IICB0WH setting */ + writel(priv->iicb0wl, priv->base + IICB0WL); + writel(priv->iicb0wh, priv->base + IICB0WH); + + /* i2c enable after setting */ + i2c_ctl0 = IICB0SLWT | IICB0SLAC | IICB0IICE; + writel(i2c_ctl0, priv->base + IICB0CTL0); +} + +static int rzv2m_i2c_write_with_ack(struct rzv2m_i2c_priv *priv, u32 data) +{ + unsigned long time_left; + + reinit_completion(&priv->msg_tia_done); + + writel(data, priv->base + IICB0DAT); + + time_left = wait_for_completion_timeout(&priv->msg_tia_done, + priv->adap.timeout); + if (!time_left) + return -ETIMEDOUT; + + /* Confirm ACK */ + if ((readl(priv->base + IICB0STR0) & IICB0SSAC) != IICB0SSAC) + return -ENXIO; + + return 0; +} + +static int rzv2m_i2c_read_with_ack(struct rzv2m_i2c_priv *priv, u8 *data, + bool last) +{ + unsigned long time_left; + u32 data_tmp; + + reinit_completion(&priv->msg_tia_done); + + /* Interrupt request timing : 8th clock */ + bit_clrl(priv->base + IICB0CTL0, IICB0SLWT); + + /* Exit the wait state */ + writel(IICB0WRET, priv->base + IICB0TRG); + + /* Wait for transaction */ + time_left = wait_for_completion_timeout(&priv->msg_tia_done, + priv->adap.timeout); + if (!time_left) + return -ETIMEDOUT; + + if (last) { + /* Disable ACK */ + bit_clrl(priv->base + IICB0CTL0, IICB0SLAC); + + /* Read data*/ + data_tmp = readl(priv->base + IICB0DAT); + + /* Interrupt request timing : 9th clock */ + bit_setl(priv->base + IICB0CTL0, IICB0SLWT); + + /* Exit the wait state */ + writel(IICB0WRET, priv->base + IICB0TRG); + + /* Wait for transaction */ + time_left = wait_for_completion_timeout(&priv->msg_tia_done, + priv->adap.timeout); + if (!time_left) + return -ETIMEDOUT; + + /* Enable ACK */ + bit_setl(priv->base + IICB0CTL0, IICB0SLAC); + } else { + /* Read data */ + data_tmp = readl(priv->base + IICB0DAT); + } + + *data = data_tmp; + + return 0; +} + +static int rzv2m_i2c_send(struct rzv2m_i2c_priv *priv, struct i2c_msg *msg, + unsigned int *count) +{ + unsigned int i; + int ret; + + for (i = 0; i < msg->len; i++) { + ret = rzv2m_i2c_write_with_ack(priv, msg->buf[i]); + if (ret < 0) + return ret; + } + *count = i; + + return 0; +} + +static int rzv2m_i2c_receive(struct rzv2m_i2c_priv *priv, struct i2c_msg *msg, + unsigned int *count) +{ + unsigned int i; + int ret; + + for (i = 0; i < msg->len; i++) { + ret = rzv2m_i2c_read_with_ack(priv, &msg->buf[i], + (msg->len - 1) == i); + if (ret < 0) + return ret; + } + *count = i; + + return 0; +} + +static int rzv2m_i2c_send_address(struct rzv2m_i2c_priv *priv, + struct i2c_msg *msg) +{ + u32 addr; + int ret; + + if (msg->flags & I2C_M_TEN) { + /* + * 10-bit address + * addr_1: 5'b11110 | addr[9:8] | (R/nW) + * addr_2: addr[7:0] + */ + addr = 0xf0 | ((msg->addr & GENMASK(9, 8)) >> 7); + addr |= !!(msg->flags & I2C_M_RD); + /* Send 1st address(extend code) */ + ret = rzv2m_i2c_write_with_ack(priv, addr); + if (ret) + return ret; + + /* Send 2nd address */ + ret = rzv2m_i2c_write_with_ack(priv, msg->addr & 0xff); + } else { + /* 7-bit address */ + addr = i2c_8bit_addr_from_msg(msg); + ret = rzv2m_i2c_write_with_ack(priv, addr); + } + + return ret; +} + +static int rzv2m_i2c_stop_condition(struct rzv2m_i2c_priv *priv) +{ + u32 value; + + /* Send stop condition */ + writel(IICB0SPT, priv->base + IICB0TRG); + return readl_poll_timeout(priv->base + IICB0STR0, + value, value & IICB0SSSP, + 100, jiffies_to_usecs(priv->adap.timeout)); +} + +static int rzv2m_i2c_master_xfer_msg(struct rzv2m_i2c_priv *priv, + struct i2c_msg *msg, int stop) +{ + unsigned int count = 0; + int ret, read = !!(msg->flags & I2C_M_RD); + + /* Send start condition */ + writel(IICB0STT, priv->base + IICB0TRG); + + ret = rzv2m_i2c_send_address(priv, msg); + if (!ret) { + if (read) + ret = rzv2m_i2c_receive(priv, msg, &count); + else + ret = rzv2m_i2c_send(priv, msg, &count); + + if (!ret && stop) + ret = rzv2m_i2c_stop_condition(priv); + } + + if (ret == -ENXIO) + rzv2m_i2c_stop_condition(priv); + else if (ret < 0) + rzv2m_i2c_init(priv); + else + ret = count; + + return ret; +} + +static int rzv2m_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct rzv2m_i2c_priv *priv = i2c_get_adapdata(adap); + struct device *dev = priv->adap.dev.parent; + unsigned int i; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + if (readl(priv->base + IICB0STR0) & IICB0SSBS) { + ret = -EAGAIN; + goto out; + } + + /* I2C main transfer */ + for (i = 0; i < num; i++) { + ret = rzv2m_i2c_master_xfer_msg(priv, &msgs[i], i == (num - 1)); + if (ret < 0) + goto out; + } + ret = num; + +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static u32 rzv2m_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_adapter_quirks rzv2m_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +static struct i2c_algorithm rzv2m_i2c_algo = { + .master_xfer = rzv2m_i2c_master_xfer, + .functionality = rzv2m_i2c_func, +}; + +static int rzv2m_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rzv2m_i2c_priv *priv; + struct reset_control *rstc; + struct i2c_adapter *adap; + struct resource *res; + int irq, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Can't get clock\n"); + + rstc = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), "Missing reset ctrl\n"); + /* + * The reset also affects other HW that is not under the control + * of Linux. Therefore, all we can do is deassert the reset. + */ + reset_control_deassert(rstc); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, rzv2m_i2c_tia_irq_handler, 0, + dev_name(dev), priv); + if (ret < 0) + return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq); + + adap = &priv->adap; + adap->nr = pdev->id; + adap->algo = &rzv2m_i2c_algo; + adap->quirks = &rzv2m_i2c_quirks; + adap->dev.parent = dev; + adap->owner = THIS_MODULE; + device_set_node(&adap->dev, dev_fwnode(dev)); + i2c_set_adapdata(adap, priv); + strscpy(adap->name, pdev->name, sizeof(adap->name)); + init_completion(&priv->msg_tia_done); + + ret = rzv2m_i2c_clock_calculate(dev, priv); + if (ret < 0) + return ret; + + pm_runtime_enable(dev); + + pm_runtime_get_sync(dev); + rzv2m_i2c_init(priv); + pm_runtime_put(dev); + + platform_set_drvdata(pdev, priv); + + ret = i2c_add_numbered_adapter(adap); + if (ret < 0) + pm_runtime_disable(dev); + + return ret; +} + +static int rzv2m_i2c_remove(struct platform_device *pdev) +{ + struct rzv2m_i2c_priv *priv = platform_get_drvdata(pdev); + struct device *dev = priv->adap.dev.parent; + + i2c_del_adapter(&priv->adap); + bit_clrl(priv->base + IICB0CTL0, IICB0IICE); + pm_runtime_disable(dev); + + return 0; +} + +static int rzv2m_i2c_suspend(struct device *dev) +{ + struct rzv2m_i2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + bit_clrl(priv->base + IICB0CTL0, IICB0IICE); + pm_runtime_put(dev); + + return 0; +} + +static int rzv2m_i2c_resume(struct device *dev) +{ + struct rzv2m_i2c_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = rzv2m_i2c_clock_calculate(dev, priv); + if (ret < 0) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + rzv2m_i2c_init(priv); + pm_runtime_put(dev); + + return 0; +} + +static const struct of_device_id rzv2m_i2c_ids[] = { + { .compatible = "renesas,rzv2m-i2c" }, + { } +}; +MODULE_DEVICE_TABLE(of, rzv2m_i2c_ids); + +static const struct dev_pm_ops rzv2m_i2c_pm_ops = { + SYSTEM_SLEEP_PM_OPS(rzv2m_i2c_suspend, rzv2m_i2c_resume) +}; + +static struct platform_driver rzv2m_i2c_driver = { + .driver = { + .name = "rzv2m-i2c", + .of_match_table = rzv2m_i2c_ids, + .pm = pm_sleep_ptr(&rzv2m_i2c_pm_ops), + }, + .probe = rzv2m_i2c_probe, + .remove = rzv2m_i2c_remove, +}; +module_platform_driver(rzv2m_i2c_driver); + +MODULE_DESCRIPTION("RZ/V2M I2C bus driver"); +MODULE_AUTHOR("Renesas Electronics Corporation"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ac720e3e0e072092667b6d62a25611c2427fdb13 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 18 Jul 2022 21:06:10 -0700 Subject: i2c: brcmstb: Use dev_name() for adapter name This make it easier to disambiguate the different i2c controllers present in a system, and then correlating with /proc/interrupts allows to know which instance is interrupt driven and which one is not. Signed-off-by: Florian Fainelli Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-brcmstb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index b00f35c0b066..3ba6cbbe84ac 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -684,9 +684,7 @@ static int brcmstb_i2c_probe(struct platform_device *pdev) adap = &dev->adapter; i2c_set_adapdata(adap, dev); adap->owner = THIS_MODULE; - strlcpy(adap->name, "Broadcom STB : ", sizeof(adap->name)); - if (int_name) - strlcat(adap->name, int_name, sizeof(adap->name)); + strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); adap->algo = &brcmstb_i2c_algo; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; -- cgit v1.2.3 From 913ee46905ab48e1cae0dbd18de098f572ffa388 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sat, 16 Jul 2022 20:50:26 -0700 Subject: i2c: qcom-geni: Propagate GENI_ABORT_DONE to geni_i2c_abort_xfer() Waiting for M_CMD_ABORT_EN in geni_i2c_abort_xfer() races with the interrupt handler which will read and clear the abort bit, the result is that every abort attempt takes 1 second and is followed by a message about the abort having times out. Introduce a new state variable to carry the abort_done state from the interrupt handler back to geni_i2c_abort_xfer(). Also, turn NACK and TIMEOUT errors into debug messages Signed-off-by: Bjorn Andersson Reviewed-by: Vinod Koul [wsa: squashed two patches into one] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qcom-geni.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 6ac402ea58fb..c189b8fbc578 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -97,6 +97,7 @@ struct geni_i2c_dev { struct dma_chan *tx_c; struct dma_chan *rx_c; bool gpi_mode; + bool abort_done; }; struct geni_i2c_err_log { @@ -203,9 +204,18 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err) dev_dbg(gi2c->se.dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n", gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags); - if (err != NACK && err != GENI_ABORT_DONE) { + switch (err) { + case GENI_ABORT_DONE: + gi2c->abort_done = true; + break; + case NACK: + case GENI_TIMEOUT: + dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg); + break; + default: dev_err(gi2c->se.dev, "%s\n", gi2c_log[err].msg); geni_i2c_err_misc(gi2c); + break; } } @@ -311,21 +321,21 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c) { - u32 val; unsigned long time_left = ABORT_TIMEOUT; unsigned long flags; spin_lock_irqsave(&gi2c->lock, flags); geni_i2c_err(gi2c, GENI_TIMEOUT); gi2c->cur = NULL; + gi2c->abort_done = false; geni_se_abort_m_cmd(&gi2c->se); spin_unlock_irqrestore(&gi2c->lock, flags); + do { time_left = wait_for_completion_timeout(&gi2c->done, time_left); - val = readl_relaxed(gi2c->se.base + SE_GENI_M_IRQ_STATUS); - } while (!(val & M_CMD_ABORT_EN) && time_left); + } while (!gi2c->abort_done && time_left); - if (!(val & M_CMD_ABORT_EN)) + if (!time_left) dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n"); } -- cgit v1.2.3 From 9fdf6d97f03035ad5298e2d1635036c74c2090ed Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 17 Jul 2022 16:52:44 +0200 Subject: i2c: cadence: Support PEC for SMBus block read SMBus packet error checking (PEC) is implemented by appending one additional byte of checksum data at the end of the message. This provides additional protection and allows to detect data corruption on the I2C bus. SMBus block reads support variable length reads. The first byte in the read message is the number of available data bytes. The combination of PEC and block read is currently not supported by the Cadence I2C driver. * When PEC is enabled the maximum transfer length for block reads increases from 33 to 34 bytes. * The I2C core smbus emulation layer relies on the driver updating the `i2c_msg` `len` field with the number of received bytes. The updated length is used when checking the PEC. Add support to the Cadence I2C driver for handling SMBus block reads with PEC. To determine the maximum transfer length uses the initial `len` value of the `i2c_msg`. When PEC is enabled this will be 2, when it is disabled it will be 1. Once a read transfer is done also increment the `len` field by the amount of received data bytes. This change has been tested with a UCM90320 PMBus power monitor, which requires block reads to access certain data fields, but also has PEC enabled by default. Fixes: df8eb5691c48 ("i2c: Add driver for Cadence I2C controller") Signed-off-by: Lars-Peter Clausen Tested-by: Shubhrajyoti Datta Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cadence.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index b4c1ad19cdae..ae9199ad8873 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -593,8 +593,13 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id) ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET); ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO; + /* + * Receive up to I2C_SMBUS_BLOCK_MAX data bytes, plus one message length + * byte, plus one checksum byte if PEC is enabled. p_msg->len will be 2 if + * PEC is enabled, otherwise 1. + */ if (id->p_msg->flags & I2C_M_RECV_LEN) - id->recv_count = I2C_SMBUS_BLOCK_MAX + 1; + id->recv_count = I2C_SMBUS_BLOCK_MAX + id->p_msg->len; id->curr_recv_count = id->recv_count; @@ -809,6 +814,9 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg, if (id->err_status & CDNS_I2C_IXR_ARB_LOST) return -EAGAIN; + if (msg->flags & I2C_M_RECV_LEN) + msg->len += min_t(unsigned int, msg->buf[0], I2C_SMBUS_BLOCK_MAX); + return 0; } -- cgit v1.2.3 From b3f0ceb7c2037c6e3affd7d9c84ac5f97af7a5b5 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sat, 16 Jul 2022 20:50:25 -0700 Subject: i2c: qcom-geni: Use the correct return value The introduction of GPI support moved things around and instead of returning the result from geni_i2c_xfer() the number of messages in the request was returned, ignoring the actual result. Fix this. Fixes: d8703554f4de ("i2c: qcom-geni: Add support for GPI DMA") Signed-off-by: Bjorn Andersson Reviewed-by: Andrew Halaney Reviewed-by: Vinod Koul Reviewed-by: Johan Hovold Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qcom-geni.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index c189b8fbc578..6ac179a373ff 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -698,7 +698,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, pm_runtime_put_autosuspend(gi2c->se.dev); gi2c->cur = NULL; gi2c->err = 0; - return num; + return ret; } static u32 geni_i2c_func(struct i2c_adapter *adap) -- cgit v1.2.3 From 6435319c34704994e19b0767f6a4e6f37439867b Mon Sep 17 00:00:00 2001 From: Liang He Date: Fri, 22 Jul 2022 09:24:01 +0800 Subject: i2c: mux-gpmux: Add of_node_put() when breaking out of loop In i2c_mux_probe(), we should call of_node_put() when breaking out of for_each_child_of_node() which will automatically increase and decrease the refcount. Fixes: ac8498f0ce53 ("i2c: i2c-mux-gpmux: new driver") Signed-off-by: Liang He Acked-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-gpmux.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c index d3acd8d66c32..33024acaac02 100644 --- a/drivers/i2c/muxes/i2c-mux-gpmux.c +++ b/drivers/i2c/muxes/i2c-mux-gpmux.c @@ -134,6 +134,7 @@ static int i2c_mux_probe(struct platform_device *pdev) return 0; err_children: + of_node_put(child); i2c_mux_del_adapters(muxc); err_parent: i2c_put_adapter(parent); -- cgit v1.2.3