clk: keystone: sci-clk: probe clocks from DT instead of firmware

Probing all the available clocks from the PM firmware takes quite a lot
of time, increasing boot time. Instead, implement functionality that
parses only the used clocks from DT, and registers these to clock core.
This way, the boot time is greatly improved.

Additionally, provide a Kconfig option for parsing all the clocks from
firmware, if someone requires this. It is mostly useful as a debugging
functionality if we want to inspect the whole clock tree.

Acked-by: Santosh Shilimkar <ssantosh@kernel.org>
Signed-off-by: Tero Kristo <t-kristo@ti.com>
This commit is contained in:
Tero Kristo 2019-05-28 16:10:22 +03:00
parent 4bfce5aba9
commit 8e48b33f9d
2 changed files with 141 additions and 0 deletions

View File

@ -14,3 +14,14 @@ config TI_SCI_CLK
This adds the clock driver support over TI System Control Interface.
If you wish to use clock resources from the PMMC firmware, say Y.
Otherwise, say N.
config TI_SCI_CLK_PROBE_FROM_FW
bool "Probe available clocks from firmware"
depends on TI_SCI_CLK
default n
help
Forces the TI SCI clock driver to probe available clocks from the
firmware. By default, only the used clocks are probed from DT.
This is mostly only useful for debugging purposes, and will
increase the boot time of the device. If you want the clocks probed
from firmware, say Y. Otherwise, say N.

View File

@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/soc/ti/ti_sci_protocol.h>
#include <linux/bsearch.h>
#include <linux/list_sort.h>
#define SCI_CLK_SSC_ENABLE BIT(0)
#define SCI_CLK_ALLOW_FREQ_CHANGE BIT(1)
@ -52,6 +53,7 @@ struct sci_clk_provider {
* @num_parents: Number of parents for this clock
* @provider: Master clock provider
* @flags: Flags for the clock
* @node: Link for handling clocks probed via DT
*/
struct sci_clk {
struct clk_hw hw;
@ -60,6 +62,7 @@ struct sci_clk {
u8 num_parents;
struct sci_clk_provider *provider;
u8 flags;
struct list_head node;
};
#define to_sci_clk(_hw) container_of(_hw, struct sci_clk, hw)
@ -403,6 +406,7 @@ static const struct of_device_id ti_sci_clk_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ti_sci_clk_of_match);
#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW
static int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider)
{
int ret;
@ -479,6 +483,124 @@ static int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider)
return 0;
}
#else
static int _cmp_sci_clk_list(void *priv, struct list_head *a,
struct list_head *b)
{
struct sci_clk *ca = container_of(a, struct sci_clk, node);
struct sci_clk *cb = container_of(b, struct sci_clk, node);
return _cmp_sci_clk(ca, &cb);
}
static int ti_sci_scan_clocks_from_dt(struct sci_clk_provider *provider)
{
struct device *dev = provider->dev;
struct device_node *np = NULL;
int ret;
int index;
struct of_phandle_args args;
struct list_head clks;
struct sci_clk *sci_clk, *prev;
int num_clks = 0;
int num_parents;
int clk_id;
const char * const clk_names[] = {
"clocks", "assigned-clocks", "assigned-clock-parents", NULL
};
const char * const *clk_name;
INIT_LIST_HEAD(&clks);
clk_name = clk_names;
while (*clk_name) {
np = of_find_node_with_property(np, *clk_name);
if (!np) {
clk_name++;
break;
}
if (!of_device_is_available(np))
continue;
index = 0;
do {
ret = of_parse_phandle_with_args(np, *clk_name,
"#clock-cells", index,
&args);
if (ret)
break;
if (args.args_count == 2 && args.np == dev->of_node) {
sci_clk = devm_kzalloc(dev, sizeof(*sci_clk),
GFP_KERNEL);
if (!sci_clk)
return -ENOMEM;
sci_clk->dev_id = args.args[0];
sci_clk->clk_id = args.args[1];
sci_clk->provider = provider;
provider->ops->get_num_parents(provider->sci,
sci_clk->dev_id,
sci_clk->clk_id,
&sci_clk->num_parents);
list_add_tail(&sci_clk->node, &clks);
num_clks++;
num_parents = sci_clk->num_parents;
if (num_parents == 1)
num_parents = 0;
clk_id = args.args[1] + 1;
while (num_parents--) {
sci_clk = devm_kzalloc(dev,
sizeof(*sci_clk),
GFP_KERNEL);
if (!sci_clk)
return -ENOMEM;
sci_clk->dev_id = args.args[0];
sci_clk->clk_id = clk_id++;
sci_clk->provider = provider;
list_add_tail(&sci_clk->node, &clks);
num_clks++;
}
}
index++;
} while (args.np);
}
list_sort(NULL, &clks, _cmp_sci_clk_list);
provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk),
GFP_KERNEL);
if (!provider->clocks)
return -ENOMEM;
num_clks = 0;
prev = NULL;
list_for_each_entry(sci_clk, &clks, node) {
if (prev && prev->dev_id == sci_clk->dev_id &&
prev->clk_id == sci_clk->clk_id)
continue;
provider->clocks[num_clks++] = sci_clk;
prev = sci_clk;
}
provider->num_clocks = num_clks;
return 0;
}
#endif
/**
* ti_sci_clk_probe - Probe function for the TI SCI clock driver
* @pdev: platform device pointer to be probed
@ -509,11 +631,19 @@ static int ti_sci_clk_probe(struct platform_device *pdev)
provider->ops = &handle->ops.clk_ops;
provider->dev = dev;
#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW
ret = ti_sci_scan_clocks_from_fw(provider);
if (ret) {
dev_err(dev, "scan clocks from FW failed: %d\n", ret);
return ret;
}
#else
ret = ti_sci_scan_clocks_from_dt(provider);
if (ret) {
dev_err(dev, "scan clocks from DT failed: %d\n", ret);
return ret;
}
#endif
ret = ti_sci_init_clocks(provider);
if (ret) {