diff options
Diffstat (limited to 'drivers/clk/rockchip/clk.c')
-rw-r--r-- | drivers/clk/rockchip/clk.c | 122 |
1 files changed, 107 insertions, 15 deletions
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 4059d9365ae6..cbf93ea119a9 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -19,6 +19,7 @@ #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/mfd/syscon.h> +#include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/reboot.h> @@ -197,12 +198,6 @@ static void rockchip_fractional_approximation(struct clk_hw *hw, clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n); } -static void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, - struct clk *clk, unsigned int id) -{ - ctx->clk_data.clks[id] = clk; -} - static struct clk *rockchip_clk_register_frac_branch( struct rockchip_clk_provider *ctx, const char *name, const char *const *parent_names, u8 num_parents, @@ -292,7 +287,7 @@ static struct clk *rockchip_clk_register_frac_branch( return mux_clk; } - rockchip_clk_add_lookup(ctx, mux_clk, child->id); + rockchip_clk_set_lookup(ctx, mux_clk, child->id); /* notifier on the fraction divider to catch rate changes */ if (frac->mux_frac_idx >= 0) { @@ -359,14 +354,17 @@ static struct clk *rockchip_clk_register_factor_branch(const char *name, return hw->clk; } -struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, - void __iomem *base, - unsigned long nr_clks) +static struct rockchip_clk_provider *rockchip_clk_init_base( + struct device_node *np, void __iomem *base, + unsigned long nr_clks, bool has_late_clocks) { struct rockchip_clk_provider *ctx; struct clk **clk_table; + struct clk *default_clk_val; int i; + default_clk_val = ERR_PTR(has_late_clocks ? -EPROBE_DEFER : -ENOENT); + ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL); if (!ctx) return ERR_PTR(-ENOMEM); @@ -376,7 +374,7 @@ struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, goto err_free; for (i = 0; i < nr_clks; ++i) - clk_table[i] = ERR_PTR(-ENOENT); + clk_table[i] = default_clk_val; ctx->reg_base = base; ctx->clk_data.clks = clk_table; @@ -393,8 +391,33 @@ err_free: kfree(ctx); return ERR_PTR(-ENOMEM); } + +struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, + void __iomem *base, + unsigned long nr_clks) +{ + return rockchip_clk_init_base(np, base, nr_clks, false); +} EXPORT_SYMBOL_GPL(rockchip_clk_init); +struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np, + void __iomem *base, + unsigned long nr_clks) +{ + return rockchip_clk_init_base(np, base, nr_clks, true); +} +EXPORT_SYMBOL_GPL(rockchip_clk_init_early); + +void rockchip_clk_finalize(struct rockchip_clk_provider *ctx) +{ + int i; + + for (i = 0; i < ctx->clk_data.clk_num; ++i) + if (ctx->clk_data.clks[i] == ERR_PTR(-EPROBE_DEFER)) + ctx->clk_data.clks[i] = ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL_GPL(rockchip_clk_finalize); + void rockchip_clk_of_add_provider(struct device_node *np, struct rockchip_clk_provider *ctx) { @@ -424,21 +447,62 @@ void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, continue; } - rockchip_clk_add_lookup(ctx, clk, list->id); + rockchip_clk_set_lookup(ctx, clk, list->id); } } EXPORT_SYMBOL_GPL(rockchip_clk_register_plls); +unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list, + unsigned int nr_clk) +{ + unsigned long max = 0; + unsigned int idx; + + for (idx = 0; idx < nr_clk; idx++, list++) { + if (list->id > max) + max = list->id; + if (list->child && list->child->id > max) + max = list->child->id; + } + + return max; +} +EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id); + +static struct platform_device *rockchip_clk_register_gate_link( + struct device *parent_dev, + struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *clkbr) +{ + struct rockchip_gate_link_platdata gate_link_pdata = { + .ctx = ctx, + .clkbr = clkbr, + }; + + struct platform_device_info pdevinfo = { + .parent = parent_dev, + .name = "rockchip-gate-link-clk", + .id = clkbr->id, + .fwnode = dev_fwnode(parent_dev), + .of_node_reused = true, + .data = &gate_link_pdata, + .size_data = sizeof(gate_link_pdata), + }; + + return platform_device_register_full(&pdevinfo); +} + void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, struct rockchip_clk_branch *list, unsigned int nr_clk) { - struct clk *clk = NULL; + struct clk *clk; unsigned int idx; unsigned long flags; for (idx = 0; idx < nr_clk; idx++, list++) { flags = list->flags; + clk = NULL; /* catch simple muxes */ switch (list->branch_type) { @@ -553,6 +617,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, list->div_width, list->div_flags, ctx->reg_base, &ctx->lock); break; + case branch_linked_gate: + /* must be registered late, fall-through for error message */ + break; } /* none of the cases above matched */ @@ -568,11 +635,36 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, continue; } - rockchip_clk_add_lookup(ctx, clk, list->id); + rockchip_clk_set_lookup(ctx, clk, list->id); } } EXPORT_SYMBOL_GPL(rockchip_clk_register_branches); +void rockchip_clk_register_late_branches(struct device *dev, + struct rockchip_clk_provider *ctx, + struct rockchip_clk_branch *list, + unsigned int nr_clk) +{ + unsigned int idx; + + for (idx = 0; idx < nr_clk; idx++, list++) { + struct platform_device *pdev = NULL; + + switch (list->branch_type) { + case branch_linked_gate: + pdev = rockchip_clk_register_gate_link(dev, ctx, list); + break; + default: + dev_err(dev, "unknown clock type %d\n", list->branch_type); + break; + } + + if (!pdev) + dev_err(dev, "failed to register device for clock %s\n", list->name); + } +} +EXPORT_SYMBOL_GPL(rockchip_clk_register_late_branches); + void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, unsigned int lookup_id, const char *name, const char *const *parent_names, @@ -592,7 +684,7 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx, return; } - rockchip_clk_add_lookup(ctx, clk, lookup_id); + rockchip_clk_set_lookup(ctx, clk, lookup_id); } EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk); |