Messages in this thread Patch in this message | | | From | David Yang <> | Subject | [PATCH v7 4/9] clk: hisilicon: Add helper functions for platform driver | Date | Thu, 23 Mar 2023 00:41:52 +0800 |
| |
Helper functions extract common operations on platform drivers.
This patch is part of devm APIs migration.
Signed-off-by: David Yang <mmyangfl@gmail.com> --- drivers/clk/hisilicon/clk-hi6220.c | 6 +- drivers/clk/hisilicon/clk.c | 132 ++++++++++++++++++++++++++++- drivers/clk/hisilicon/clk.h | 75 +++++++++++++--- drivers/clk/hisilicon/crg.h | 5 ++ drivers/clk/hisilicon/reset.c | 65 +++++++++++++- 5 files changed, 264 insertions(+), 19 deletions(-)
diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index e7cdf72d4..d9de83a19 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -191,7 +191,7 @@ static void __init hi6220_clk_sys_init(struct device_node *np) hisi_clk_register_mux(hi6220_mux_clks_sys, ARRAY_SIZE(hi6220_mux_clks_sys), clk_data); - hi6220_clk_register_divider(hi6220_div_clks_sys, + hi6220_clk_register_divider(NULL, hi6220_div_clks_sys, ARRAY_SIZE(hi6220_div_clks_sys), clk_data); } CLK_OF_DECLARE_DRIVER(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init); @@ -248,7 +248,7 @@ static void __init hi6220_clk_media_init(struct device_node *np) hisi_clk_register_mux(hi6220_mux_clks_media, ARRAY_SIZE(hi6220_mux_clks_media), clk_data); - hi6220_clk_register_divider(hi6220_div_clks_media, + hi6220_clk_register_divider(NULL, hi6220_div_clks_media, ARRAY_SIZE(hi6220_div_clks_media), clk_data); } CLK_OF_DECLARE_DRIVER(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init); @@ -279,7 +279,7 @@ static void __init hi6220_clk_power_init(struct device_node *np) hisi_clk_register_gate(hi6220_gate_clks_power, ARRAY_SIZE(hi6220_gate_clks_power), clk_data); - hi6220_clk_register_divider(hi6220_div_clks_power, + hi6220_clk_register_divider(NULL, hi6220_div_clks_power, ARRAY_SIZE(hi6220_div_clks_power), clk_data); } CLK_OF_DECLARE(hi6220_clk_power, "hisilicon,hi6220-pmctrl", hi6220_clk_power_init); diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index a949be5fa..439fd154b 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -58,6 +58,13 @@ EXPORT_SYMBOL_GPL(hisi_clk_init); void hisi_clk_free(struct device_node *np, struct hisi_clock_data *data) { + if (data->clks) { + if (data->clks->fixed_rate_clks_num) + hisi_clk_unregister_fixed_rate(data); + if (data->clks->fixed_factor_clks_num) + hisi_clk_unregister_fixed_factor(data); + } + of_clk_del_provider(np); kfree(data->clk_data.clks); kfree(data); @@ -260,7 +267,7 @@ int hisi_clk_register_gate(const struct hisi_gate_clock *clks, } EXPORT_SYMBOL_GPL(hisi_clk_register_gate); -void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, +int hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, int nums, struct hisi_clock_data *data) { struct clk *clk; @@ -286,11 +293,14 @@ void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } + + return 0; } EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep); -void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, - int nums, struct hisi_clock_data *data) +int hi6220_clk_register_divider(struct device *dev, + const struct hi6220_divider_clock *clks, + int nums, struct hisi_clock_data *data) { struct clk *clk; void __iomem *base = data->base; @@ -308,7 +318,7 @@ void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, clks[i].name); - continue; + return PTR_ERR(clk); } if (clks[i].alias) @@ -316,4 +326,118 @@ void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, data->clk_data.clks[clks[i].id] = clk; } + + return 0; +} +EXPORT_SYMBOL_GPL(hi6220_clk_register_divider); + +int hisi_clk_register(struct device *dev, const struct hisi_clocks *clks, + struct hisi_clock_data *data) +{ + int ret; + +#define do_hisi_clk_register(type) do { \ + if (clks->type##_clks_num) { \ + ret = hisi_clk_register_##type(dev, clks->type##_clks, \ + clks->type##_clks_num, data); \ + if (ret) \ + return ret; \ + } \ +} while (0) + + do_hisi_clk_register(mux); + do_hisi_clk_register(phase); + do_hisi_clk_register(divider); + do_hisi_clk_register(gate); + do_hisi_clk_register(gate_sep); + + if (clks->clk_register_customized && clks->customized_clks_num) { + ret = clks->clk_register_customized(dev, clks->customized_clks, + clks->customized_clks_num, data); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(hisi_clk_register); + +int hisi_clk_early_init(struct device_node *np, const struct hisi_clocks *clks) +{ + struct hisi_clock_data *data; + int ret; + + data = hisi_clk_init(np, clks->nr); + if (!data) + return -ENOMEM; + data->clks = clks; + + ret = hisi_clk_register_fixed_rate(clks->fixed_rate_clks, + clks->fixed_rate_clks_num, data); + if (ret) + goto err; + + ret = hisi_clk_register_fixed_factor(clks->fixed_factor_clks, + clks->fixed_factor_clks_num, data); + if (ret) + goto err; + + np->data = data; + return 0; + +err: + hisi_clk_free(np, data); + return ret; +} +EXPORT_SYMBOL_GPL(hisi_clk_early_init); + +int hisi_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct hisi_clocks *clks; + struct hisi_clock_data *data; + int ret; + + clks = of_device_get_match_data(dev); + if (!clks) + return -ENOENT; + + if (!np->data) { + ret = hisi_clk_early_init(np, clks); + if (ret) + return ret; + } + + data = np->data; + np->data = NULL; + + if (clks->prologue) { + ret = clks->prologue(dev, data); + if (ret) + goto err; + } + + ret = hisi_clk_register(dev, clks, data); + if (ret) + goto err; + + platform_set_drvdata(pdev, data); + return 0; + +err: + hisi_clk_free(np, data); + return ret; +} +EXPORT_SYMBOL_GPL(hisi_clk_probe); + +int hisi_clk_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct hisi_clock_data *data = platform_get_drvdata(pdev); + + hisi_clk_free(np, data); + return 0; } +EXPORT_SYMBOL_GPL(hisi_clk_remove); diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 30b6d4405..daa85acda 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -17,10 +17,22 @@ #include <linux/spinlock.h> struct platform_device; +struct hisi_clocks; +/* + * (Virtual) fixed clocks, often depended by crucial peripherals, require + * early initialization before device probing, thus cannot use devm APIs. + * Otherwise, kernel will defer those peripherals, causing boot failure. + * + * fixed_rate and fixed_factor clocks are driver-managed. They are freed by + * `hisi_clk_free` altogether. + * + * Other clocks are devm-managed. + */ struct hisi_clock_data { struct clk_onecell_data clk_data; void __iomem *base; + const struct hisi_clocks *clks; }; struct hisi_fixed_rate_clock { @@ -103,6 +115,38 @@ struct hisi_gate_clock { const char *alias; }; +struct hisi_clocks { + int nr; + + int (*prologue)(struct device *dev, struct hisi_clock_data *data); + + const struct hisi_fixed_rate_clock *fixed_rate_clks; + int fixed_rate_clks_num; + + const struct hisi_fixed_factor_clock *fixed_factor_clks; + int fixed_factor_clks_num; + + const struct hisi_mux_clock *mux_clks; + int mux_clks_num; + + const struct hisi_phase_clock *phase_clks; + int phase_clks_num; + + const struct hisi_divider_clock *divider_clks; + int divider_clks_num; + + const struct hisi_gate_clock *gate_clks; + int gate_clks_num; + + const struct hisi_gate_clock *gate_sep_clks; + int gate_sep_clks_num; + + const void *customized_clks; + int customized_clks_num; + int (*clk_register_customized)(struct device *dev, const void *clks, + int num, struct hisi_clock_data *data); +}; + struct clk *hisi_register_clkgate_sep(struct device *, const char *, const char *, unsigned long, void __iomem *, u8, @@ -118,6 +162,7 @@ int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *, int, struct hisi_clock_data *); int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *, int, struct hisi_clock_data *); + int hisi_clk_register_mux(const struct hisi_mux_clock *, int, struct hisi_clock_data *); struct clk *clk_register_hisi_phase(struct device *dev, @@ -130,12 +175,22 @@ int hisi_clk_register_divider(const struct hisi_divider_clock *, int, struct hisi_clock_data *); int hisi_clk_register_gate(const struct hisi_gate_clock *, int, struct hisi_clock_data *); -void hisi_clk_register_gate_sep(const struct hisi_gate_clock *, - int, struct hisi_clock_data *); -void hi6220_clk_register_divider(const struct hi6220_divider_clock *, - int, struct hisi_clock_data *); +int hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, + int nums, struct hisi_clock_data *data); +int hi6220_clk_register_divider(struct device *dev, + const struct hi6220_divider_clock *clks, + int nums, struct hisi_clock_data *data); + +int hisi_clk_register(struct device *dev, const struct hisi_clocks *clks, + struct hisi_clock_data *data); + +/* helper functions for platform driver */ + +int hisi_clk_early_init(struct device_node *np, const struct hisi_clocks *clks); +int hisi_clk_probe(struct platform_device *pdev); +int hisi_clk_remove(struct platform_device *pdev); -#define hisi_clk_unregister(type) \ +#define hisi_clk_unregister_fn(type) \ static inline \ void hisi_clk_unregister_##type(const struct hisi_##type##_clock *clks, \ int nums, struct hisi_clock_data *data) \ @@ -149,10 +204,10 @@ void hisi_clk_unregister_##type(const struct hisi_##type##_clock *clks, \ } \ } -hisi_clk_unregister(fixed_rate) -hisi_clk_unregister(fixed_factor) -hisi_clk_unregister(mux) -hisi_clk_unregister(divider) -hisi_clk_unregister(gate) +hisi_clk_unregister_fn(fixed_rate) +hisi_clk_unregister_fn(fixed_factor) +hisi_clk_unregister_fn(mux) +hisi_clk_unregister_fn(divider) +hisi_clk_unregister_fn(gate) #endif /* __HISI_CLK_H */ diff --git a/drivers/clk/hisilicon/crg.h b/drivers/clk/hisilicon/crg.h index 803f6ba6d..d9544f1f2 100644 --- a/drivers/clk/hisilicon/crg.h +++ b/drivers/clk/hisilicon/crg.h @@ -22,4 +22,9 @@ struct hisi_crg_dev { const struct hisi_crg_funcs *funcs; }; +/* helper functions for platform driver */ + +int hisi_crg_probe(struct platform_device *pdev); +int hisi_crg_remove(struct platform_device *pdev); + #endif /* __HISI_CRG_H */ diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c index 93cee17db..471b5670e 100644 --- a/drivers/clk/hisilicon/reset.c +++ b/drivers/clk/hisilicon/reset.c @@ -5,12 +5,16 @@ * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. */ +#include <linux/device.h> #include <linux/io.h> +#include <linux/kernel.h> #include <linux/of_address.h> -#include <linux/platform_device.h> +#include <linux/of_device.h> #include <linux/reset-controller.h> -#include <linux/slab.h> #include <linux/spinlock.h> + +#include "clk.h" +#include "crg.h" #include "reset.h" #define HISI_RESET_BIT_MASK 0x1f @@ -116,3 +120,60 @@ void hisi_reset_exit(struct hisi_reset_controller *rstc) reset_controller_unregister(&rstc->rcdev); } EXPORT_SYMBOL_GPL(hisi_reset_exit); + +int hisi_crg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct hisi_clocks *clks; + struct hisi_crg_dev *crg; + int ret; + + clks = of_device_get_match_data(dev); + if (!clks) + return -ENOENT; + + crg = devm_kmalloc(dev, sizeof(*crg), GFP_KERNEL); + if (!crg) + return -ENOMEM; + + if (!np->data) { + ret = hisi_clk_early_init(np, clks); + if (ret) + return ret; + } + + crg->clk_data = np->data; + np->data = NULL; + + ret = hisi_clk_register(dev, clks, crg->clk_data); + if (ret) + goto err; + + crg->rstc = hisi_reset_init(pdev); + if (!crg->rstc) { + ret = -ENOMEM; + goto err; + } + + platform_set_drvdata(pdev, crg); + return 0; + +err: + hisi_clk_free(np, crg->clk_data); + return ret; +} +EXPORT_SYMBOL_GPL(hisi_crg_probe); + +int hisi_crg_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct hisi_crg_dev *crg = platform_get_drvdata(pdev); + + hisi_reset_exit(crg->rstc); + crg->funcs->unregister_clks(pdev); + hisi_clk_free(np, crg->clk_data); + return 0; +} +EXPORT_SYMBOL_GPL(hisi_crg_remove); -- 2.39.2
| |