@@ -66,6 +66,7 @@ struct clk_core {
unsigned long flags;
bool orphan;
bool rpm_enabled;
+ bool inherit_enabled; /* clock was enabled by bootloader */
unsigned int enable_count;
unsigned int prepare_count;
unsigned int protect_count;
@@ -1166,6 +1167,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
hlist_for_each_entry(child, &core->children, child_node)
clk_unprepare_unused_subtree(child);
+ if (core->inherit_enabled)
+ return;
+
if (core->prepare_count)
return;
@@ -1197,6 +1201,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
hlist_for_each_entry(child, &core->children, child_node)
clk_disable_unused_subtree(child);
+ if (core->inherit_enabled)
+ return;
+
if (core->flags & CLK_OPS_PARENT_ENABLE)
clk_core_prepare_enable(core->parent);
@@ -1270,6 +1277,45 @@ static int clk_disable_unused(void)
}
late_initcall_sync(clk_disable_unused);
+/* Ignore CLK_INHERIT_BOOTLOADER clocks enabled by bootloader. This
+ * gives a debug knob to disable inheriting clks from bootloader, so
+ * that drivers that used to work, when loaded as a module, thanks
+ * to disabling "unused" clocks at late_initcall(), can continue to
+ * work.
+ *
+ * The proper solution is to fix the drivers.
+ */
+static bool clk_ignore_inherited;
+static int __init clk_ignore_inherited_setup(char *__unused)
+{
+ clk_ignore_inherited = true;
+ return 1;
+}
+__setup("clk_ignore_inherited", clk_ignore_inherited_setup);
+
+/* clock and it's parents are already prepared/enabled from bootloader,
+ * so simply record the fact.
+ */
+static void __clk_inherit_enabled(struct clk_core *core)
+{
+ unsigned long parent_rate = 0;
+ core->inherit_enabled = true;
+ if (core->parent) {
+ __clk_inherit_enabled(core->parent);
+ parent_rate = core->parent->rate;
+ }
+ if (core->ops->recalc_rate)
+ core->rate = core->ops->recalc_rate(core->hw, parent_rate);
+}
+
+void clk_inherit_enabled(struct clk *clk)
+{
+ if (clk_ignore_inherited)
+ return;
+ __clk_inherit_enabled(clk->core);
+}
+EXPORT_SYMBOL_GPL(clk_inherit_enabled);
+
static int clk_core_determine_round_nolock(struct clk_core *core,
struct clk_rate_request *req)
{
@@ -3302,6 +3348,8 @@ static int __clk_core_init(struct clk_core *core)
* are enabled during init but might not have a parent yet.
*/
if (parent) {
+ if (orphan->inherit_enabled)
+ __clk_inherit_enabled(parent);
/* update the clk tree topology */
__clk_set_parent_before(orphan, parent);
__clk_set_parent_after(orphan, parent, NULL);
@@ -290,6 +290,31 @@ int qcom_cc_really_probe(struct platform_device *pdev,
if (ret)
return ret;
+ /*
+ * Check which of clocks that we inherit state from bootloader
+ * are enabled, and fixup enable/prepare state (as well as that
+ * of it's parents).
+ */
+ for (i = 0; i < num_clks; i++) {
+ struct clk_hw *hw;
+
+ if (!rclks[i])
+ continue;
+
+ hw = &rclks[i]->hw;
+
+ if (!(hw->init->flags & CLK_INHERIT_BOOTLOADER))
+ continue;
+
+ if (!clk_is_enabled_regmap(hw))
+ continue;
+
+ dev_dbg(dev, "%s is enabled from bootloader!\n",
+ hw->init->name);
+
+ clk_inherit_enabled(hw->clk);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
@@ -263,6 +263,7 @@ static struct clk_branch disp_cc_mdss_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "disp_cc_mdss_ahb_clk",
+ .flags = CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -276,6 +277,7 @@ static struct clk_branch disp_cc_mdss_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "disp_cc_mdss_axi_clk",
+ .flags = CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -294,7 +296,7 @@ static struct clk_branch disp_cc_mdss_byte0_clk = {
"disp_cc_mdss_byte0_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -330,7 +332,7 @@ static struct clk_branch disp_cc_mdss_byte0_intf_clk = {
"disp_cc_mdss_byte0_div_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -349,7 +351,7 @@ static struct clk_branch disp_cc_mdss_byte1_clk = {
"disp_cc_mdss_byte1_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -385,7 +387,7 @@ static struct clk_branch disp_cc_mdss_byte1_intf_clk = {
"disp_cc_mdss_byte1_div_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -403,7 +405,7 @@ static struct clk_branch disp_cc_mdss_esc0_clk = {
"disp_cc_mdss_esc0_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -421,7 +423,7 @@ static struct clk_branch disp_cc_mdss_esc1_clk = {
"disp_cc_mdss_esc1_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -439,7 +441,7 @@ static struct clk_branch disp_cc_mdss_mdp_clk = {
"disp_cc_mdss_mdp_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -475,7 +477,7 @@ static struct clk_branch disp_cc_mdss_pclk0_clk = {
"disp_cc_mdss_pclk0_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -494,7 +496,7 @@ static struct clk_branch disp_cc_mdss_pclk1_clk = {
"disp_cc_mdss_pclk1_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -561,7 +563,7 @@ static struct clk_branch disp_cc_mdss_vsync_clk = {
"disp_cc_mdss_vsync_clk_src",
},
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -1314,7 +1314,7 @@ static struct clk_branch gcc_disp_ahb_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_disp_ahb_clk",
- .flags = CLK_IS_CRITICAL,
+ .flags = CLK_IS_CRITICAL | CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -1328,6 +1328,7 @@ static struct clk_branch gcc_disp_axi_clk = {
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_disp_axi_clk",
+ .flags = CLK_INHERIT_BOOTLOADER,
.ops = &clk_branch2_ops,
},
},
@@ -34,6 +34,7 @@
#define CLK_OPS_PARENT_ENABLE BIT(12)
/* duty cycle call may be forwarded to the parent clock */
#define CLK_DUTY_CYCLE_PARENT BIT(13)
+#define CLK_INHERIT_BOOTLOADER BIT(14) /* clk may be enabled from bootloader */
struct clk;
struct clk_hw;
@@ -349,6 +350,15 @@ void clk_hw_unregister_fixed_rate(struct clk_hw *hw);
void of_fixed_clk_setup(struct device_node *np);
+/**
+ * clk_inherit_enabled - update the enable/prepare count of a clock and it's
+ * parents for clock enabled by bootloader.
+ *
+ * Intended to be used by clock drivers to inform the clk core of a clock
+ * that is already running.
+ */
+void clk_inherit_enabled(struct clk *clk);
+
/**
* struct clk_gate - gating clock
*