diff options
Diffstat (limited to 'drivers/clk/ti/clk.c')
-rw-r--r-- | drivers/clk/ti/clk.c | 71 |
1 files changed, 60 insertions, 11 deletions
diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 1862958ab4..f2117fef7c 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -7,6 +7,7 @@ * Tero Kristo <t-kristo@ti.com> */ +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clkdev.h> @@ -15,6 +16,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/list.h> +#include <linux/minmax.h> #include <linux/regmap.h> #include <linux/string_helpers.h> #include <linux/memblock.h> @@ -114,20 +116,26 @@ int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops) /* * Eventually we could standardize to using '_' for clk-*.c files to follow the - * TRM naming and leave out the tmp name here. + * TRM naming. */ static struct device_node *ti_find_clock_provider(struct device_node *from, const char *name) { + char *tmp __free(kfree) = NULL; struct device_node *np; bool found = false; const char *n; - char *tmp; + char *p; tmp = kstrdup_and_replace(name, '-', '_', GFP_KERNEL); if (!tmp) return NULL; + /* Ignore a possible address for the node name */ + p = strchr(tmp, '@'); + if (p) + *p = '\0'; + /* Node named "clock" with "clock-output-names" */ for_each_of_allnodes_from(from, np) { if (of_property_read_string_index(np, "clock-output-names", @@ -140,7 +148,6 @@ static struct device_node *ti_find_clock_provider(struct device_node *from, break; } } - kfree(tmp); if (found) { of_node_put(from); @@ -148,7 +155,7 @@ static struct device_node *ti_find_clock_provider(struct device_node *from, } /* Fall back to using old node name base provider name */ - return of_find_node_by_name(from, name); + return of_find_node_by_name(from, tmp); } /** @@ -301,8 +308,9 @@ int __init ti_clk_retry_init(struct device_node *node, void *user, int ti_clk_get_reg_addr(struct device_node *node, int index, struct clk_omap_reg *reg) { - u32 val; - int i; + u32 clksel_addr, val; + bool is_clksel = false; + int i, err; for (i = 0; i < CLK_MAX_MEMMAPS; i++) { if (clocks_node_ptr[i] == node->parent) @@ -318,21 +326,62 @@ int ti_clk_get_reg_addr(struct device_node *node, int index, reg->index = i; - if (of_property_read_u32_index(node, "reg", index, &val)) { - if (of_property_read_u32_index(node->parent, "reg", - index, &val)) { - pr_err("%pOFn or parent must have reg[%d]!\n", - node, index); + if (of_device_is_compatible(node->parent, "ti,clksel")) { + err = of_property_read_u32_index(node->parent, "reg", index, &clksel_addr); + if (err) { + pr_err("%pOFn parent clksel must have reg[%d]!\n", node, index); return -EINVAL; } + is_clksel = true; } + err = of_property_read_u32_index(node, "reg", index, &val); + if (err && is_clksel) { + /* Legacy clksel with no reg and a possible ti,bit-shift property */ + reg->offset = clksel_addr; + reg->bit = ti_clk_get_legacy_bit_shift(node); + reg->ptr = NULL; + + return 0; + } + + /* Updated clksel clock with a proper reg property */ + if (is_clksel) { + reg->offset = clksel_addr; + reg->bit = val; + reg->ptr = NULL; + return 0; + } + + /* Other clocks that may or may not have ti,bit-shift property */ reg->offset = val; + reg->bit = ti_clk_get_legacy_bit_shift(node); reg->ptr = NULL; return 0; } +/** + * ti_clk_get_legacy_bit_shift - get bit shift for a clock register + * @node: device node for the clock + * + * Gets the clock register bit shift using the legacy ti,bit-shift + * property. Only needed for legacy clock, and can be eventually + * dropped once all the composite clocks use a clksel node with a + * proper reg property. + */ +int ti_clk_get_legacy_bit_shift(struct device_node *node) +{ + int err; + u32 val; + + err = of_property_read_u32(node, "ti,bit-shift", &val); + if (!err && in_range(val, 0, 32)) + return val; + + return 0; +} + void ti_clk_latch(struct clk_omap_reg *reg, s8 shift) { u32 latch; |