Commit 95ec14fa authored by Tony Lindgren's avatar Tony Lindgren
Browse files

bus: ti-sysc: Check for lost context in sysc_reinit_module()



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: default avatarTony Lindgren <tony@atomide.com>
parent b3e94318
Loading
Loading
Loading
Loading
+49 −11
Original line number Diff line number Diff line
@@ -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;
@@ -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)
@@ -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)
@@ -1216,8 +1221,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;
@@ -1225,7 +1232,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;
		}
	}

@@ -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,
@@ -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);