From: Dong Aisheng <aisheng.dong@nxp.com> To: linux-clk@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, sboyd@kernel.org, mturquette@baylibre.com, shawnguo@kernel.org, fabio.estevam@nxp.com, linux-imx@nxp.com, kernel@pengutronix.de, Dong Aisheng <aisheng.dong@nxp.com> Subject: [PATCH RESEND v5 08/11] clk: imx: imx8qxp-lpcg: add parsing clocks from device tree Date: Sun, 17 Nov 2019 20:25:16 +0800 [thread overview] Message-ID: <1573993519-14308-9-git-send-email-aisheng.dong@nxp.com> (raw) In-Reply-To: <1573993519-14308-1-git-send-email-aisheng.dong@nxp.com> One LPCG controller supports up to 8 clock outputs while each of them is fixed to 4 bits. It supports only gating function with fixed bits. So we can use the clk-indices to fetch the corresponding clock idx from device tree. With this way, we can write a generic LPCG clock drivers. This patch add that support to parse clocks from device tree. Cc: Stephen Boyd <sboyd@kernel.org> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <kernel@pengutronix.de> Cc: Michael Turquette <mturquette@baylibre.com> Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> --- Changelog: v4->v5: * add error check for devm_of_clk_add_hw_provider * add more commit message * a few minor updates v3->v4: * remove hw_autogate which is not still used by driver * use clock-indices to indicate LPCG clock bit offset v1->v3: no changes --- drivers/clk/imx/clk-imx8qxp-lpcg.c | 124 +++++++++++++++++++++++++++++ drivers/clk/imx/clk-lpcg-scu.c | 7 ++ drivers/clk/imx/clk-scu.h | 1 + 3 files changed, 132 insertions(+) diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.c b/drivers/clk/imx/clk-imx8qxp-lpcg.c index c0aff7ca6374..4d53f2b34191 100644 --- a/drivers/clk/imx/clk-imx8qxp-lpcg.c +++ b/drivers/clk/imx/clk-imx8qxp-lpcg.c @@ -9,6 +9,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -157,6 +158,122 @@ static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = { .num_max = IMX_LSIO_LPCG_CLK_END, }; +#define IMX_LPCG_MAX_CLKS 8 + +static struct clk_hw *imx_lpcg_of_clk_src_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_hw_onecell_data *hw_data = data; + unsigned int idx = clkspec->args[0] / 4; + + if (idx >= hw_data->num) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return hw_data->hws[idx]; +} + +static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev, + struct device_node *np) +{ + const char *output_names[IMX_LPCG_MAX_CLKS]; + const char *parent_names[IMX_LPCG_MAX_CLKS]; + unsigned int bit_offset[IMX_LPCG_MAX_CLKS]; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clk_hws; + struct resource *res; + void __iomem *base; + int count; + int idx; + int ret; + int i; + + if (!of_device_is_compatible(np, "fsl,imx8qxp-lpcg")) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + count = of_property_count_u32_elems(np, "clock-indices"); + if (count < 0) { + dev_err(&pdev->dev, "failed to count clocks\n"); + return -EINVAL; + } + + /* + * A trick here is that we set the num of clks to the MAX instead + * of the count from clock-indices because one LPCG supports up to + * 8 clock outputs which each of them is fixed to 4 bits. Then we can + * easily get the clock by clk-indices (bit-offset) / 4. + * And the cost is very limited few pointers. + */ + + clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws, + IMX_LPCG_MAX_CLKS), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX_LPCG_MAX_CLKS; + clk_hws = clk_data->hws; + + ret = of_property_read_u32_array(np, "clock-indices", bit_offset, + count); + if (ret < 0) { + dev_err(&pdev->dev, "failed to read clock-indices\n"); + return -EINVAL; + } + + ret = of_clk_parent_fill(np, parent_names, count); + if (ret != count) { + dev_err(&pdev->dev, "failed to get clock parent names\n"); + return count; + } + + ret = of_property_read_string_array(np, "clock-output-names", + output_names, count); + if (ret != count) { + dev_err(&pdev->dev, "failed to read clock-output-names\n"); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + idx = bit_offset[i] / 4; + if (idx > IMX_LPCG_MAX_CLKS) { + dev_warn(&pdev->dev, "invalid bit offset of clock %d\n", + i); + ret = -EINVAL; + goto unreg; + } + + clk_hws[idx] = imx_clk_lpcg_scu(output_names[i], + parent_names[i], 0, base, + bit_offset[i], false); + if (IS_ERR(clk_hws[idx])) { + dev_warn(&pdev->dev, "failed to register clock %d\n", + idx); + ret = PTR_ERR(clk_hws[idx]); + goto unreg; + } + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, imx_lpcg_of_clk_src_get, + clk_data); + if (!ret) + return 0; + +unreg: + while (--i >= 0) { + idx = bit_offset[i] / 4; + if (clk_hws[idx]) + imx_clk_lpcg_scu_unregister(clk_hws[idx]); + } + + return ret; +} + static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -167,8 +284,14 @@ static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev) struct resource *res; struct clk_hw **clks; void __iomem *base; + int ret; int i; + /* try new binding to parse clocks from device tree first */ + ret = imx_lpcg_parse_clks_from_dt(pdev, np); + if (!ret) + return 0; + ss_lpcg = of_device_get_match_data(dev); if (!ss_lpcg) return -ENODEV; @@ -208,6 +331,7 @@ static const struct of_device_id imx8qxp_lpcg_match[] = { { .compatible = "fsl,imx8qxp-lpcg-adma", &imx8qxp_ss_adma, }, { .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, }, { .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, }, + { .compatible = "fsl,imx8qxp-lpcg", NULL }, { /* sentinel */ } }; diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c index a73a799fb777..5740064b2fdb 100644 --- a/drivers/clk/imx/clk-lpcg-scu.c +++ b/drivers/clk/imx/clk-lpcg-scu.c @@ -114,3 +114,10 @@ struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, return hw; } + +void imx_clk_lpcg_scu_unregister(struct clk_hw *hw) +{ + struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); + + kfree(clk); +} diff --git a/drivers/clk/imx/clk-scu.h b/drivers/clk/imx/clk-scu.h index 11eb3781ab6b..2db226a2401f 100644 --- a/drivers/clk/imx/clk-scu.h +++ b/drivers/clk/imx/clk-scu.h @@ -45,4 +45,5 @@ static inline struct clk_hw *imx_clk_scu2(const char *name, const char * const * struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, bool hw_gate); +void imx_clk_lpcg_scu_unregister(struct clk_hw *hw); #endif -- 2.23.0
WARNING: multiple messages have this Message-ID (diff)
From: Dong Aisheng <aisheng.dong@nxp.com> To: linux-clk@vger.kernel.org Cc: Dong Aisheng <aisheng.dong@nxp.com>, sboyd@kernel.org, mturquette@baylibre.com, linux-imx@nxp.com, kernel@pengutronix.de, fabio.estevam@nxp.com, shawnguo@kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH RESEND v5 08/11] clk: imx: imx8qxp-lpcg: add parsing clocks from device tree Date: Sun, 17 Nov 2019 20:25:16 +0800 [thread overview] Message-ID: <1573993519-14308-9-git-send-email-aisheng.dong@nxp.com> (raw) In-Reply-To: <1573993519-14308-1-git-send-email-aisheng.dong@nxp.com> One LPCG controller supports up to 8 clock outputs while each of them is fixed to 4 bits. It supports only gating function with fixed bits. So we can use the clk-indices to fetch the corresponding clock idx from device tree. With this way, we can write a generic LPCG clock drivers. This patch add that support to parse clocks from device tree. Cc: Stephen Boyd <sboyd@kernel.org> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <kernel@pengutronix.de> Cc: Michael Turquette <mturquette@baylibre.com> Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> --- Changelog: v4->v5: * add error check for devm_of_clk_add_hw_provider * add more commit message * a few minor updates v3->v4: * remove hw_autogate which is not still used by driver * use clock-indices to indicate LPCG clock bit offset v1->v3: no changes --- drivers/clk/imx/clk-imx8qxp-lpcg.c | 124 +++++++++++++++++++++++++++++ drivers/clk/imx/clk-lpcg-scu.c | 7 ++ drivers/clk/imx/clk-scu.h | 1 + 3 files changed, 132 insertions(+) diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.c b/drivers/clk/imx/clk-imx8qxp-lpcg.c index c0aff7ca6374..4d53f2b34191 100644 --- a/drivers/clk/imx/clk-imx8qxp-lpcg.c +++ b/drivers/clk/imx/clk-imx8qxp-lpcg.c @@ -9,6 +9,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -157,6 +158,122 @@ static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = { .num_max = IMX_LSIO_LPCG_CLK_END, }; +#define IMX_LPCG_MAX_CLKS 8 + +static struct clk_hw *imx_lpcg_of_clk_src_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_hw_onecell_data *hw_data = data; + unsigned int idx = clkspec->args[0] / 4; + + if (idx >= hw_data->num) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return hw_data->hws[idx]; +} + +static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev, + struct device_node *np) +{ + const char *output_names[IMX_LPCG_MAX_CLKS]; + const char *parent_names[IMX_LPCG_MAX_CLKS]; + unsigned int bit_offset[IMX_LPCG_MAX_CLKS]; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clk_hws; + struct resource *res; + void __iomem *base; + int count; + int idx; + int ret; + int i; + + if (!of_device_is_compatible(np, "fsl,imx8qxp-lpcg")) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + count = of_property_count_u32_elems(np, "clock-indices"); + if (count < 0) { + dev_err(&pdev->dev, "failed to count clocks\n"); + return -EINVAL; + } + + /* + * A trick here is that we set the num of clks to the MAX instead + * of the count from clock-indices because one LPCG supports up to + * 8 clock outputs which each of them is fixed to 4 bits. Then we can + * easily get the clock by clk-indices (bit-offset) / 4. + * And the cost is very limited few pointers. + */ + + clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws, + IMX_LPCG_MAX_CLKS), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX_LPCG_MAX_CLKS; + clk_hws = clk_data->hws; + + ret = of_property_read_u32_array(np, "clock-indices", bit_offset, + count); + if (ret < 0) { + dev_err(&pdev->dev, "failed to read clock-indices\n"); + return -EINVAL; + } + + ret = of_clk_parent_fill(np, parent_names, count); + if (ret != count) { + dev_err(&pdev->dev, "failed to get clock parent names\n"); + return count; + } + + ret = of_property_read_string_array(np, "clock-output-names", + output_names, count); + if (ret != count) { + dev_err(&pdev->dev, "failed to read clock-output-names\n"); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + idx = bit_offset[i] / 4; + if (idx > IMX_LPCG_MAX_CLKS) { + dev_warn(&pdev->dev, "invalid bit offset of clock %d\n", + i); + ret = -EINVAL; + goto unreg; + } + + clk_hws[idx] = imx_clk_lpcg_scu(output_names[i], + parent_names[i], 0, base, + bit_offset[i], false); + if (IS_ERR(clk_hws[idx])) { + dev_warn(&pdev->dev, "failed to register clock %d\n", + idx); + ret = PTR_ERR(clk_hws[idx]); + goto unreg; + } + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, imx_lpcg_of_clk_src_get, + clk_data); + if (!ret) + return 0; + +unreg: + while (--i >= 0) { + idx = bit_offset[i] / 4; + if (clk_hws[idx]) + imx_clk_lpcg_scu_unregister(clk_hws[idx]); + } + + return ret; +} + static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -167,8 +284,14 @@ static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev) struct resource *res; struct clk_hw **clks; void __iomem *base; + int ret; int i; + /* try new binding to parse clocks from device tree first */ + ret = imx_lpcg_parse_clks_from_dt(pdev, np); + if (!ret) + return 0; + ss_lpcg = of_device_get_match_data(dev); if (!ss_lpcg) return -ENODEV; @@ -208,6 +331,7 @@ static const struct of_device_id imx8qxp_lpcg_match[] = { { .compatible = "fsl,imx8qxp-lpcg-adma", &imx8qxp_ss_adma, }, { .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, }, { .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, }, + { .compatible = "fsl,imx8qxp-lpcg", NULL }, { /* sentinel */ } }; diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c index a73a799fb777..5740064b2fdb 100644 --- a/drivers/clk/imx/clk-lpcg-scu.c +++ b/drivers/clk/imx/clk-lpcg-scu.c @@ -114,3 +114,10 @@ struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, return hw; } + +void imx_clk_lpcg_scu_unregister(struct clk_hw *hw) +{ + struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); + + kfree(clk); +} diff --git a/drivers/clk/imx/clk-scu.h b/drivers/clk/imx/clk-scu.h index 11eb3781ab6b..2db226a2401f 100644 --- a/drivers/clk/imx/clk-scu.h +++ b/drivers/clk/imx/clk-scu.h @@ -45,4 +45,5 @@ static inline struct clk_hw *imx_clk_scu2(const char *name, const char * const * struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, bool hw_gate); +void imx_clk_lpcg_scu_unregister(struct clk_hw *hw); #endif -- 2.23.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2019-11-17 12:27 UTC|newest] Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-11-17 12:25 [PATCH RESEND V5 00/11] clk: imx8: add new clock binding for better pm support Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 01/11] dt-bindings: firmware: imx-scu: new binding to parse clocks from device tree Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 02/11] dt-bindings: clock: imx-lpcg: add support " Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 03/11] clk: imx: scu: add two cells binding support Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 04/11] clk: imx: scu: bypass cpu power domains Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 05/11] clk: imx: scu: allow scu clk to take device pointer Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 06/11] clk: imx: scu: add runtime pm support Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 07/11] clk: imx: scu: add suspend/resume support Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng [this message] 2019-11-17 12:25 ` [PATCH RESEND v5 08/11] clk: imx: imx8qxp-lpcg: add parsing clocks from device tree Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 09/11] clk: imx: lpcg: allow lpcg clk to take device pointer Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 10/11] clk: imx: clk-imx8qxp-lpcg: add runtime pm support Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-11-17 12:25 ` [PATCH RESEND v5 11/11] clk: imx: lpcg: add suspend/resume support Dong Aisheng 2019-11-17 12:25 ` Dong Aisheng 2019-12-11 8:05 ` [PATCH RESEND V5 00/11] clk: imx8: add new clock binding for better pm support Shawn Guo 2019-12-11 8:05 ` Shawn Guo 2020-01-02 8:25 ` Aisheng Dong 2020-01-02 8:25 ` Aisheng Dong 2020-01-21 5:00 ` Aisheng Dong 2020-01-21 5:00 ` Aisheng Dong 2020-02-07 11:06 ` Oliver Graute 2020-02-07 11:06 ` Oliver Graute
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1573993519-14308-9-git-send-email-aisheng.dong@nxp.com \ --to=aisheng.dong@nxp.com \ --cc=fabio.estevam@nxp.com \ --cc=kernel@pengutronix.de \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-clk@vger.kernel.org \ --cc=linux-imx@nxp.com \ --cc=mturquette@baylibre.com \ --cc=sboyd@kernel.org \ --cc=shawnguo@kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.