Unverified Commit 030f4e72 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'omap-for-v5.16/ti-sysc-signed' of...

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: default avatarArnd Bergmann <arnd@arndb.de>
parents 24e18b0f 1b1da99b
Loading
Loading
Loading
Loading
+241 −35
Original line number Diff line number Diff line
@@ -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,34 +234,74 @@ 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;
	}

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

	} else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) {
	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;
}

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

+3 −0
Original line number Diff line number Diff line
@@ -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)