2024-05-14 10:35:21

by Shengjiu Wang

[permalink] [raw]
Subject: [PATCH v3 0/6] clk: imx: clk-audiomix: Improvement for audiomix

Some improvement for audiomix driver:
Add reset controller for EARC function
Add CLK_SET_RATE_PARENT flags for clocks
Corrent parent clock for earc_phy and audpll clocks.

changes in v3:
- separate reset driver to driver/reset/
- add binding doc for reset driver.
- modify imx8mp.dtsi accordingly

changes in v2:
- add more info in commit messages.

Shengjiu Wang (6):
dt-bindings: reset: fsl,imx8mp-audiomix-reset: add bindings
reset: imx8mp-audiomix: Add AudioMix Block Control reset driver
dt-bindings: clock: imx8mp: Add reset-controller sub-node
clk: imx: clk-audiomix: Add CLK_SET_RATE_PARENT flags for clocks
clk: imx: clk-audiomix: Corrent parent clock for earc_phy and audpll
arm64: dts: imx8mp: Add reset-controller sub node for audio_blk_ctrl

.../bindings/clock/imx8mp-audiomix.yaml | 17 ++-
.../reset/fsl,imx8mp-audiomix-reset.yaml | 37 ++++++
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 7 +-
drivers/clk/imx/clk-imx8mp-audiomix.c | 23 +++-
drivers/reset/Kconfig | 8 ++
drivers/reset/Makefile | 1 +
drivers/reset/reset-imx8mp-audiomix.c | 117 ++++++++++++++++++
7 files changed, 201 insertions(+), 9 deletions(-)
create mode 100644 Documentation/devicetree/bindings/reset/fsl,imx8mp-audiomix-reset.yaml
create mode 100644 drivers/reset/reset-imx8mp-audiomix.c

--
2.34.1



2024-05-14 10:35:56

by Shengjiu Wang

[permalink] [raw]
Subject: [PATCH v3 5/6] clk: imx: clk-audiomix: Corrent parent clock for earc_phy and audpll

According to Reference Manual of i.MX8MP
The parent clock of "earc_phy" is "sai_pll_out_div2",
The parent clock of "audpll" is "osc_24m".

Add CLK_GATE_PARENT() macro for usage of specifying parent clock.

Fixes: 6cd95f7b151c ("clk: imx: imx8mp: Add audiomix block control")
Signed-off-by: Shengjiu Wang <[email protected]>
---
drivers/clk/imx/clk-imx8mp-audiomix.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-imx8mp-audiomix.c
index 466b5b0d665c..f4a02ae7e64f 100644
--- a/drivers/clk/imx/clk-imx8mp-audiomix.c
+++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
@@ -154,6 +154,15 @@ static const struct clk_parent_data clk_imx8mp_audiomix_pll_bypass_sels[] = {
PDM_SEL, 2, 0 \
}

+#define CLK_GATE_PARENT(gname, cname, pname) \
+ { \
+ gname"_cg", \
+ IMX8MP_CLK_AUDIOMIX_##cname, \
+ { .fw_name = pname, .name = pname }, NULL, 1, \
+ CLKEN0 + 4 * !!(IMX8MP_CLK_AUDIOMIX_##cname / 32), \
+ 1, IMX8MP_CLK_AUDIOMIX_##cname % 32 \
+ }
+
struct clk_imx8mp_audiomix_sel {
const char *name;
int clkid;
@@ -171,14 +180,14 @@ static struct clk_imx8mp_audiomix_sel sels[] = {
CLK_GATE("earc", EARC_IPG),
CLK_GATE("ocrama", OCRAMA_IPG),
CLK_GATE("aud2htx", AUD2HTX_IPG),
- CLK_GATE("earc_phy", EARC_PHY),
+ CLK_GATE_PARENT("earc_phy", EARC_PHY, "sai_pll_out_div2"),
CLK_GATE("sdma2", SDMA2_ROOT),
CLK_GATE("sdma3", SDMA3_ROOT),
CLK_GATE("spba2", SPBA2_ROOT),
CLK_GATE("dsp", DSP_ROOT),
CLK_GATE("dspdbg", DSPDBG_ROOT),
CLK_GATE("edma", EDMA_ROOT),
- CLK_GATE("audpll", AUDPLL_ROOT),
+ CLK_GATE_PARENT("audpll", AUDPLL_ROOT, "osc_24m"),
CLK_GATE("mu2", MU2_ROOT),
CLK_GATE("mu3", MU3_ROOT),
CLK_PDM,
--
2.34.1


2024-05-14 10:36:28

by Shengjiu Wang

[permalink] [raw]
Subject: [PATCH v3 2/6] reset: imx8mp-audiomix: Add AudioMix Block Control reset driver

Audiomix block control can be a reset controller for
Enhanced Audio Return Channel (EARC), which is one of
modules in this audiomix subsystem.

The EARC PHY software reset and EARC controller software
reset need to be supported.

Signed-off-by: Shengjiu Wang <[email protected]>
---
drivers/reset/Kconfig | 8 ++
drivers/reset/Makefile | 1 +
drivers/reset/reset-imx8mp-audiomix.c | 117 ++++++++++++++++++++++++++
3 files changed, 126 insertions(+)
create mode 100644 drivers/reset/reset-imx8mp-audiomix.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 7112f5932609..0e7da0bb0a21 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -91,6 +91,14 @@ config RESET_IMX7
help
This enables the reset controller driver for i.MX7 SoCs.

+config RESET_IMX8MP_AUDIOMIX
+ tristate "i.MX8MP AudioMix Reset Driver"
+ depends on HAS_IOMEM
+ depends on (ARM64 && ARCH_MXC) || COMPILE_TEST
+ select MFD_SYSCON
+ help
+ This enables the reset controller driver for i.MX8MP AudioMix.
+
config RESET_INTEL_GW
bool "Intel Reset Controller Driver"
depends on X86 || COMPILE_TEST
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index fd8b49fa46fc..a6796e83900b 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
obj-$(CONFIG_RESET_GPIO) += reset-gpio.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
+obj-$(CONFIG_RESET_IMX8MP_AUDIOMIX) += reset-imx8mp-audiomix.o
obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
obj-$(CONFIG_RESET_K210) += reset-k210.o
obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
new file mode 100644
index 000000000000..8ba0d4406b36
--- /dev/null
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2024 NXP
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#define EARC 0x200
+#define EARC_RESET_MASK 0x3
+
+struct imx8mp_audiomix_rst_priv {
+ struct regmap *regmap;
+ struct reset_controller_dev rcdev;
+};
+
+static int imx8mp_audiomix_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx8mp_audiomix_rst_priv *priv = container_of(rcdev,
+ struct imx8mp_audiomix_rst_priv, rcdev);
+ unsigned int mask = BIT(id);
+
+ /* bit = 0 reset, bit = 1 unreset */
+ if (assert)
+ regmap_update_bits(priv->regmap, EARC, mask, 0);
+ else
+ regmap_update_bits(priv->regmap, EARC, mask, mask);
+
+ return 0;
+}
+
+static int imx8mp_audiomix_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ imx8mp_audiomix_reset_set(rcdev, id, true);
+
+ return imx8mp_audiomix_reset_set(rcdev, id, false);
+}
+
+static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mp_audiomix_reset_set(rcdev, id, true);
+}
+
+static int imx8mp_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mp_audiomix_reset_set(rcdev, id, false);
+}
+
+static int imx8mp_audiomix_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ unsigned long id = reset_spec->args[0];
+
+ if (!(BIT(id) & EARC_RESET_MASK))
+ return -EINVAL;
+
+ return id;
+}
+
+static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
+ .reset = imx8mp_audiomix_reset_reset,
+ .assert = imx8mp_audiomix_reset_assert,
+ .deassert = imx8mp_audiomix_reset_deassert,
+};
+
+static int imx8mp_audiomix_reset_probe(struct platform_device *pdev)
+{
+ struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
+ struct imx8mp_audiomix_rst_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = fls(EARC_RESET_MASK);
+ priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
+ priv->rcdev.of_node = pdev->dev.of_node;
+ priv->rcdev.dev = &pdev->dev;
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->rcdev.of_xlate = imx8mp_audiomix_reset_xlate;
+
+ return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
+}
+
+static const struct of_device_id imx8mp_audiomix_reset_dt_match[] = {
+ { .compatible = "fsl,imx8mp-audiomix-reset" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8mp_audiomix_reset_dt_match);
+
+static struct platform_driver imx8mp_audiomix_reset_driver = {
+ .probe = imx8mp_audiomix_reset_probe,
+ .driver = {
+ .name = "imx8mp-audiomix-reset",
+ .of_match_table = imx8mp_audiomix_reset_dt_match,
+ },
+};
+module_platform_driver(imx8mp_audiomix_reset_driver);
+
+MODULE_AUTHOR("Shengjiu Wang <[email protected]>");
+MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller reset driver");
+MODULE_LICENSE("GPL");
--
2.34.1


2024-05-14 10:42:10

by Shengjiu Wang

[permalink] [raw]
Subject: [PATCH v3 4/6] clk: imx: clk-audiomix: Add CLK_SET_RATE_PARENT flags for clocks

Add CLK_SET_RATE_PARENT flags that when the device driver sets the
child clock rate, parent clock frequency can be refined accordingly.

Signed-off-by: Shengjiu Wang <[email protected]>
---
drivers/clk/imx/clk-imx8mp-audiomix.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-imx8mp-audiomix.c
index b381d6f784c8..466b5b0d665c 100644
--- a/drivers/clk/imx/clk-imx8mp-audiomix.c
+++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
@@ -269,12 +269,12 @@ static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(sels); i++) {
if (sels[i].num_parents == 1) {
hw = devm_clk_hw_register_gate_parent_data(dev,
- sels[i].name, &sels[i].parent, 0,
+ sels[i].name, &sels[i].parent, CLK_SET_RATE_PARENT,
base + sels[i].reg, sels[i].shift, 0, NULL);
} else {
hw = devm_clk_hw_register_mux_parent_data_table(dev,
sels[i].name, sels[i].parents,
- sels[i].num_parents, 0,
+ sels[i].num_parents, CLK_SET_RATE_PARENT,
base + sels[i].reg,
sels[i].shift, sels[i].width,
0, NULL, NULL);
@@ -317,7 +317,8 @@ static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw;

hw = devm_clk_hw_register_gate(dev, "sai_pll_out", "sai_pll_bypass",
- 0, base + SAI_PLL_GNRL_CTL, 13,
+ CLK_SET_RATE_PARENT,
+ base + SAI_PLL_GNRL_CTL, 13,
0, NULL);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
@@ -326,7 +327,8 @@ static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw;

hw = devm_clk_hw_register_fixed_factor(dev, "sai_pll_out_div2",
- "sai_pll_out", 0, 1, 2);
+ "sai_pll_out",
+ CLK_SET_RATE_PARENT, 1, 2);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
goto err_clk_register;
--
2.34.1


2024-05-14 10:42:30

by Shengjiu Wang

[permalink] [raw]
Subject: [PATCH v3 6/6] arm64: dts: imx8mp: Add reset-controller sub node for audio_blk_ctrl

The Audio Block Control contains clock distribution and gating
controls, as well as reset handling to several of the AUDIOMIX
peripherals. Especially the reset controls for Enhanced Audio
Return Channel (EARC) PHY and Controller

Add reset-controller sub-node for audio_blk_ctrl.

Signed-off-by: Shengjiu Wang <[email protected]>
---
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
index 459c4a54d30e..f94702ad4210 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
@@ -1565,7 +1565,7 @@ sdma2: dma-controller@30e10000 {
};

audio_blk_ctrl: clock-controller@30e20000 {
- compatible = "fsl,imx8mp-audio-blk-ctrl";
+ compatible = "fsl,imx8mp-audio-blk-ctrl", "syscon", "simple-mfd";
reg = <0x30e20000 0x10000>;
#clock-cells = <1>;
clocks = <&clk IMX8MP_CLK_AUDIO_ROOT>,
@@ -1582,6 +1582,11 @@ audio_blk_ctrl: clock-controller@30e20000 {
assigned-clocks = <&clk IMX8MP_AUDIO_PLL1>,
<&clk IMX8MP_AUDIO_PLL2>;
assigned-clock-rates = <393216000>, <361267200>;
+
+ audio_blk_ctrl_rst: reset-controller {
+ compatible = "fsl,imx8mp-audiomix-reset";
+ #reset-cells = <1>;
+ };
};
};

--
2.34.1


2024-05-14 14:23:54

by Frank Li

[permalink] [raw]
Subject: Re: [PATCH v3 2/6] reset: imx8mp-audiomix: Add AudioMix Block Control reset driver

On Tue, May 14, 2024 at 05:33:26PM +0800, Shengjiu Wang wrote:
> Audiomix block control can be a reset controller for
> Enhanced Audio Return Channel (EARC), which is one of
> modules in this audiomix subsystem.
>
> The EARC PHY software reset and EARC controller software
> reset need to be supported.
>
> Signed-off-by: Shengjiu Wang <[email protected]>
> ---
> drivers/reset/Kconfig | 8 ++
> drivers/reset/Makefile | 1 +
> drivers/reset/reset-imx8mp-audiomix.c | 117 ++++++++++++++++++++++++++

It is just some bit change. Can you reuse reset-ti-syscon.c ?

Frank

> 3 files changed, 126 insertions(+)
> create mode 100644 drivers/reset/reset-imx8mp-audiomix.c
>
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index 7112f5932609..0e7da0bb0a21 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -91,6 +91,14 @@ config RESET_IMX7
> help
> This enables the reset controller driver for i.MX7 SoCs.
>
> +config RESET_IMX8MP_AUDIOMIX
> + tristate "i.MX8MP AudioMix Reset Driver"
> + depends on HAS_IOMEM
> + depends on (ARM64 && ARCH_MXC) || COMPILE_TEST
> + select MFD_SYSCON
> + help
> + This enables the reset controller driver for i.MX8MP AudioMix.
> +
> config RESET_INTEL_GW
> bool "Intel Reset Controller Driver"
> depends on X86 || COMPILE_TEST
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index fd8b49fa46fc..a6796e83900b 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
> obj-$(CONFIG_RESET_GPIO) += reset-gpio.o
> obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
> obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
> +obj-$(CONFIG_RESET_IMX8MP_AUDIOMIX) += reset-imx8mp-audiomix.o
> obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
> obj-$(CONFIG_RESET_K210) += reset-k210.o
> obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
> diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
> new file mode 100644
> index 000000000000..8ba0d4406b36
> --- /dev/null
> +++ b/drivers/reset/reset-imx8mp-audiomix.c
> @@ -0,0 +1,117 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright 2024 NXP
> + */
> +
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/reset-controller.h>
> +
> +#define EARC 0x200
> +#define EARC_RESET_MASK 0x3
> +
> +struct imx8mp_audiomix_rst_priv {
> + struct regmap *regmap;
> + struct reset_controller_dev rcdev;
> +};
> +
> +static int imx8mp_audiomix_reset_set(struct reset_controller_dev *rcdev,
> + unsigned long id, bool assert)
> +{
> + struct imx8mp_audiomix_rst_priv *priv = container_of(rcdev,
> + struct imx8mp_audiomix_rst_priv, rcdev);
> + unsigned int mask = BIT(id);
> +
> + /* bit = 0 reset, bit = 1 unreset */
> + if (assert)
> + regmap_update_bits(priv->regmap, EARC, mask, 0);
> + else
> + regmap_update_bits(priv->regmap, EARC, mask, mask);
> +
> + return 0;
> +}
> +
> +static int imx8mp_audiomix_reset_reset(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + imx8mp_audiomix_reset_set(rcdev, id, true);
> +
> + return imx8mp_audiomix_reset_set(rcdev, id, false);
> +}
> +
> +static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + return imx8mp_audiomix_reset_set(rcdev, id, true);
> +}
> +
> +static int imx8mp_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + return imx8mp_audiomix_reset_set(rcdev, id, false);
> +}
> +
> +static int imx8mp_audiomix_reset_xlate(struct reset_controller_dev *rcdev,
> + const struct of_phandle_args *reset_spec)
> +{
> + unsigned long id = reset_spec->args[0];
> +
> + if (!(BIT(id) & EARC_RESET_MASK))
> + return -EINVAL;
> +
> + return id;
> +}
> +
> +static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
> + .reset = imx8mp_audiomix_reset_reset,
> + .assert = imx8mp_audiomix_reset_assert,
> + .deassert = imx8mp_audiomix_reset_deassert,
> +};
> +
> +static int imx8mp_audiomix_reset_probe(struct platform_device *pdev)
> +{
> + struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
> + struct imx8mp_audiomix_rst_priv *priv;
> +
> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->regmap = syscon_node_to_regmap(parent_np);
> + of_node_put(parent_np);
> + if (IS_ERR(priv->regmap))
> + return PTR_ERR(priv->regmap);
> +
> + priv->rcdev.owner = THIS_MODULE;
> + priv->rcdev.nr_resets = fls(EARC_RESET_MASK);
> + priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
> + priv->rcdev.of_node = pdev->dev.of_node;
> + priv->rcdev.dev = &pdev->dev;
> + priv->rcdev.of_reset_n_cells = 1;
> + priv->rcdev.of_xlate = imx8mp_audiomix_reset_xlate;
> +
> + return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
> +}
> +
> +static const struct of_device_id imx8mp_audiomix_reset_dt_match[] = {
> + { .compatible = "fsl,imx8mp-audiomix-reset" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx8mp_audiomix_reset_dt_match);
> +
> +static struct platform_driver imx8mp_audiomix_reset_driver = {
> + .probe = imx8mp_audiomix_reset_probe,
> + .driver = {
> + .name = "imx8mp-audiomix-reset",
> + .of_match_table = imx8mp_audiomix_reset_dt_match,
> + },
> +};
> +module_platform_driver(imx8mp_audiomix_reset_driver);
> +
> +MODULE_AUTHOR("Shengjiu Wang <[email protected]>");
> +MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller reset driver");
> +MODULE_LICENSE("GPL");
> --
> 2.34.1
>