mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-08 00:29:36 -04:00
Merge tag 'omap-for-v5.16/ti-sysc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into arm/drivers
Driver changes for ti-sysc for v5.16 Changes for ti-sysc driver for improved system suspend and resume support as some drivers need to be reinitialized on resume. Also a non-urgent resume warning fix, and dropping of legacy flags for gpio and sham: - Fix timekeeping suspended warning on resume. Probably no need to merge this into fixes as it's gone unnoticed for a while. - Check for context loss for reinit of a module - Add add quirk handling to reinit on context loss, and also fix a build warning it caused - Add quirk handling to reset on reinit - Use context loss quirk for gpmc and otg - Handle otg force-idle quirk even if no driver is loaded - Drop legacy flags for gpio and sham * tag 'omap-for-v5.16/ti-sysc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap: bus: ti-sysc: Fix variable set but not used warning for reinit_modules bus: ti-sysc: Drop legacy quirk flag for sham bus: ti-sysc: Drop legacy quirk flag for gpio bus: ti-sysc: Handle otg force idle quirk bus: ti-sysc: Use context lost quirk for otg bus: ti-sysc: Use context lost quirks for gpmc bus: ti-sysc: Add quirk handling for reset on re-init bus: ti-sysc: Add quirk handling for reinit on context lost bus: ti-sysc: Check for lost context in sysc_reinit_module() bus: ti-sysc: Fix timekeeping_suspended warning on resume Link: https://lore.kernel.org/r/pull-1633950030-501948@atomide.com-2 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
@@ -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>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/timekeeping.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include <linux/platform_data/ti-sysc.h>
|
||||
@@ -51,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 {
|
||||
@@ -131,6 +140,7 @@ struct sysc {
|
||||
struct ti_sysc_cookie cookie;
|
||||
const char *name;
|
||||
u32 revision;
|
||||
u32 sysconfig;
|
||||
unsigned int reserved:1;
|
||||
unsigned int enabled:1;
|
||||
unsigned int needs_resume:1;
|
||||
@@ -147,6 +157,7 @@ struct sysc {
|
||||
|
||||
static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
|
||||
bool is_child);
|
||||
static int sysc_reset(struct sysc *ddata);
|
||||
|
||||
static void sysc_write(struct sysc *ddata, int offset, u32 value)
|
||||
{
|
||||
@@ -223,37 +234,77 @@ static u32 sysc_read_sysstatus(struct sysc *ddata)
|
||||
return sysc_read(ddata, offset);
|
||||
}
|
||||
|
||||
/* Poll on reset status */
|
||||
static int sysc_wait_softreset(struct sysc *ddata)
|
||||
static int sysc_poll_reset_sysstatus(struct sysc *ddata)
|
||||
{
|
||||
u32 sysc_mask, syss_done, rstval;
|
||||
int syss_offset, error = 0;
|
||||
|
||||
if (ddata->cap->regbits->srst_shift < 0)
|
||||
return 0;
|
||||
|
||||
syss_offset = ddata->offsets[SYSC_SYSSTATUS];
|
||||
sysc_mask = BIT(ddata->cap->regbits->srst_shift);
|
||||
int error, retries;
|
||||
u32 syss_done, rstval;
|
||||
|
||||
if (ddata->cfg.quirks & SYSS_QUIRK_RESETDONE_INVERTED)
|
||||
syss_done = 0;
|
||||
else
|
||||
syss_done = ddata->cfg.syss_mask;
|
||||
|
||||
if (syss_offset >= 0) {
|
||||
if (likely(!timekeeping_suspended)) {
|
||||
error = readx_poll_timeout_atomic(sysc_read_sysstatus, ddata,
|
||||
rstval, (rstval & ddata->cfg.syss_mask) ==
|
||||
syss_done, 100, MAX_MODULE_SOFTRESET_WAIT);
|
||||
} else {
|
||||
retries = MAX_MODULE_SOFTRESET_WAIT;
|
||||
while (retries--) {
|
||||
rstval = sysc_read_sysstatus(ddata);
|
||||
if ((rstval & ddata->cfg.syss_mask) == syss_done)
|
||||
return 0;
|
||||
udelay(2); /* Account for udelay flakeyness */
|
||||
}
|
||||
error = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
} else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) {
|
||||
return error;
|
||||
}
|
||||
|
||||
static int sysc_poll_reset_sysconfig(struct sysc *ddata)
|
||||
{
|
||||
int error, retries;
|
||||
u32 sysc_mask, rstval;
|
||||
|
||||
sysc_mask = BIT(ddata->cap->regbits->srst_shift);
|
||||
|
||||
if (likely(!timekeeping_suspended)) {
|
||||
error = readx_poll_timeout_atomic(sysc_read_sysconfig, ddata,
|
||||
rstval, !(rstval & sysc_mask),
|
||||
100, MAX_MODULE_SOFTRESET_WAIT);
|
||||
} else {
|
||||
retries = MAX_MODULE_SOFTRESET_WAIT;
|
||||
while (retries--) {
|
||||
rstval = sysc_read_sysconfig(ddata);
|
||||
if (!(rstval & sysc_mask))
|
||||
return 0;
|
||||
udelay(2); /* Account for udelay flakeyness */
|
||||
}
|
||||
error = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Poll on reset status */
|
||||
static int sysc_wait_softreset(struct sysc *ddata)
|
||||
{
|
||||
int syss_offset, error = 0;
|
||||
|
||||
if (ddata->cap->regbits->srst_shift < 0)
|
||||
return 0;
|
||||
|
||||
syss_offset = ddata->offsets[SYSC_SYSSTATUS];
|
||||
|
||||
if (syss_offset >= 0)
|
||||
error = sysc_poll_reset_sysstatus(ddata);
|
||||
else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS)
|
||||
error = sysc_poll_reset_sysconfig(ddata);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int sysc_add_named_clock_from_child(struct sysc *ddata,
|
||||
const char *name,
|
||||
const char *optfck_name)
|
||||
@@ -1094,7 +1145,8 @@ static int sysc_enable_module(struct device *dev)
|
||||
best_mode = fls(ddata->cfg.midlemodes) - 1;
|
||||
if (best_mode > SYSC_IDLE_MASK) {
|
||||
dev_err(dev, "%s: invalid midlemode\n", __func__);
|
||||
return -EINVAL;
|
||||
error = -EINVAL;
|
||||
goto save_context;
|
||||
}
|
||||
|
||||
if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY)
|
||||
@@ -1112,13 +1164,16 @@ static int sysc_enable_module(struct device *dev)
|
||||
sysc_write_sysconfig(ddata, reg);
|
||||
}
|
||||
|
||||
/* Flush posted write */
|
||||
sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
|
||||
error = 0;
|
||||
|
||||
save_context:
|
||||
/* Save context and flush posted write */
|
||||
ddata->sysconfig = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
|
||||
|
||||
if (ddata->module_enable_quirk)
|
||||
ddata->module_enable_quirk(ddata);
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int sysc_best_idle_mode(u32 idlemodes, u32 *best_mode)
|
||||
@@ -1175,8 +1230,10 @@ static int sysc_disable_module(struct device *dev)
|
||||
set_sidle:
|
||||
/* Set SIDLE mode */
|
||||
idlemodes = ddata->cfg.sidlemodes;
|
||||
if (!idlemodes || regbits->sidle_shift < 0)
|
||||
return 0;
|
||||
if (!idlemodes || regbits->sidle_shift < 0) {
|
||||
ret = 0;
|
||||
goto save_context;
|
||||
}
|
||||
|
||||
if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_SIDLE) {
|
||||
best_mode = SYSC_IDLE_FORCE;
|
||||
@@ -1184,7 +1241,8 @@ static int sysc_disable_module(struct device *dev)
|
||||
ret = sysc_best_idle_mode(idlemodes, &best_mode);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: invalid sidlemode\n", __func__);
|
||||
return ret;
|
||||
ret = -EINVAL;
|
||||
goto save_context;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1195,10 +1253,13 @@ static int sysc_disable_module(struct device *dev)
|
||||
reg |= 1 << regbits->autoidle_shift;
|
||||
sysc_write_sysconfig(ddata, reg);
|
||||
|
||||
/* Flush posted write */
|
||||
sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
|
||||
ret = 0;
|
||||
|
||||
return 0;
|
||||
save_context:
|
||||
/* Save context and flush posted write */
|
||||
ddata->sysconfig = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused sysc_runtime_suspend_legacy(struct device *dev,
|
||||
@@ -1336,13 +1397,40 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if device context was lost. Assumes the sysconfig register value
|
||||
* after lost context is different from the configured value. Only works for
|
||||
* enabled devices.
|
||||
*
|
||||
* Eventually we may want to also add support to using the context lost
|
||||
* registers that some SoCs have.
|
||||
*/
|
||||
static int sysc_check_context(struct sysc *ddata)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (!ddata->enabled)
|
||||
return -ENODATA;
|
||||
|
||||
reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
|
||||
if (reg == ddata->sysconfig)
|
||||
return 0;
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
static int sysc_reinit_module(struct sysc *ddata, bool leave_enabled)
|
||||
{
|
||||
struct device *dev = ddata->dev;
|
||||
int error;
|
||||
|
||||
/* Disable target module if it is enabled */
|
||||
if (ddata->enabled) {
|
||||
/* Nothing to do if enabled and context not lost */
|
||||
error = sysc_check_context(ddata);
|
||||
if (!error)
|
||||
return 0;
|
||||
|
||||
/* Disable target module if it is enabled */
|
||||
error = sysc_runtime_suspend(dev);
|
||||
if (error)
|
||||
dev_warn(dev, "reinit suspend failed: %i\n", error);
|
||||
@@ -1353,6 +1441,15 @@ static int sysc_reinit_module(struct sysc *ddata, bool leave_enabled)
|
||||
if (error)
|
||||
dev_warn(dev, "reinit resume failed: %i\n", error);
|
||||
|
||||
/* Some modules like am335x gpmc need reset and restore of sysconfig */
|
||||
if (ddata->cfg.quirks & SYSC_QUIRK_RESET_ON_CTX_LOST) {
|
||||
error = sysc_reset(ddata);
|
||||
if (error)
|
||||
dev_warn(dev, "reinit reset failed: %i\n", error);
|
||||
|
||||
sysc_write_sysconfig(ddata, ddata->sysconfig);
|
||||
}
|
||||
|
||||
if (leave_enabled)
|
||||
return error;
|
||||
|
||||
@@ -1442,10 +1539,6 @@ struct sysc_revision_quirk {
|
||||
|
||||
static const struct sysc_revision_quirk sysc_revision_quirks[] = {
|
||||
/* These drivers need to be fixed to not use pm_runtime_irq_safe() */
|
||||
SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffff00ff,
|
||||
SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET),
|
||||
SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff,
|
||||
SYSC_QUIRK_LEGACY_IDLE),
|
||||
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff,
|
||||
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE),
|
||||
SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
|
||||
@@ -1476,7 +1569,10 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
|
||||
SYSC_QUIRK_CLKDM_NOAUTO),
|
||||
SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff,
|
||||
SYSC_QUIRK_CLKDM_NOAUTO),
|
||||
SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffff00ff,
|
||||
SYSC_QUIRK_OPT_CLKS_IN_RESET),
|
||||
SYSC_QUIRK("gpmc", 0, 0, 0x10, 0x14, 0x00000060, 0xffffffff,
|
||||
SYSC_QUIRK_REINIT_ON_CTX_LOST | SYSC_QUIRK_RESET_ON_CTX_LOST |
|
||||
SYSC_QUIRK_GPMC_DEBUG),
|
||||
SYSC_QUIRK("hdmi", 0, 0, 0x10, -ENODEV, 0x50030200, 0xffffffff,
|
||||
SYSC_QUIRK_OPT_CLKS_NEEDED),
|
||||
@@ -1512,10 +1608,11 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
|
||||
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -ENODEV, 0x50700101, 0xffffffff,
|
||||
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
|
||||
SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
|
||||
0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
|
||||
0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY |
|
||||
SYSC_MODULE_QUIRK_OTG),
|
||||
SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -ENODEV, 0x4ea2080d, 0xffffffff,
|
||||
SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY |
|
||||
SYSC_QUIRK_REINIT_ON_RESUME),
|
||||
SYSC_QUIRK_REINIT_ON_CTX_LOST),
|
||||
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0,
|
||||
SYSC_MODULE_QUIRK_WDT),
|
||||
/* PRUSS on am3, am4 and am5 */
|
||||
@@ -1580,6 +1677,7 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
|
||||
SYSC_QUIRK("sdio", 0, 0, 0x10, -ENODEV, 0x40202301, 0xffff0ff0, 0),
|
||||
SYSC_QUIRK("sdio", 0, 0x2fc, 0x110, 0x114, 0x31010000, 0xffffffff, 0),
|
||||
SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0),
|
||||
SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff, 0),
|
||||
SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40000902, 0xffffffff, 0),
|
||||
SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40002903, 0xffffffff, 0),
|
||||
SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff, 0),
|
||||
@@ -1871,6 +1969,22 @@ static void sysc_module_lock_quirk_rtc(struct sysc *ddata)
|
||||
sysc_quirk_rtc(ddata, true);
|
||||
}
|
||||
|
||||
/* OTG omap2430 glue layer up to omap4 needs OTG_FORCESTDBY configured */
|
||||
static void sysc_module_enable_quirk_otg(struct sysc *ddata)
|
||||
{
|
||||
int offset = 0x414; /* OTG_FORCESTDBY */
|
||||
|
||||
sysc_write(ddata, offset, 0);
|
||||
}
|
||||
|
||||
static void sysc_module_disable_quirk_otg(struct sysc *ddata)
|
||||
{
|
||||
int offset = 0x414; /* OTG_FORCESTDBY */
|
||||
u32 val = BIT(0); /* ENABLEFORCE */
|
||||
|
||||
sysc_write(ddata, offset, val);
|
||||
}
|
||||
|
||||
/* 36xx SGX needs a quirk for to bypass OCP IPG interrupt logic */
|
||||
static void sysc_module_enable_quirk_sgx(struct sysc *ddata)
|
||||
{
|
||||
@@ -1953,6 +2067,11 @@ static void sysc_init_module_quirks(struct sysc *ddata)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_OTG) {
|
||||
ddata->module_enable_quirk = sysc_module_enable_quirk_otg;
|
||||
ddata->module_disable_quirk = sysc_module_disable_quirk_otg;
|
||||
}
|
||||
|
||||
if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_SGX)
|
||||
ddata->module_enable_quirk = sysc_module_enable_quirk_sgx;
|
||||
|
||||
@@ -2398,6 +2517,78 @@ 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;
|
||||
|
||||
list_for_each(pos, &sysc_soc->restored_modules) {
|
||||
module = list_entry(pos, struct sysc_module, node);
|
||||
ddata = module->ddata;
|
||||
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
|
||||
@@ -2897,12 +3088,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;
|
||||
@@ -2918,6 +3111,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);
|
||||
@@ -2981,15 +3175,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);
|
||||
@@ -3057,7 +3260,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;
|
||||
|
||||
@@ -3155,6 +3358,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:
|
||||
@@ -3236,7 +3442,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);
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ struct sysc_regbits {
|
||||
s8 emufree_shift;
|
||||
};
|
||||
|
||||
#define SYSC_MODULE_QUIRK_OTG BIT(30)
|
||||
#define SYSC_QUIRK_RESET_ON_CTX_LOST BIT(29)
|
||||
#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)
|
||||
|
||||
Reference in New Issue
Block a user