Skip to content

Commit

Permalink
bus: ti-sysc: Check for lost context in sysc_reinit_module()
Browse files Browse the repository at this point in the history
There is no need to restore context if it was not lost. Let's add a new
function sysc_check_context() to check for lost context. To make use of it,
we need to save the sysconfig register status on enable and disable.

Signed-off-by: Tony Lindgren <[email protected]>
  • Loading branch information
tmlind committed Sep 21, 2021
1 parent b3e9431 commit 95ec14f
Showing 1 changed file with 49 additions and 11 deletions.
60 changes: 49 additions & 11 deletions drivers/bus/ti-sysc.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,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;
Expand Down Expand Up @@ -1135,7 +1136,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)
Expand All @@ -1153,13 +1155,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)
Expand Down Expand Up @@ -1216,16 +1221,19 @@ 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;
} else {
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;
}
}

Expand All @@ -1236,10 +1244,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,
Expand Down Expand Up @@ -1377,13 +1388,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);
Expand Down

0 comments on commit 95ec14f

Please sign in to comment.