Messages in this thread Patch in this message | | | From | Parshuram Thombare <> | Subject | [PATCH v6 6/8] i3c: master: secondary master initialization | Date | Fri, 17 Apr 2020 18:22:35 +0200 |
| |
This patch add secondary master support to I3C master subsystem.
Signed-off-by: Parshuram Thombare <pthombar@cadence.com> --- drivers/i3c/master.c | 153 ++++++++++++++++++++++++++++--------- include/linux/i3c/master.h | 1 + 2 files changed, 116 insertions(+), 38 deletions(-)
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 19d4800ed573..39a412b32c59 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1621,10 +1621,6 @@ int i3c_master_set_info(struct i3c_master_controller *master, if (!i3c_bus_dev_addr_is_avail(&master->bus, info->dyn_addr)) return -EINVAL; - if (I3C_BCR_DEVICE_ROLE(info->bcr) == I3C_BCR_I3C_MASTER && - master->secondary) - return -EINVAL; - if (master->this) return -EINVAL; @@ -2040,9 +2036,10 @@ EXPORT_SYMBOL_GPL(i3c_master_process_defslvs); * 1. Attach I2C and statically defined I3C devs to the master so that the * master can fill its internal device table appropriately * - * 2. Call &i3c_master_controller_ops->bus_init() method to initialize - * the master controller. That's usually where the bus mode is selected - * (pure bus or mixed fast/slow bus) + * 2. Should have called &i3c_master_controller_ops->bus_init() + * method with pure bus mode to initialize the master controller. + * That's usually where the bus mode is selected (pure bus or + * mixed fast/slow bus) * * 3. Instruct all devices on the bus to drop their dynamic address. This is * particularly important when the bus was previously configured by someone @@ -2126,14 +2123,6 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) } } - /* - * Now execute the controller specific ->bus_init() routine, which - * might configure its internal logic to match the bus limitations. - */ - ret = master->ops->bus_init(master); - if (ret) - goto err_detach_devs; - ret = master->ops->master_set_info(master); if (ret) goto err_detach_devs; @@ -2146,7 +2135,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) dev_err(&master->dev, "master_set_info() was not called in ->bus_init()\n"); ret = -EINVAL; - goto err_bus_cleanup; + goto err_detach_devs; } /* @@ -2155,14 +2144,14 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) */ ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); if (ret && ret != I3C_ERROR_M2) - goto err_bus_cleanup; + goto err_detach_devs; /* Disable all slave events before starting DAA. */ ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR, I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ); if (ret && ret != I3C_ERROR_M2) - goto err_bus_cleanup; + goto err_detach_devs; /* * Pre-assign dynamic address and retrieve device information if @@ -2180,10 +2169,6 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) err_rstdaa: i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); -err_bus_cleanup: - if (master->ops->bus_cleanup) - master->ops->bus_cleanup(master); - err_detach_devs: i3c_master_detach_free_devs(master); @@ -2813,9 +2798,68 @@ static int i3c_master_check_ops(const struct i3c_master_controller_ops *ops) !ops->recycle_ibi_slot)) return -EINVAL; + if (ops->request_mastership && + (!ops->enable_mr_events || !ops->disable_mr_events || + !ops->check_event_set)) + return -EINVAL; + return 0; } +static void i3c_secondary_master_register(struct work_struct *work) +{ + struct i3c_master_controller *master; + struct i3c_bus *i3cbus; + int ret; + + master = container_of(work, struct i3c_master_controller, + sec_mst_register_work); + i3cbus = i3c_master_get_bus(master); + + i3c_bus_normaluse_lock(&master->bus); + if (!master->ops->check_event_set(master, I3C_SLV_DEFSLVS_CCC) || + i3c_master_acquire_bus(master)) { + i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, work); + return; + } + i3c_bus_normaluse_unlock(&master->bus); + + ret = device_add(&master->dev); + if (ret) + goto err_cleanup_bus; + + /* + * Expose our I3C bus as an I2C adapter so that I2C devices are exposed + * through the I2C subsystem. + */ + ret = i3c_master_i2c_adapter_init(master); + if (ret) + goto err_del_dev; + + /* + * We're done initializing the bus and the controller, we can now + * register I3C devices from defslvs list. + */ + master->init_done = true; + i3c_bus_normaluse_lock(&master->bus); + i3c_master_register_new_i3c_devs(master); + i3c_bus_normaluse_unlock(&master->bus); + + i3c_master_enable_mr_events(master); + + return; + +err_del_dev: + device_del(&master->dev); + +err_cleanup_bus: + if (master->ops->bus_cleanup) + master->ops->bus_cleanup(master); + + put_device(&master->dev); +} + /** * i3c_master_register() - register an I3C master * @master: master used to send frames on the bus @@ -2845,10 +2889,10 @@ int i3c_master_register(struct i3c_master_controller *master, struct i3c_bus *i3cbus = i3c_master_get_bus(master); enum i3c_bus_mode mode = I3C_BUS_MODE_PURE; struct i2c_dev_boardinfo *i2cbi; - int ret; + int ret, sz; - /* We do not support secondary masters yet. */ - if (secondary) + /*Check if controller driver supports secondary masters. */ + if (secondary && !ops->request_mastership) return -ENOTSUPP; ret = i3c_master_check_ops(ops); @@ -2872,13 +2916,45 @@ int i3c_master_register(struct i3c_master_controller *master, device_initialize(&master->dev); dev_set_name(&master->dev, "i3c-%d", i3cbus->id); + master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent)); + if (!master->wq) { + ret = -ENOMEM; + goto err_put_dev; + } + + master->mr_state = I3C_MR_IDLE; INIT_WORK(&master->sec_mst_work, i3c_sec_mst_acquire_bus); INIT_WORK(&master->mst_work, i3c_mst_yield_bus); - ret = of_populate_i3c_bus(master); + ret = i3c_bus_set_mode(i3cbus, mode); + if (ret) + goto err_put_dev; + + /* + * Now execute the controller specific ->bus_init() routine, which + * might configure its internal logic to match the bus limitations. + */ + ret = master->ops->bus_init(master); if (ret) goto err_put_dev; + if (secondary) { + sz = sizeof(struct i3c_ccc_dev_desc) * I3C_BUS_MAX_DEVS; + master->defslvs_data.devs = devm_kzalloc(&master->dev, sz, + GFP_KERNEL); + if (!master->defslvs_data.devs) + goto err_put_dev; + + INIT_WORK(&master->sec_mst_register_work, + i3c_secondary_master_register); + queue_work(master->wq, &master->sec_mst_register_work); + return 0; + } + + ret = of_populate_i3c_bus(master); + if (ret) + goto err_cleanup_bus; + list_for_each_entry(i2cbi, &master->boardinfo.i2c, node) { switch (i2cbi->lvr & I3C_LVR_I2C_INDEX_MASK) { case I3C_LVR_I2C_INDEX(0): @@ -2892,23 +2968,13 @@ int i3c_master_register(struct i3c_master_controller *master, break; default: ret = -EINVAL; - goto err_put_dev; + goto err_cleanup_bus; } } - ret = i3c_bus_set_mode(i3cbus, mode); - if (ret) - goto err_put_dev; - - master->wq = alloc_workqueue("%s", 0, 0, dev_name(parent)); - if (!master->wq) { - ret = -ENOMEM; - goto err_put_dev; - } - ret = i3c_master_bus_init(master); if (ret) - goto err_put_dev; + goto err_cleanup_bus; ret = device_add(&master->dev); if (ret) @@ -2931,6 +2997,17 @@ int i3c_master_register(struct i3c_master_controller *master, i3c_master_register_new_i3c_devs(master); i3c_bus_normaluse_unlock(&master->bus); + ret = i3c_bus_set_mode(i3cbus, mode); + if (ret) + goto err_del_dev; + + ret = master->ops->bus_init(master); + if (ret) + goto err_del_dev; + + if (ops->request_mastership) + i3c_master_enable_mr_events(master); + return 0; err_del_dev: diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index cc482934803b..08b63e285b7b 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -515,6 +515,7 @@ struct i3c_master_controller { } boardinfo; struct i3c_bus bus; struct workqueue_struct *wq; + struct work_struct sec_mst_register_work; struct work_struct mst_work; struct work_struct sec_mst_work; struct completion mr_comp; -- 2.17.1
| |