From 88c50322449a4dea8687ed155968d19cfc763393 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gaj Date: Tue, 16 Apr 2019 09:36:14 +0100 Subject: i3c: Drop support for I2C 10 bit addresing This patch drops support for I2C devices with 10 bit addressing. When I2C device with 10 bit address is defined in DT, I3C master registration fails. Address space for I2C devices has been reduced and ->i2c_funcs() hook has been removed. Because this patch series dropped support for 10 bit I2C devices, support is also dropped in Cadence I3C master driver and Synopsys DesignWare I3C master driver. Signed-off-by: Przemyslaw Gaj Signed-off-by: Boris Brezillon --- drivers/i3c/master.c | 21 ++++++++++++++------- drivers/i3c/master/dw-i3c-master.c | 6 ------ drivers/i3c/master/i3c-master-cdns.c | 10 +--------- 3 files changed, 15 insertions(+), 22 deletions(-) (limited to 'drivers/i3c') diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 5f4bd52121fe..b9d2b88928e1 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1963,6 +1963,16 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, if (ret) return ret; + /* + * The I3C Specification does not clearly say I2C devices with 10-bit + * address are supported. These devices can't be passed properly through + * DEFSLVS command. + */ + if (boardinfo->base.flags & I2C_CLIENT_TEN) { + dev_err(&master->dev, "I2C device with 10 bit address not supported."); + return -ENOTSUPP; + } + /* LVR is encoded in reg[2]. */ boardinfo->lvr = reg[2]; @@ -2111,16 +2121,14 @@ static int i3c_master_i2c_adapter_xfer(struct i2c_adapter *adap, return ret ? ret : nxfers; } -static u32 i3c_master_i2c_functionalities(struct i2c_adapter *adap) +static u32 i3c_master_i2c_funcs(struct i2c_adapter *adapter) { - struct i3c_master_controller *master = i2c_adapter_to_i3c_master(adap); - - return master->ops->i2c_funcs(master); + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; } static const struct i2c_algorithm i3c_master_i2c_algo = { .master_xfer = i3c_master_i2c_adapter_xfer, - .functionality = i3c_master_i2c_functionalities, + .functionality = i3c_master_i2c_funcs, }; static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master) @@ -2379,8 +2387,7 @@ EXPORT_SYMBOL_GPL(i3c_generic_ibi_recycle_slot); static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops) { if (!ops || !ops->bus_init || !ops->priv_xfers || - !ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers || - !ops->i2c_funcs) + !ops->send_ccc_cmd || !ops->do_daa || !ops->i2c_xfers) return -EINVAL; if (ops->request_ibi && diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 1d83c97431c7..d6e517e69f84 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1060,11 +1060,6 @@ static void dw_i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev) kfree(data); } -static u32 dw_i3c_master_i2c_funcs(struct i3c_master_controller *m) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id) { struct dw_i3c_master *master = dev_id; @@ -1099,7 +1094,6 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = { .attach_i2c_dev = dw_i3c_master_attach_i2c_dev, .detach_i2c_dev = dw_i3c_master_detach_i2c_dev, .i2c_xfers = dw_i3c_master_i2c_xfers, - .i2c_funcs = dw_i3c_master_i2c_funcs, }; static int dw_i3c_probe(struct platform_device *pdev) diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 8889a4fdb454..237f24adddc6 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -864,11 +864,6 @@ static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, return ret; } -static u32 cdns_i3c_master_i2c_funcs(struct i3c_master_controller *m) -{ - return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; -} - struct cdns_i3c_i2c_dev_data { u16 id; s16 ibi; @@ -1010,9 +1005,7 @@ static int cdns_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev) master->free_rr_slots &= ~BIT(slot); i2c_dev_set_master_data(dev, data); - writel(prepare_rr0_dev_address(dev->boardinfo->base.addr) | - (dev->boardinfo->base.flags & I2C_CLIENT_TEN ? - DEV_ID_RR0_LVR_EXT_ADDR : 0), + writel(prepare_rr0_dev_address(dev->boardinfo->base.addr), master->regs + DEV_ID_RR0(data->id)); writel(dev->boardinfo->lvr, master->regs + DEV_ID_RR2(data->id)); writel(readl(master->regs + DEVS_CTRL) | @@ -1518,7 +1511,6 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = { .send_ccc_cmd = cdns_i3c_master_send_ccc_cmd, .priv_xfers = cdns_i3c_master_priv_xfers, .i2c_xfers = cdns_i3c_master_i2c_xfers, - .i2c_funcs = cdns_i3c_master_i2c_funcs, .enable_ibi = cdns_i3c_master_enable_ibi, .disable_ibi = cdns_i3c_master_disable_ibi, .request_ibi = cdns_i3c_master_request_ibi, -- cgit v1.2.3-70-g09d2 From ecc8fb54bd443bf69996d9d5ddb8d90a50f14936 Mon Sep 17 00:00:00 2001 From: Vitor Soares Date: Wed, 19 Jun 2019 20:36:31 +0200 Subject: i3c: fix i2c and i3c scl rate by bus mode Currently the I3C framework limits SCL frequency to FM speed when dealing with a mixed slow bus, even if all I2C devices are FM+ capable. The core was also not accounting for I3C speed limitations when operating in mixed slow mode and was erroneously using FM+ speed as the max I2C speed when operating in mixed fast mode. Fixes: 3a379bbcea0a ("i3c: Add core I3C infrastructure") Signed-off-by: Vitor Soares Cc: Boris Brezillon Cc: Cc: Signed-off-by: Boris Brezillon --- drivers/i3c/master.c | 51 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) (limited to 'drivers/i3c') diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index b9d2b88928e1..54026be03998 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -91,6 +91,12 @@ void i3c_bus_normaluse_unlock(struct i3c_bus *bus) up_read(&bus->lock); } +static struct i3c_master_controller * +i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) +{ + return container_of(i3cbus, struct i3c_master_controller, bus); +} + static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev) { return container_of(dev, struct i3c_master_controller, dev); @@ -565,20 +571,38 @@ static const struct device_type i3c_masterdev_type = { .groups = i3c_masterdev_groups, }; -int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode) +int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode, + unsigned long max_i2c_scl_rate) { - i3cbus->mode = mode; + struct i3c_master_controller *master = i3c_bus_to_i3c_master(i3cbus); - if (!i3cbus->scl_rate.i3c) - i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; + i3cbus->mode = mode; - if (!i3cbus->scl_rate.i2c) { - if (i3cbus->mode == I3C_BUS_MODE_MIXED_SLOW) - i3cbus->scl_rate.i2c = I3C_BUS_I2C_FM_SCL_RATE; - else - i3cbus->scl_rate.i2c = I3C_BUS_I2C_FM_PLUS_SCL_RATE; + switch (i3cbus->mode) { + case I3C_BUS_MODE_PURE: + if (!i3cbus->scl_rate.i3c) + i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; + break; + case I3C_BUS_MODE_MIXED_FAST: + if (!i3cbus->scl_rate.i3c) + i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; + if (!i3cbus->scl_rate.i2c) + i3cbus->scl_rate.i2c = max_i2c_scl_rate; + break; + case I3C_BUS_MODE_MIXED_SLOW: + if (!i3cbus->scl_rate.i2c) + i3cbus->scl_rate.i2c = max_i2c_scl_rate; + if (!i3cbus->scl_rate.i3c || + i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c) + i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c; + break; + default: + return -EINVAL; } + dev_dbg(&master->dev, "i2c-scl = %ld Hz i3c-scl = %ld Hz\n", + i3cbus->scl_rate.i2c, i3cbus->scl_rate.i3c); + /* * I3C/I2C frequency may have been overridden, check that user-provided * values are not exceeding max possible frequency. @@ -1976,9 +2000,6 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, /* LVR is encoded in reg[2]. */ boardinfo->lvr = reg[2]; - if (boardinfo->lvr & I3C_LVR_I2C_FM_MODE) - master->bus.scl_rate.i2c = I3C_BUS_I2C_FM_SCL_RATE; - list_add_tail(&boardinfo->node, &master->boardinfo.i2c); of_node_get(node); @@ -2424,6 +2445,7 @@ int i3c_master_register(struct i3c_master_controller *master, const struct i3c_master_controller_ops *ops, bool secondary) { + unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_RATE; struct i3c_bus *i3cbus = i3c_master_get_bus(master); enum i3c_bus_mode mode = I3C_BUS_MODE_PURE; struct i2c_dev_boardinfo *i2cbi; @@ -2473,9 +2495,12 @@ int i3c_master_register(struct i3c_master_controller *master, ret = -EINVAL; goto err_put_dev; } + + if (i2cbi->lvr & I3C_LVR_I2C_FM_MODE) + i2c_scl_rate = I3C_BUS_I2C_FM_SCL_RATE; } - ret = i3c_bus_set_mode(i3cbus, mode); + ret = i3c_bus_set_mode(i3cbus, mode, i2c_scl_rate); if (ret) goto err_put_dev; -- cgit v1.2.3-70-g09d2 From cbf4f7325a638ced1d815580dfed44ea3b76163c Mon Sep 17 00:00:00 2001 From: Vitor Soares Date: Wed, 19 Jun 2019 20:36:32 +0200 Subject: i3c: add mixed limited bus mode The i3c bus spec defines a bus configuration where i2c devices don't have a 50ns filter but support SCL running at SDR max rate (12.5MHz). This patch introduces the limited bus mode so that users can use a higher speed in presence of i2c devices index 1. Signed-off-by: Vitor Soares Cc: Boris Brezillon Cc: Signed-off-by: Boris Brezillon --- drivers/i3c/master.c | 5 +++++ include/linux/i3c/master.h | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'drivers/i3c') diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 54026be03998..1318adfe0216 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -470,6 +470,7 @@ static int i3c_bus_init(struct i3c_bus *i3cbus) static const char * const i3c_bus_mode_strings[] = { [I3C_BUS_MODE_PURE] = "pure", [I3C_BUS_MODE_MIXED_FAST] = "mixed-fast", + [I3C_BUS_MODE_MIXED_LIMITED] = "mixed-limited", [I3C_BUS_MODE_MIXED_SLOW] = "mixed-slow", }; @@ -584,6 +585,7 @@ int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode, i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; break; case I3C_BUS_MODE_MIXED_FAST: + case I3C_BUS_MODE_MIXED_LIMITED: if (!i3cbus->scl_rate.i3c) i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE; if (!i3cbus->scl_rate.i2c) @@ -2487,6 +2489,9 @@ int i3c_master_register(struct i3c_master_controller *master, mode = I3C_BUS_MODE_MIXED_FAST; break; case I3C_LVR_I2C_INDEX(1): + if (mode < I3C_BUS_MODE_MIXED_LIMITED) + mode = I3C_BUS_MODE_MIXED_LIMITED; + break; case I3C_LVR_I2C_INDEX(2): if (mode < I3C_BUS_MODE_MIXED_SLOW) mode = I3C_BUS_MODE_MIXED_SLOW; diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index eca8337bdaa5..1f08fa8d69d2 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -250,12 +250,17 @@ struct i3c_device { * the bus. The only impact in this mode is that the * high SCL pulse has to stay below 50ns to trick I2C * devices when transmitting I3C frames + * @I3C_BUS_MODE_MIXED_LIMITED: I2C devices without 50ns spike filter are + * present on the bus. However they allow + * compliance up to the maximum SDR SCL clock + * frequency. * @I3C_BUS_MODE_MIXED_SLOW: I2C devices without 50ns spike filter are present * on the bus */ enum i3c_bus_mode { I3C_BUS_MODE_PURE, I3C_BUS_MODE_MIXED_FAST, + I3C_BUS_MODE_MIXED_LIMITED, I3C_BUS_MODE_MIXED_SLOW, }; -- cgit v1.2.3-70-g09d2 From f467907c1c38575c8d936d012957b19a69538249 Mon Sep 17 00:00:00 2001 From: Vitor Soares Date: Wed, 19 Jun 2019 20:36:33 +0200 Subject: i3c: dw: add limited bus mode support This patch add limited bus mode support for DesignWare i3c master Signed-off-by: Vitor Soares Cc: Boris Brezillon Cc: Signed-off-by: Boris Brezillon --- drivers/i3c/master/dw-i3c-master.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/i3c') diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index d6e517e69f84..09912d75c6d5 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -599,6 +599,7 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) switch (bus->mode) { case I3C_BUS_MODE_MIXED_FAST: + case I3C_BUS_MODE_MIXED_LIMITED: ret = dw_i2c_clk_cfg(master); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From ede2001569c32e5bafd2203c7272bbd3249e942e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 31 May 2019 12:35:32 -0500 Subject: i3c: master: Use struct_size() helper Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes, in particular in the context in which this code is being used. So, replace the following form: sizeof(*defslvs) + ((ndevs - 1) * sizeof(struct i3c_ccc_dev_desc)) with: struct_size(defslvs, slaves, ndevs - 1) This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Boris Brezillon --- drivers/i3c/master.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/i3c') diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 1318adfe0216..d6f8b038a896 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -950,9 +950,8 @@ int i3c_master_defslvs_locked(struct i3c_master_controller *master) ndevs++; defslvs = i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, - sizeof(*defslvs) + - ((ndevs - 1) * - sizeof(struct i3c_ccc_dev_desc))); + struct_size(defslvs, slaves, + ndevs - 1)); if (!defslvs) return -ENOMEM; -- cgit v1.2.3-70-g09d2