Commit 030f4e72 authored by Arnd Bergmann's avatar Arnd Bergmann

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-2Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 24e18b0f 1b1da99b
......@@ -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,33 +234,73 @@ 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);
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment