summaryrefslogtreecommitdiff
path: root/drivers/clk/clk.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-09-20 15:45:07 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-09-20 15:45:07 -0700
commita703d279c57e1bfe2b6536c3a17c1c498b416d24 (patch)
tree3b11cc5bf81720b3cd07b0c07d04d4724d65b972 /drivers/clk/clk.c
parent1ddd00276fd5fbd14dd5e366d8777dcd5f2d1b65 (diff)
parentebd47c8434064687ab6641e837144e0a3ea3872d (diff)
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd: "We have a small collection of core framework updates this time, mostly around clk registration by clk providers and debugfs "nice to haves" for rate constraints. I'll highlight that we're now setting the clk_init_data pointer inside struct clk_hw to NULL during clk_register(), which may break some drivers that thought they could use that pointer during normal operations. That change has been sitting in next for a while now but maybe something is still broken. We'l see. Other than that the core framework changes aren't invasive and they're fixing bugs, simplifying, and making things better. On the clk driver side we got the usual addition of new SoC support, new features for existing drivers, and bug fixes scattered throughout. The biggest diffstat is the Amlogic driver that gained CPU clk support in addition to migrating to the new way of specifying clk parents. After that the Qualcomm, i.MX, Mediatek, and Rockchip clk drivers got support for various new SoCs and clock controllers from those vendors. Core: - Drop NULL checks in clk debugfs - Add min/max rates to clk debugfs - Set clk_init_data pointer inside clk_hw to NULL after registration - Make clk_bulk_get_all() return an 'id' corresponding to clock-names - Evict parents from parent cache when they're unregistered New Drivers: - Add clock driver for i.MX8MN SoCs - Support aspeed AST2600 SoCs - Support for Mediatek MT6779 SoCs - Support qcom SM8150 GCC and RPMh clks - Support qcom QCS404 WCSS clks - Add CPU clock support for Armada 7K/8K (specifically AP806 and AP807) - Addition of clock driver for Rockchip rk3308 SoCs Updates: - Add regulator support to the cdce925 clk driver - Add support for Raspberry Pi 4 bcm2711 SoCs - Add SDIO gate support to aspeed driver - Add missing of_node_put() calls in various clk drivers - Migrate Amlogic driver to new clock parent description method - Add DVFS support to Amlogic Meson g12 - Add Amlogic Meson g12a reset support to the axg audio clock controller - Add sm1 support to the Amlogic Meson g12a clock controller - Switch i.MX8MM clock driver to platform driver - Add Hifi4 DSP related clocks for i.MX8QXP SoC - Fix Audio PLL setting and parent clock for USB - Misc i.MX8 clock driver improvements and corrections - Set floor ops for Qualcomm SD clks so that rounding works - Fix "always-on" Clock Domains on Renesas R-Car M1A, RZ/A1, RZ/A2, and RZ/N1 - Enable the Allwinner V3 SoC and fix the i2s clock for H6" * tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (137 commits) clk: Drop !clk checks in debugfs dumping clk: imx: imx8mn: fix pll mux bit clk: imx: imx8mm: fix pll mux bit clk: imx: clk-pll14xx: unbypass PLL by default clk: imx: pll14xx: avoid glitch when set rate clk: mvebu: ap80x: add AP807 clock support clk: mvebu: ap806: Prepare the introduction of AP807 clock support clk: mvebu: ap806: add AP-DCLK (hclk) to system controller driver clk: mvebu: ap806: be more explicit on what SaR is clk: mvebu: ap80x-cpu: add AP807 CPU clock support clk: mvebu: ap806-cpu: prepare mapping of AP807 CPU clock dt-bindings: ap806: Document AP807 clock compatible dt-bindings: ap80x: Document AP807 CPU clock compatible clk: sprd: add missing kfree clk: at91: allow 24 Mhz clock as input for PLL clk: Make clk_bulk_get_all() return a valid "id" clk: actions: Fix factor clk struct member access clk: qcom: rcg: Return failure for RCG update clk: remove extra ---help--- tags in Kconfig clk: add include guard to clk-conf.h ...
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r--drivers/clk/clk.c169
1 files changed, 132 insertions, 37 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index ca99e9db6575..1c677d7f7f53 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -37,6 +37,12 @@ static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);
+static struct hlist_head *all_lists[] = {
+ &clk_root_list,
+ &clk_orphan_list,
+ NULL,
+};
+
/*** private data structures ***/
struct clk_parent_map {
@@ -615,6 +621,8 @@ static void clk_core_get_boundaries(struct clk_core *core,
{
struct clk *clk_user;
+ lockdep_assert_held(&prepare_lock);
+
*min_rate = core->min_rate;
*max_rate = core->max_rate;
@@ -2460,7 +2468,7 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
if (core->parent == parent)
return 0;
- /* verify ops for for multi-parent clks */
+ /* verify ops for multi-parent clks */
if (core->num_parents > 1 && !core->ops->set_parent)
return -EPERM;
@@ -2862,12 +2870,6 @@ static int inited = 0;
static DEFINE_MUTEX(clk_debug_lock);
static HLIST_HEAD(clk_debug_list);
-static struct hlist_head *all_lists[] = {
- &clk_root_list,
- &clk_orphan_list,
- NULL,
-};
-
static struct hlist_head *orphan_list[] = {
&clk_orphan_list,
NULL,
@@ -2876,9 +2878,6 @@ static struct hlist_head *orphan_list[] = {
static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
int level)
{
- if (!c)
- return;
-
seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %5d %6d\n",
level * 3 + 1, "",
30 - level * 3, c->name,
@@ -2893,9 +2892,6 @@ static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
{
struct clk_core *child;
- if (!c)
- return;
-
clk_summary_show_one(s, c, level);
hlist_for_each_entry(child, &c->children, child_node)
@@ -2925,8 +2921,9 @@ DEFINE_SHOW_ATTRIBUTE(clk_summary);
static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)
{
- if (!c)
- return;
+ unsigned long min_rate, max_rate;
+
+ clk_core_get_boundaries(c, &min_rate, &max_rate);
/* This should be JSON format, i.e. elements separated with a comma */
seq_printf(s, "\"%s\": { ", c->name);
@@ -2934,6 +2931,8 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)
seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
seq_printf(s, "\"protect_count\": %d,", c->protect_count);
seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c));
+ seq_printf(s, "\"min_rate\": %lu,", min_rate);
+ seq_printf(s, "\"max_rate\": %lu,", max_rate);
seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c));
seq_printf(s, "\"phase\": %d,", clk_core_get_phase(c));
seq_printf(s, "\"duty_cycle\": %u",
@@ -2944,9 +2943,6 @@ static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level)
{
struct clk_core *child;
- if (!c)
- return;
-
clk_dump_one(s, c, level);
hlist_for_each_entry(child, &c->children, child_node) {
@@ -3042,15 +3038,15 @@ static void possible_parent_show(struct seq_file *s, struct clk_core *core,
*/
parent = clk_core_get_parent_by_index(core, i);
if (parent)
- seq_printf(s, "%s", parent->name);
+ seq_puts(s, parent->name);
else if (core->parents[i].name)
- seq_printf(s, "%s", core->parents[i].name);
+ seq_puts(s, core->parents[i].name);
else if (core->parents[i].fw_name)
seq_printf(s, "<%s>(fw)", core->parents[i].fw_name);
else if (core->parents[i].index >= 0)
- seq_printf(s, "%s",
- of_clk_get_parent_name(core->of_node,
- core->parents[i].index));
+ seq_puts(s,
+ of_clk_get_parent_name(core->of_node,
+ core->parents[i].index));
else
seq_puts(s, "(missing)");
@@ -3093,6 +3089,34 @@ static int clk_duty_cycle_show(struct seq_file *s, void *data)
}
DEFINE_SHOW_ATTRIBUTE(clk_duty_cycle);
+static int clk_min_rate_show(struct seq_file *s, void *data)
+{
+ struct clk_core *core = s->private;
+ unsigned long min_rate, max_rate;
+
+ clk_prepare_lock();
+ clk_core_get_boundaries(core, &min_rate, &max_rate);
+ clk_prepare_unlock();
+ seq_printf(s, "%lu\n", min_rate);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(clk_min_rate);
+
+static int clk_max_rate_show(struct seq_file *s, void *data)
+{
+ struct clk_core *core = s->private;
+ unsigned long min_rate, max_rate;
+
+ clk_prepare_lock();
+ clk_core_get_boundaries(core, &min_rate, &max_rate);
+ clk_prepare_unlock();
+ seq_printf(s, "%lu\n", max_rate);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(clk_max_rate);
+
static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
{
struct dentry *root;
@@ -3104,6 +3128,8 @@ static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
core->dentry = root;
debugfs_create_ulong("clk_rate", 0444, root, &core->rate);
+ debugfs_create_file("clk_min_rate", 0444, root, core, &clk_min_rate_fops);
+ debugfs_create_file("clk_max_rate", 0444, root, core, &clk_max_rate_fops);
debugfs_create_ulong("clk_accuracy", 0444, root, &core->accuracy);
debugfs_create_u32("clk_phase", 0444, root, &core->phase);
debugfs_create_file("clk_flags", 0444, root, core, &clk_flags_fops);
@@ -3513,9 +3539,9 @@ static int clk_cpy_name(const char **dst_p, const char *src, bool must_exist)
return 0;
}
-static int clk_core_populate_parent_map(struct clk_core *core)
+static int clk_core_populate_parent_map(struct clk_core *core,
+ const struct clk_init_data *init)
{
- const struct clk_init_data *init = core->hw->init;
u8 num_parents = init->num_parents;
const char * const *parent_names = init->parent_names;
const struct clk_hw **parent_hws = init->parent_hws;
@@ -3595,6 +3621,14 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
{
int ret;
struct clk_core *core;
+ const struct clk_init_data *init = hw->init;
+
+ /*
+ * The init data is not supposed to be used outside of registration path.
+ * Set it to NULL so that provider drivers can't use it either and so that
+ * we catch use of hw->init early on in the core.
+ */
+ hw->init = NULL;
core = kzalloc(sizeof(*core), GFP_KERNEL);
if (!core) {
@@ -3602,17 +3636,17 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
goto fail_out;
}
- core->name = kstrdup_const(hw->init->name, GFP_KERNEL);
+ core->name = kstrdup_const(init->name, GFP_KERNEL);
if (!core->name) {
ret = -ENOMEM;
goto fail_name;
}
- if (WARN_ON(!hw->init->ops)) {
+ if (WARN_ON(!init->ops)) {
ret = -EINVAL;
goto fail_ops;
}
- core->ops = hw->init->ops;
+ core->ops = init->ops;
if (dev && pm_runtime_enabled(dev))
core->rpm_enabled = true;
@@ -3621,13 +3655,13 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
if (dev && dev->driver)
core->owner = dev->driver->owner;
core->hw = hw;
- core->flags = hw->init->flags;
- core->num_parents = hw->init->num_parents;
+ core->flags = init->flags;
+ core->num_parents = init->num_parents;
core->min_rate = 0;
core->max_rate = ULONG_MAX;
hw->core = core;
- ret = clk_core_populate_parent_map(core);
+ ret = clk_core_populate_parent_map(core, init);
if (ret)
goto fail_parents;
@@ -3766,6 +3800,34 @@ static const struct clk_ops clk_nodrv_ops = {
.set_parent = clk_nodrv_set_parent,
};
+static void clk_core_evict_parent_cache_subtree(struct clk_core *root,
+ struct clk_core *target)
+{
+ int i;
+ struct clk_core *child;
+
+ for (i = 0; i < root->num_parents; i++)
+ if (root->parents[i].core == target)
+ root->parents[i].core = NULL;
+
+ hlist_for_each_entry(child, &root->children, child_node)
+ clk_core_evict_parent_cache_subtree(child, target);
+}
+
+/* Remove this clk from all parent caches */
+static void clk_core_evict_parent_cache(struct clk_core *core)
+{
+ struct hlist_head **lists;
+ struct clk_core *root;
+
+ lockdep_assert_held(&prepare_lock);
+
+ for (lists = all_lists; *lists; lists++)
+ hlist_for_each_entry(root, *lists, child_node)
+ clk_core_evict_parent_cache_subtree(root, core);
+
+}
+
/**
* clk_unregister - unregister a currently registered clock
* @clk: clock to unregister
@@ -3804,6 +3866,8 @@ void clk_unregister(struct clk *clk)
clk_core_set_parent_nolock(child, NULL);
}
+ clk_core_evict_parent_cache(clk->core);
+
hlist_del_init(&clk->core->child_node);
if (clk->core->prepare_count)
@@ -4345,12 +4409,43 @@ void devm_of_clk_del_provider(struct device *dev)
}
EXPORT_SYMBOL(devm_of_clk_del_provider);
-/*
- * Beware the return values when np is valid, but no clock provider is found.
- * If name == NULL, the function returns -ENOENT.
- * If name != NULL, the function returns -EINVAL. This is because
- * of_parse_phandle_with_args() is called even if of_property_match_string()
- * returns an error.
+/**
+ * of_parse_clkspec() - Parse a DT clock specifier for a given device node
+ * @np: device node to parse clock specifier from
+ * @index: index of phandle to parse clock out of. If index < 0, @name is used
+ * @name: clock name to find and parse. If name is NULL, the index is used
+ * @out_args: Result of parsing the clock specifier
+ *
+ * Parses a device node's "clocks" and "clock-names" properties to find the
+ * phandle and cells for the index or name that is desired. The resulting clock
+ * specifier is placed into @out_args, or an errno is returned when there's a
+ * parsing error. The @index argument is ignored if @name is non-NULL.
+ *
+ * Example:
+ *
+ * phandle1: clock-controller@1 {
+ * #clock-cells = <2>;
+ * }
+ *
+ * phandle2: clock-controller@2 {
+ * #clock-cells = <1>;
+ * }
+ *
+ * clock-consumer@3 {
+ * clocks = <&phandle1 1 2 &phandle2 3>;
+ * clock-names = "name1", "name2";
+ * }
+ *
+ * To get a device_node for `clock-controller@2' node you may call this
+ * function a few different ways:
+ *
+ * of_parse_clkspec(clock-consumer@3, -1, "name2", &args);
+ * of_parse_clkspec(clock-consumer@3, 1, NULL, &args);
+ * of_parse_clkspec(clock-consumer@3, 1, "name2", &args);
+ *
+ * Return: 0 upon successfully parsing the clock specifier. Otherwise, -ENOENT
+ * if @name is NULL or -EINVAL if @name is non-NULL and it can't be found in
+ * the "clock-names" property of @np.
*/
static int of_parse_clkspec(const struct device_node *np, int index,
const char *name, struct of_phandle_args *out_args)