summaryrefslogtreecommitdiffstats
path: root/drivers/bus/ti-sysc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bus/ti-sysc.c')
-rw-r--r--drivers/bus/ti-sysc.c318
1 files changed, 176 insertions, 142 deletions
diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
index e6deabd8305d..f702c85c81b6 100644
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* ti-sysc.c - Texas Instruments sysc interconnect target driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/io.h>
@@ -62,18 +54,27 @@ static const char * const clock_names[SYSC_MAX_CLOCKS] = {
* @module_size: size of the interconnect target module
* @module_va: virtual address of the interconnect target module
* @offsets: register offsets from module base
+ * @mdata: ti-sysc to hwmod translation data for a module
* @clocks: clocks used by the interconnect target module
* @clock_roles: clock role names for the found clocks
* @nr_clocks: number of clocks used by the interconnect target module
+ * @rsts: resets used by the interconnect target module
* @legacy_mode: configured for legacy mode if set
* @cap: interconnect target module capabilities
* @cfg: interconnect target module configuration
+ * @cookie: data used by legacy platform callbacks
* @name: name if available
* @revision: interconnect target module revision
+ * @enabled: sysc runtime enabled status
* @needs_resume: runtime resume needed on resume from suspend
+ * @child_needs_resume: runtime resume needed for child on resume from suspend
+ * @disable_on_idle: status flag used for disabling modules with resets
+ * @idle_work: work structure used to perform delayed idle on a module
* @clk_enable_quirk: module specific clock enable quirk
* @clk_disable_quirk: module specific clock disable quirk
* @reset_done_quirk: module specific reset done quirk
+ * @module_enable_quirk: module specific enable quirk
+ * @module_disable_quirk: module specific disable quirk
*/
struct sysc {
struct device *dev;
@@ -95,11 +96,12 @@ struct sysc {
unsigned int enabled:1;
unsigned int needs_resume:1;
unsigned int child_needs_resume:1;
- unsigned int disable_on_idle:1;
struct delayed_work idle_work;
void (*clk_enable_quirk)(struct sysc *sysc);
void (*clk_disable_quirk)(struct sysc *sysc);
void (*reset_done_quirk)(struct sysc *sysc);
+ void (*module_enable_quirk)(struct sysc *sysc);
+ void (*module_disable_quirk)(struct sysc *sysc);
};
static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np,
@@ -280,9 +282,6 @@ static int sysc_get_one_clock(struct sysc *ddata, const char *name)
ddata->clocks[index] = devm_clk_get(ddata->dev, name);
if (IS_ERR(ddata->clocks[index])) {
- if (PTR_ERR(ddata->clocks[index]) == -ENOENT)
- return 0;
-
dev_err(ddata->dev, "clock get error for %s: %li\n",
name, PTR_ERR(ddata->clocks[index]));
@@ -344,6 +343,12 @@ static int sysc_get_clocks(struct sysc *ddata)
return -EINVAL;
}
+ /* Always add a slot for main clocks fck and ick even if unused */
+ if (!nr_fck)
+ ddata->nr_clocks++;
+ if (!nr_ick)
+ ddata->nr_clocks++;
+
ddata->clocks = devm_kcalloc(ddata->dev,
ddata->nr_clocks, sizeof(*ddata->clocks),
GFP_KERNEL);
@@ -357,7 +362,7 @@ static int sysc_get_clocks(struct sysc *ddata)
continue;
error = sysc_get_one_clock(ddata, name);
- if (error && error != -ENOENT)
+ if (error)
return error;
}
@@ -422,7 +427,7 @@ static int sysc_enable_opt_clocks(struct sysc *ddata)
struct clk *clock;
int i, error;
- if (!ddata->clocks)
+ if (!ddata->clocks || ddata->nr_clocks < SYSC_OPTFCK0 + 1)
return 0;
for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) {
@@ -456,7 +461,7 @@ static void sysc_disable_opt_clocks(struct sysc *ddata)
struct clk *clock;
int i;
- if (!ddata->clocks)
+ if (!ddata->clocks || ddata->nr_clocks < SYSC_OPTFCK0 + 1)
return;
for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) {
@@ -474,7 +479,7 @@ static void sysc_clkdm_deny_idle(struct sysc *ddata)
{
struct ti_sysc_platform_data *pdata;
- if (ddata->legacy_mode)
+ if (ddata->legacy_mode || (ddata->cfg.quirks & SYSC_QUIRK_CLKDM_NOAUTO))
return;
pdata = dev_get_platdata(ddata->dev);
@@ -486,7 +491,7 @@ static void sysc_clkdm_allow_idle(struct sysc *ddata)
{
struct ti_sysc_platform_data *pdata;
- if (ddata->legacy_mode)
+ if (ddata->legacy_mode || (ddata->cfg.quirks & SYSC_QUIRK_CLKDM_NOAUTO))
return;
pdata = dev_get_platdata(ddata->dev);
@@ -503,11 +508,9 @@ static void sysc_clkdm_allow_idle(struct sysc *ddata)
static int sysc_init_resets(struct sysc *ddata)
{
ddata->rsts =
- devm_reset_control_get_optional(ddata->dev, "rstctrl");
- if (IS_ERR(ddata->rsts))
- return PTR_ERR(ddata->rsts);
+ devm_reset_control_get_optional_shared(ddata->dev, "rstctrl");
- return 0;
+ return PTR_ERR_OR_ZERO(ddata->rsts);
}
/**
@@ -615,8 +618,8 @@ static void sysc_check_quirk_stdout(struct sysc *ddata,
* node but children have "ti,hwmods". These belong to the interconnect
* target node and are managed by this driver.
*/
-static int sysc_check_one_child(struct sysc *ddata,
- struct device_node *np)
+static void sysc_check_one_child(struct sysc *ddata,
+ struct device_node *np)
{
const char *name;
@@ -626,22 +629,14 @@ static int sysc_check_one_child(struct sysc *ddata,
sysc_check_quirk_stdout(ddata, np);
sysc_parse_dts_quirks(ddata, np, true);
-
- return 0;
}
-static int sysc_check_children(struct sysc *ddata)
+static void sysc_check_children(struct sysc *ddata)
{
struct device_node *child;
- int error;
- for_each_child_of_node(ddata->dev->of_node, child) {
- error = sysc_check_one_child(ddata, child);
- if (error)
- return error;
- }
-
- return 0;
+ for_each_child_of_node(ddata->dev->of_node, child)
+ sysc_check_one_child(ddata, child);
}
/*
@@ -794,9 +789,7 @@ static int sysc_map_and_check_registers(struct sysc *ddata)
if (error)
return error;
- error = sysc_check_children(ddata);
- if (error)
- return error;
+ sysc_check_children(ddata);
error = sysc_parse_registers(ddata);
if (error)
@@ -928,6 +921,9 @@ set_midle:
return -EINVAL;
}
+ if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_MSTANDBY)
+ best_mode = SYSC_IDLE_NO;
+
reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift);
reg |= best_mode << regbits->midle_shift;
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
@@ -940,6 +936,9 @@ set_autoidle:
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
}
+ if (ddata->module_enable_quirk)
+ ddata->module_enable_quirk(ddata);
+
return 0;
}
@@ -949,7 +948,7 @@ static int sysc_best_idle_mode(u32 idlemodes, u32 *best_mode)
*best_mode = SYSC_IDLE_SMART_WKUP;
else if (idlemodes & BIT(SYSC_IDLE_SMART))
*best_mode = SYSC_IDLE_SMART;
- else if (idlemodes & SYSC_IDLE_FORCE)
+ else if (idlemodes & BIT(SYSC_IDLE_FORCE))
*best_mode = SYSC_IDLE_FORCE;
else
return -EINVAL;
@@ -969,6 +968,9 @@ static int sysc_disable_module(struct device *dev)
if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV)
return 0;
+ if (ddata->module_disable_quirk)
+ ddata->module_disable_quirk(ddata);
+
regbits = ddata->cap->regbits;
reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]);
@@ -983,6 +985,10 @@ static int sysc_disable_module(struct device *dev)
return ret;
}
+ if (ddata->cfg.quirks & (SYSC_QUIRK_SWSUP_MSTANDBY) ||
+ ddata->cfg.quirks & (SYSC_QUIRK_FORCE_MSTANDBY))
+ best_mode = SYSC_IDLE_FORCE;
+
reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift);
reg |= best_mode << regbits->midle_shift;
sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg);
@@ -1031,8 +1037,7 @@ static int __maybe_unused sysc_runtime_suspend_legacy(struct device *dev,
dev_err(dev, "%s: could not idle: %i\n",
__func__, error);
- if (ddata->disable_on_idle)
- reset_control_assert(ddata->rsts);
+ reset_control_assert(ddata->rsts);
return 0;
}
@@ -1043,9 +1048,6 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev,
struct ti_sysc_platform_data *pdata;
int error;
- if (ddata->disable_on_idle)
- reset_control_deassert(ddata->rsts);
-
pdata = dev_get_platdata(ddata->dev);
if (!pdata)
return 0;
@@ -1058,6 +1060,8 @@ static int __maybe_unused sysc_runtime_resume_legacy(struct device *dev,
dev_err(dev, "%s: could not enable: %i\n",
__func__, error);
+ reset_control_deassert(ddata->rsts);
+
return 0;
}
@@ -1091,10 +1095,9 @@ static int __maybe_unused sysc_runtime_suspend(struct device *dev)
ddata->enabled = false;
err_allow_idle:
- sysc_clkdm_allow_idle(ddata);
+ reset_control_assert(ddata->rsts);
- if (ddata->disable_on_idle)
- reset_control_assert(ddata->rsts);
+ sysc_clkdm_allow_idle(ddata);
return error;
}
@@ -1109,8 +1112,6 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
if (ddata->enabled)
return 0;
- if (ddata->disable_on_idle)
- reset_control_deassert(ddata->rsts);
sysc_clkdm_deny_idle(ddata);
@@ -1124,6 +1125,8 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev)
if (error)
goto err_opt_clocks;
+ reset_control_deassert(ddata->rsts);
+
if (ddata->legacy_mode) {
error = sysc_runtime_resume_legacy(dev, ddata);
if (error)
@@ -1211,10 +1214,6 @@ 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("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
- SYSC_QUIRK_LEGACY_IDLE),
- SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000030, 0xffffffff,
- SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff,
SYSC_QUIRK_LEGACY_IDLE),
SYSC_QUIRK("smartreflex", 0, -1, 0x24, -1, 0x00000000, 0xffffffff,
@@ -1244,6 +1243,14 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK_SWSUP_SIDLE),
/* Quirks that need to be set based on detected module */
+ SYSC_QUIRK("aess", 0, 0, 0x10, -1, 0x40000000, 0xffffffff,
+ SYSC_MODULE_QUIRK_AESS),
+ SYSC_QUIRK("dcan", 0x48480000, 0x20, -1, -1, 0xa3170504, 0xffffffff,
+ SYSC_QUIRK_CLKDM_NOAUTO),
+ SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -1, 0x500a0200, 0xffffffff,
+ SYSC_QUIRK_CLKDM_NOAUTO),
+ SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -1, 0x500a0200, 0xffffffff,
+ SYSC_QUIRK_CLKDM_NOAUTO),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff,
SYSC_MODULE_QUIRK_HDQ1W),
SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff,
@@ -1256,22 +1263,35 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_MODULE_QUIRK_I2C),
SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0,
SYSC_MODULE_QUIRK_I2C),
+ SYSC_QUIRK("gpu", 0x50000000, 0x14, -1, -1, 0x00010201, 0xffffffff, 0),
+ SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff,
+ SYSC_MODULE_QUIRK_SGX),
+ SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
+ 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
+ SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -1, 0x4ea2080d, 0xffffffff,
+ SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY),
SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0,
SYSC_MODULE_QUIRK_WDT),
+ /* Watchdog on am3 and am4 */
+ SYSC_QUIRK("wdt", 0x44e35000, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0,
+ SYSC_MODULE_QUIRK_WDT | SYSC_QUIRK_SWSUP_SIDLE),
#ifdef DEBUG
SYSC_QUIRK("adc", 0, 0, 0x10, -1, 0x47300001, 0xffffffff, 0),
SYSC_QUIRK("atl", 0, 0, -1, -1, 0x0a070100, 0xffffffff, 0),
- SYSC_QUIRK("aess", 0, 0, 0x10, -1, 0x40000000, 0xffffffff, 0),
SYSC_QUIRK("cm", 0, 0, -1, -1, 0x40000301, 0xffffffff, 0),
SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0),
SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902,
0xffff00f0, 0),
- SYSC_QUIRK("dcan", 0, 0, -1, -1, 0xffffffff, 0xffffffff, 0),
+ SYSC_QUIRK("dcan", 0, 0x20, -1, -1, 0xa3170504, 0xffffffff, 0),
+ SYSC_QUIRK("dcan", 0, 0x20, -1, -1, 0x4edb1902, 0xffffffff, 0),
SYSC_QUIRK("dmic", 0, 0, 0x10, -1, 0x50010000, 0xffffffff, 0),
SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0),
+ SYSC_QUIRK("d2d", 0x4a0b6000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0),
+ SYSC_QUIRK("d2d", 0x4a0cd000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0),
SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0),
SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0),
+ SYSC_QUIRK("gpu", 0, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff, 0),
SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0),
SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0),
SYSC_QUIRK("lcdc", 0, 0, 0x54, -1, 0x4f201000, 0xffffffff, 0),
@@ -1307,8 +1327,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0),
SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0),
- SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
- 0xffffffff, 0),
SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0),
#endif
};
@@ -1392,6 +1410,14 @@ static void sysc_clk_enable_quirk_hdq1w(struct sysc *ddata)
sysc_write(ddata, offset, val);
}
+/* AESS (Audio Engine SubSystem) needs autogating set after enable */
+static void sysc_module_enable_quirk_aess(struct sysc *ddata)
+{
+ int offset = 0x7c; /* AESS_AUTO_GATING_ENABLE */
+
+ sysc_write(ddata, offset, 1);
+}
+
/* I2C needs extra enable bit toggling for reset */
static void sysc_clk_quirk_i2c(struct sysc *ddata, bool enable)
{
@@ -1423,6 +1449,15 @@ static void sysc_clk_disable_quirk_i2c(struct sysc *ddata)
sysc_clk_quirk_i2c(ddata, false);
}
+/* 36xx SGX needs a quirk for to bypass OCP IPG interrupt logic */
+static void sysc_module_enable_quirk_sgx(struct sysc *ddata)
+{
+ int offset = 0xff08; /* OCP_DEBUG_CONFIG */
+ u32 val = BIT(31); /* THALIA_INT_BYPASS */
+
+ sysc_write(ddata, offset, val);
+}
+
/* Watchdog timer needs a disable sequence after reset */
static void sysc_reset_done_quirk_wdt(struct sysc *ddata)
{
@@ -1437,14 +1472,14 @@ static void sysc_reset_done_quirk_wdt(struct sysc *ddata)
!(val & 0x10), 100,
MAX_MODULE_SOFTRESET_WAIT);
if (error)
- dev_warn(ddata->dev, "wdt disable spr failed\n");
+ dev_warn(ddata->dev, "wdt disable step1 failed\n");
- sysc_write(ddata, wps, 0x5555);
+ sysc_write(ddata, spr, 0x5555);
error = readl_poll_timeout(ddata->module_va + wps, val,
!(val & 0x10), 100,
MAX_MODULE_SOFTRESET_WAIT);
if (error)
- dev_warn(ddata->dev, "wdt disable wps failed\n");
+ dev_warn(ddata->dev, "wdt disable step2 failed\n");
}
static void sysc_init_module_quirks(struct sysc *ddata)
@@ -1465,8 +1500,16 @@ static void sysc_init_module_quirks(struct sysc *ddata)
return;
}
- if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_WDT)
+ if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_AESS)
+ ddata->module_enable_quirk = sysc_module_enable_quirk_aess;
+
+ if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_SGX)
+ ddata->module_enable_quirk = sysc_module_enable_quirk_sgx;
+
+ if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_WDT) {
ddata->reset_done_quirk = sysc_reset_done_quirk_wdt;
+ ddata->module_disable_quirk = sysc_reset_done_quirk_wdt;
+ }
}
static int sysc_clockdomain_init(struct sysc *ddata)
@@ -1516,54 +1559,17 @@ static int sysc_legacy_init(struct sysc *ddata)
return error;
}
-/**
- * sysc_rstctrl_reset_deassert - deassert rstctrl reset
- * @ddata: device driver data
- * @reset: reset before deassert
- *
- * A module can have both OCP softreset control and external rstctrl.
- * If more complicated rstctrl resets are needed, please handle these
- * directly from the child device driver and map only the module reset
- * for the parent interconnect target module device.
- *
- * Automatic reset of the module on init can be skipped with the
- * "ti,no-reset-on-init" device tree property.
- */
-static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset)
-{
- int error, val;
-
- if (!ddata->rsts)
- return 0;
-
- if (reset) {
- error = reset_control_assert(ddata->rsts);
- if (error)
- return error;
- }
-
- error = reset_control_deassert(ddata->rsts);
- if (error == -EEXIST)
- return 0;
-
- error = readx_poll_timeout(reset_control_status, ddata->rsts, val,
- val == 0, 100, MAX_MODULE_SOFTRESET_WAIT);
-
- return error;
-}
-
/*
* Note that the caller must ensure the interconnect target module is enabled
* before calling reset. Otherwise reset will not complete.
*/
static int sysc_reset(struct sysc *ddata)
{
- int sysc_offset, syss_offset, sysc_val, rstval, quirks, error = 0;
+ int sysc_offset, syss_offset, sysc_val, rstval, error = 0;
u32 sysc_mask, syss_done;
sysc_offset = ddata->offsets[SYSC_SYSCONFIG];
syss_offset = ddata->offsets[SYSC_SYSSTATUS];
- quirks = ddata->cfg.quirks;
if (ddata->legacy_mode || sysc_offset < 0 ||
ddata->cap->regbits->srst_shift < 0 ||
@@ -1584,6 +1590,10 @@ static int sysc_reset(struct sysc *ddata)
sysc_val |= sysc_mask;
sysc_write(ddata, sysc_offset, sysc_val);
+ if (ddata->cfg.srst_udelay)
+ usleep_range(ddata->cfg.srst_udelay,
+ ddata->cfg.srst_udelay * 2);
+
if (ddata->clk_enable_quirk)
ddata->clk_enable_quirk(ddata);
@@ -1615,34 +1625,27 @@ static int sysc_reset(struct sysc *ddata)
static int sysc_init_module(struct sysc *ddata)
{
int error = 0;
- bool manage_clocks = true;
- error = sysc_rstctrl_reset_deassert(ddata, false);
+ error = sysc_clockdomain_init(ddata);
if (error)
return error;
- if (ddata->cfg.quirks &
- (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))
- manage_clocks = false;
+ sysc_clkdm_deny_idle(ddata);
- error = sysc_clockdomain_init(ddata);
+ /*
+ * Always enable clocks. The bootloader may or may not have enabled
+ * the related clocks.
+ */
+ error = sysc_enable_opt_clocks(ddata);
if (error)
return error;
- if (manage_clocks) {
- sysc_clkdm_deny_idle(ddata);
-
- error = sysc_enable_opt_clocks(ddata);
- if (error)
- return error;
-
- error = sysc_enable_main_clocks(ddata);
- if (error)
- goto err_opt_clocks;
- }
+ error = sysc_enable_main_clocks(ddata);
+ if (error)
+ goto err_opt_clocks;
if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) {
- error = sysc_rstctrl_reset_deassert(ddata, true);
+ error = reset_control_deassert(ddata->rsts);
if (error)
goto err_main_clocks;
}
@@ -1654,27 +1657,32 @@ static int sysc_init_module(struct sysc *ddata)
if (ddata->legacy_mode) {
error = sysc_legacy_init(ddata);
if (error)
- goto err_main_clocks;
+ goto err_reset;
}
- if (!ddata->legacy_mode && manage_clocks) {
+ if (!ddata->legacy_mode) {
error = sysc_enable_module(ddata->dev);
if (error)
- goto err_main_clocks;
+ goto err_reset;
}
error = sysc_reset(ddata);
if (error)
dev_err(ddata->dev, "Reset failed with %d\n", error);
- if (!ddata->legacy_mode && manage_clocks)
+ if (error && !ddata->legacy_mode)
sysc_disable_module(ddata->dev);
+err_reset:
+ if (error && !(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT))
+ reset_control_assert(ddata->rsts);
+
err_main_clocks:
- if (manage_clocks)
+ if (error)
sysc_disable_main_clocks(ddata);
err_opt_clocks:
- if (manage_clocks) {
+ /* No re-enable of clockdomain autoidle to prevent module autoidle */
+ if (error) {
sysc_disable_opt_clocks(ddata);
sysc_clkdm_allow_idle(ddata);
}
@@ -1692,10 +1700,7 @@ static int sysc_init_sysc_mask(struct sysc *ddata)
if (error)
return 0;
- if (val)
- ddata->cfg.sysc_val = val & ddata->cap->sysc_mask;
- else
- ddata->cfg.sysc_val = ddata->cap->sysc_mask;
+ ddata->cfg.sysc_val = val & ddata->cap->sysc_mask;
return 0;
}
@@ -1784,9 +1789,8 @@ static int sysc_child_add_named_clock(struct sysc *ddata,
clk = clk_get(child, name);
if (!IS_ERR(clk)) {
- clk_put(clk);
-
- return -EEXIST;
+ error = -EEXIST;
+ goto put_clk;
}
clk = clk_get(ddata->dev, name);
@@ -1796,7 +1800,7 @@ static int sysc_child_add_named_clock(struct sysc *ddata,
l = clkdev_create(clk, name, dev_name(child));
if (!l)
error = -ENOMEM;
-
+put_clk:
clk_put(clk);
return error;
@@ -2357,6 +2361,27 @@ static void ti_sysc_idle(struct work_struct *work)
ddata = container_of(work, struct sysc, idle_work.work);
+ /*
+ * One time decrement of clock usage counts if left on from init.
+ * Note that we disable opt clocks unconditionally in this case
+ * as they are enabled unconditionally during init without
+ * considering sysc_opt_clks_needed() at that point.
+ */
+ if (ddata->cfg.quirks & (SYSC_QUIRK_NO_IDLE |
+ SYSC_QUIRK_NO_IDLE_ON_INIT)) {
+ sysc_disable_main_clocks(ddata);
+ sysc_disable_opt_clocks(ddata);
+ sysc_clkdm_allow_idle(ddata);
+ }
+
+ /* Keep permanent PM runtime usage count for SYSC_QUIRK_NO_IDLE */
+ if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE)
+ return;
+
+ /*
+ * Decrement PM runtime usage count for SYSC_QUIRK_NO_IDLE_ON_INIT
+ * and SYSC_QUIRK_NO_RESET_ON_INIT
+ */
if (pm_runtime_active(ddata->dev))
pm_runtime_put_sync(ddata->dev);
}
@@ -2385,27 +2410,27 @@ static int sysc_probe(struct platform_device *pdev)
error = sysc_init_dts_quirks(ddata);
if (error)
- goto unprepare;
+ return error;
error = sysc_map_and_check_registers(ddata);
if (error)
- goto unprepare;
+ return error;
error = sysc_init_sysc_mask(ddata);
if (error)
- goto unprepare;
+ return error;
error = sysc_init_idlemodes(ddata);
if (error)
- goto unprepare;
+ return error;
error = sysc_init_syss_mask(ddata);
if (error)
- goto unprepare;
+ return error;
error = sysc_init_pdata(ddata);
if (error)
- goto unprepare;
+ return error;
sysc_init_early_quirks(ddata);
@@ -2415,7 +2440,7 @@ static int sysc_probe(struct platform_device *pdev)
error = sysc_init_resets(ddata);
if (error)
- return error;
+ goto unprepare;
error = sysc_init_module(ddata);
if (error)
@@ -2429,6 +2454,17 @@ static int sysc_probe(struct platform_device *pdev)
goto unprepare;
}
+ /* Balance use counts as PM runtime should have enabled these all */
+ if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT))
+ reset_control_assert(ddata->rsts);
+
+ if (!(ddata->cfg.quirks &
+ (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT))) {
+ sysc_disable_main_clocks(ddata);
+ sysc_disable_opt_clocks(ddata);
+ sysc_clkdm_allow_idle(ddata);
+ }
+
sysc_show_registers(ddata);
ddata->dev->type = &sysc_device_type;
@@ -2441,16 +2477,14 @@ static int sysc_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&ddata->idle_work, ti_sysc_idle);
/* At least earlycon won't survive without deferred idle */
- if (ddata->cfg.quirks & (SYSC_QUIRK_NO_IDLE_ON_INIT |
+ if (ddata->cfg.quirks & (SYSC_QUIRK_NO_IDLE |
+ SYSC_QUIRK_NO_IDLE_ON_INIT |
SYSC_QUIRK_NO_RESET_ON_INIT)) {
schedule_delayed_work(&ddata->idle_work, 3000);
} else {
pm_runtime_put(&pdev->dev);
}
- if (!of_get_available_child_count(ddata->dev->of_node))
- ddata->disable_on_idle = true;
-
return 0;
err:
OpenPOWER on IntegriCloud