Skip to content

Commit

Permalink
bus: ti-sysc: Add quirk handling for reinit on context lost
Browse files Browse the repository at this point in the history
Some interconnect target modules such as otg and gpmc on am335x need a
re-init after resume. As we also have PM runtime cases where the context
may be lost, let's handle these all with cpu_pm.

For the am335x resume path, we already have cpu_pm_resume() call
cpu_pm_cluster_exit().

Signed-off-by: Tony Lindgren <[email protected]>
  • Loading branch information
tmlind committed Sep 21, 2021
1 parent 95ec14f commit 9d88136
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 6 deletions.
108 changes: 102 additions & 6 deletions drivers/bus/ti-sysc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/module.h>
Expand Down Expand Up @@ -52,11 +53,18 @@ struct sysc_address {
struct list_head node;
};

struct sysc_module {
struct sysc *ddata;
struct list_head node;
};

struct sysc_soc_info {
unsigned long general_purpose:1;
enum sysc_soc soc;
struct mutex list_lock; /* disabled modules list lock */
struct mutex list_lock; /* disabled and restored modules list lock */
struct list_head disabled_modules;
struct list_head restored_modules;
struct notifier_block nb;
};

enum sysc_clocks {
Expand Down Expand Up @@ -2477,6 +2485,79 @@ static struct dev_pm_domain sysc_child_pm_domain = {
}
};

/* Caller needs to take list_lock if ever used outside of cpu_pm */
static void sysc_reinit_modules(struct sysc_soc_info *soc)
{
struct sysc_module *module;
struct list_head *pos;
struct sysc *ddata;
int error = 0;

list_for_each(pos, &sysc_soc->restored_modules) {
module = list_entry(pos, struct sysc_module, node);
ddata = module->ddata;
error = sysc_reinit_module(ddata, ddata->enabled);
}
}

/**
* sysc_context_notifier - optionally reset and restore module after idle
* @nb: notifier block
* @cmd: unused
* @v: unused
*
* Some interconnect target modules need to be restored, or reset and restored
* on CPU_PM CPU_PM_CLUSTER_EXIT notifier. This is needed at least for am335x
* OTG and GPMC target modules even if the modules are unused.
*/
static int sysc_context_notifier(struct notifier_block *nb, unsigned long cmd,
void *v)
{
struct sysc_soc_info *soc;

soc = container_of(nb, struct sysc_soc_info, nb);

switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
break;
case CPU_CLUSTER_PM_ENTER_FAILED: /* No need to restore context */
break;
case CPU_CLUSTER_PM_EXIT:
sysc_reinit_modules(soc);
break;
}

return NOTIFY_OK;
}

/**
* sysc_add_restored - optionally add reset and restore quirk hanlling
* @ddata: device data
*/
static void sysc_add_restored(struct sysc *ddata)
{
struct sysc_module *restored_module;

restored_module = kzalloc(sizeof(*restored_module), GFP_KERNEL);
if (!restored_module)
return;

restored_module->ddata = ddata;

mutex_lock(&sysc_soc->list_lock);

list_add(&restored_module->node, &sysc_soc->restored_modules);

if (sysc_soc->nb.notifier_call)
goto out_unlock;

sysc_soc->nb.notifier_call = sysc_context_notifier;
cpu_pm_register_notifier(&sysc_soc->nb);

out_unlock:
mutex_unlock(&sysc_soc->list_lock);
}

/**
* sysc_legacy_idle_quirk - handle children in omap_device compatible way
* @ddata: device driver data
Expand Down Expand Up @@ -2976,12 +3057,14 @@ static int sysc_add_disabled(unsigned long base)
}

/*
* One time init to detect the booted SoC and disable unavailable features.
* One time init to detect the booted SoC, disable unavailable features
* and initialize list for optional cpu_pm notifier.
*
* Note that we initialize static data shared across all ti-sysc instances
* so ddata is only used for SoC type. This can be called from module_init
* once we no longer need to rely on platform data.
*/
static int sysc_init_soc(struct sysc *ddata)
static int sysc_init_static_data(struct sysc *ddata)
{
const struct soc_device_attribute *match;
struct ti_sysc_platform_data *pdata;
Expand All @@ -2997,6 +3080,7 @@ static int sysc_init_soc(struct sysc *ddata)

mutex_init(&sysc_soc->list_lock);
INIT_LIST_HEAD(&sysc_soc->disabled_modules);
INIT_LIST_HEAD(&sysc_soc->restored_modules);
sysc_soc->general_purpose = true;

pdata = dev_get_platdata(ddata->dev);
Expand Down Expand Up @@ -3060,15 +3144,24 @@ static int sysc_init_soc(struct sysc *ddata)
return 0;
}

static void sysc_cleanup_soc(void)
static void sysc_cleanup_static_data(void)
{
struct sysc_module *restored_module;
struct sysc_address *disabled_module;
struct list_head *pos, *tmp;

if (!sysc_soc)
return;

if (sysc_soc->nb.notifier_call)
cpu_pm_unregister_notifier(&sysc_soc->nb);

mutex_lock(&sysc_soc->list_lock);
list_for_each_safe(pos, tmp, &sysc_soc->restored_modules) {
restored_module = list_entry(pos, struct sysc_module, node);
list_del(pos);
kfree(restored_module);
}
list_for_each_safe(pos, tmp, &sysc_soc->disabled_modules) {
disabled_module = list_entry(pos, struct sysc_address, node);
list_del(pos);
Expand Down Expand Up @@ -3136,7 +3229,7 @@ static int sysc_probe(struct platform_device *pdev)
ddata->dev = &pdev->dev;
platform_set_drvdata(pdev, ddata);

error = sysc_init_soc(ddata);
error = sysc_init_static_data(ddata);
if (error)
return error;

Expand Down Expand Up @@ -3234,6 +3327,9 @@ static int sysc_probe(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
}

if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_CTX_LOST)
sysc_add_restored(ddata);

return 0;

err:
Expand Down Expand Up @@ -3315,7 +3411,7 @@ static void __exit sysc_exit(void)
{
bus_unregister_notifier(&platform_bus_type, &sysc_nb);
platform_driver_unregister(&sysc_driver);
sysc_cleanup_soc();
sysc_cleanup_static_data();
}
module_exit(sysc_exit);

Expand Down
1 change: 1 addition & 0 deletions include/linux/platform_data/ti-sysc.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct sysc_regbits {
s8 emufree_shift;
};

#define SYSC_QUIRK_REINIT_ON_CTX_LOST BIT(28)
#define SYSC_QUIRK_REINIT_ON_RESUME BIT(27)
#define SYSC_QUIRK_GPMC_DEBUG BIT(26)
#define SYSC_MODULE_QUIRK_ENA_RESETDONE BIT(25)
Expand Down

0 comments on commit 9d88136

Please sign in to comment.