We can implement reset control framework for ti-sysc for the connected devices to use for the interconnect target reset. Signed-off-by: Tony Lindgren <tony@xxxxxxxxxxx> --- drivers/bus/ti-sysc.c | 109 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -25,6 +25,7 @@ #include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/reset.h> +#include <linux/reset-controller.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/slab.h> @@ -42,6 +43,7 @@ #define SOC_FLAG(match, flag) { .machine = match, .data = (void *)(flag), } +#define TI_SYSC_SOFTRESET_ID 0 #define MAX_MODULE_SOFTRESET_WAIT 10000 enum sysc_soc { @@ -79,6 +81,11 @@ struct sysc_soc_info { struct notifier_block nb; }; +struct sysc_reset_lookup { + struct list_head node; + struct reset_control_lookup lookup; +}; + enum sysc_clocks { SYSC_FCK, SYSC_ICK, @@ -147,6 +154,9 @@ struct sysc { const char **clock_roles; int nr_clocks; struct reset_control *rsts; + struct reset_controller_dev rcdev; + struct list_head child_resets; + struct mutex child_lock; /* child device data list lock */ const char *legacy_mode; const struct sysc_capabilities *cap; struct sysc_config cfg; @@ -2194,6 +2204,46 @@ static int sysc_reset(struct sysc *ddata) return error; } +/* + * Only handles the softreset for the interconnect target, does not consider + * the device specific external resets. We must ensure the interconnect target + * is runtime PM active for the reset. And we must restore the sysconfig + * register after reset. Locking is currently not needed as we only touch the + * sysconfig register on PM runtime state changes, and no other sysconfig + * register access happens when the interconnect target is runtime PM active. + * Interconnect targets with multiple children must coordinate the reset + * usage with reset_control_get_shared(). + */ +static int ti_sysc_reset_control_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct sysc *ddata; + int error; + + if (id != TI_SYSC_SOFTRESET_ID) + return -EINVAL; + + ddata = container_of(rcdev, struct sysc, rcdev); + + error = pm_runtime_resume_and_get(ddata->dev); + if (error < 0) + return error; + + error = sysc_reset(ddata); + if (error) + dev_warn(ddata->dev, "reset failed: %i\n", error); + + sysc_write_sysconfig(ddata, ddata->sysconfig); + + pm_runtime_put_sync(ddata->dev); + + return error; +} + +static const struct reset_control_ops ti_sysc_reset_ops = { + .reset = ti_sysc_reset_control_reset, +}; + /* * At this point the module is configured enough to read the revision but * module may not be completely configured yet to use PM runtime. Enable @@ -2408,6 +2458,45 @@ static int sysc_child_add_clocks(struct sysc *ddata, return 0; } +static int sysc_child_add_reset(struct sysc *ddata, + struct device *child) +{ + struct sysc_reset_lookup *srl; + + srl = kzalloc(sizeof(*srl), GFP_KERNEL); + if (!srl) + return -ENOMEM; + + srl->lookup.provider = dev_name(ddata->dev); + srl->lookup.index = TI_SYSC_SOFTRESET_ID; + srl->lookup.dev_id = dev_name(child); + srl->lookup.con_id = "softreset"; + reset_controller_add_lookup(&srl->lookup, 1); + mutex_lock(&ddata->child_lock); + list_add(&srl->node, &ddata->child_resets); + mutex_unlock(&ddata->child_lock); + + return 0; +} + +static void sysc_child_remove_reset(struct sysc *ddata, + struct device *child) +{ + struct sysc_reset_lookup *srl; + + mutex_lock(&ddata->child_lock); + list_for_each_entry(srl, &ddata->child_resets, node) { + if (srl->lookup.index == TI_SYSC_SOFTRESET_ID && + !strcmp(dev_name(child), srl->lookup.dev_id)) { + reset_controller_remove_lookup(&srl->lookup, 1); + list_del(&srl->node); + kfree(srl); + break; + } + } + mutex_unlock(&ddata->child_lock); +} + static const struct device_type sysc_device_type = { }; @@ -2541,6 +2630,14 @@ static int sysc_notifier_call(struct notifier_block *nb, error = sysc_child_add_clocks(ddata, dev); if (error) return error; + + error = sysc_child_add_reset(ddata, dev); + if (error) + return error; + + break; + case BUS_NOTIFY_REMOVED_DEVICE: + sysc_child_remove_reset(ddata, dev); break; default: break; @@ -3186,6 +3283,8 @@ static int sysc_probe(struct platform_device *pdev) ddata->offsets[SYSC_SYSCONFIG] = -ENODEV; ddata->offsets[SYSC_SYSSTATUS] = -ENODEV; ddata->dev = &pdev->dev; + mutex_init(&ddata->child_lock); + INIT_LIST_HEAD(&ddata->child_resets); platform_set_drvdata(pdev, ddata); error = sysc_init_static_data(ddata); @@ -3266,6 +3365,16 @@ static int sysc_probe(struct platform_device *pdev) ddata->dev->type = &sysc_device_type; + ddata->rcdev.owner = THIS_MODULE; + ddata->rcdev.nr_resets = 1; + ddata->rcdev.ops = &ti_sysc_reset_ops; + ddata->rcdev.dev = &pdev->dev; + ddata->rcdev.of_node = ddata->dev->of_node; + + error = devm_reset_controller_register(ddata->dev, &ddata->rcdev); + if (error) + goto err; + if (!ddata->reserved) { error = of_platform_populate(ddata->dev->of_node, sysc_match_table, -- 2.44.0