bus: ti-sysc: Add quirk handling for reinit on context lost
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 <tony@atomide.com>
This commit is contained in:
parent
95ec14faac
commit
9d88136120
@ -6,6 +6,7 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clkdev.h>
|
#include <linux/clkdev.h>
|
||||||
|
#include <linux/cpu_pm.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@ -52,11 +53,18 @@ struct sysc_address {
|
|||||||
struct list_head node;
|
struct list_head node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sysc_module {
|
||||||
|
struct sysc *ddata;
|
||||||
|
struct list_head node;
|
||||||
|
};
|
||||||
|
|
||||||
struct sysc_soc_info {
|
struct sysc_soc_info {
|
||||||
unsigned long general_purpose:1;
|
unsigned long general_purpose:1;
|
||||||
enum sysc_soc soc;
|
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 disabled_modules;
|
||||||
|
struct list_head restored_modules;
|
||||||
|
struct notifier_block nb;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sysc_clocks {
|
enum sysc_clocks {
|
||||||
@ -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
|
* sysc_legacy_idle_quirk - handle children in omap_device compatible way
|
||||||
* @ddata: device driver data
|
* @ddata: device driver data
|
||||||
@ -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
|
* 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
|
* 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.
|
* 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;
|
const struct soc_device_attribute *match;
|
||||||
struct ti_sysc_platform_data *pdata;
|
struct ti_sysc_platform_data *pdata;
|
||||||
@ -2997,6 +3080,7 @@ static int sysc_init_soc(struct sysc *ddata)
|
|||||||
|
|
||||||
mutex_init(&sysc_soc->list_lock);
|
mutex_init(&sysc_soc->list_lock);
|
||||||
INIT_LIST_HEAD(&sysc_soc->disabled_modules);
|
INIT_LIST_HEAD(&sysc_soc->disabled_modules);
|
||||||
|
INIT_LIST_HEAD(&sysc_soc->restored_modules);
|
||||||
sysc_soc->general_purpose = true;
|
sysc_soc->general_purpose = true;
|
||||||
|
|
||||||
pdata = dev_get_platdata(ddata->dev);
|
pdata = dev_get_platdata(ddata->dev);
|
||||||
@ -3060,15 +3144,24 @@ static int sysc_init_soc(struct sysc *ddata)
|
|||||||
return 0;
|
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 sysc_address *disabled_module;
|
||||||
struct list_head *pos, *tmp;
|
struct list_head *pos, *tmp;
|
||||||
|
|
||||||
if (!sysc_soc)
|
if (!sysc_soc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (sysc_soc->nb.notifier_call)
|
||||||
|
cpu_pm_unregister_notifier(&sysc_soc->nb);
|
||||||
|
|
||||||
mutex_lock(&sysc_soc->list_lock);
|
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) {
|
list_for_each_safe(pos, tmp, &sysc_soc->disabled_modules) {
|
||||||
disabled_module = list_entry(pos, struct sysc_address, node);
|
disabled_module = list_entry(pos, struct sysc_address, node);
|
||||||
list_del(pos);
|
list_del(pos);
|
||||||
@ -3136,7 +3229,7 @@ static int sysc_probe(struct platform_device *pdev)
|
|||||||
ddata->dev = &pdev->dev;
|
ddata->dev = &pdev->dev;
|
||||||
platform_set_drvdata(pdev, ddata);
|
platform_set_drvdata(pdev, ddata);
|
||||||
|
|
||||||
error = sysc_init_soc(ddata);
|
error = sysc_init_static_data(ddata);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -3234,6 +3327,9 @@ static int sysc_probe(struct platform_device *pdev)
|
|||||||
pm_runtime_put(&pdev->dev);
|
pm_runtime_put(&pdev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_CTX_LOST)
|
||||||
|
sysc_add_restored(ddata);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -3315,7 +3411,7 @@ static void __exit sysc_exit(void)
|
|||||||
{
|
{
|
||||||
bus_unregister_notifier(&platform_bus_type, &sysc_nb);
|
bus_unregister_notifier(&platform_bus_type, &sysc_nb);
|
||||||
platform_driver_unregister(&sysc_driver);
|
platform_driver_unregister(&sysc_driver);
|
||||||
sysc_cleanup_soc();
|
sysc_cleanup_static_data();
|
||||||
}
|
}
|
||||||
module_exit(sysc_exit);
|
module_exit(sysc_exit);
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ struct sysc_regbits {
|
|||||||
s8 emufree_shift;
|
s8 emufree_shift;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SYSC_QUIRK_REINIT_ON_CTX_LOST BIT(28)
|
||||||
#define SYSC_QUIRK_REINIT_ON_RESUME BIT(27)
|
#define SYSC_QUIRK_REINIT_ON_RESUME BIT(27)
|
||||||
#define SYSC_QUIRK_GPMC_DEBUG BIT(26)
|
#define SYSC_QUIRK_GPMC_DEBUG BIT(26)
|
||||||
#define SYSC_MODULE_QUIRK_ENA_RESETDONE BIT(25)
|
#define SYSC_MODULE_QUIRK_ENA_RESETDONE BIT(25)
|
||||||
|
Loading…
Reference in New Issue
Block a user