The i.MX8MP has some new IPs called mixes. They are formed usually by some
GPRs that can be split into different functionalities. The first example
here is the audiomix which has dedicated registers that can be registered
as a clock controller and some other registers that can be registered as
a reset controller, plus some dedicated ones that will be registered as
syscon and used by each dedicated audio IP.
More mixes to be following the same structure are to come, like hdmimix,
dispmix and mediamix. They will all be populated and registered by the MFD
imx-mix generic driver.
Abel Vesa (11):
mfd: Add i.MX generic mix support
arm64: dts: imx8mp: Add AIPS 4 and 5
arm64: dts: imx8mp: Add audiomix node
clk: imx: Add gate shared for i.MX8MP audiomix
clk: imx: pll14xx: Add the device as argument when registering
clk: imx: Add helpers for passing the device as argument
dt-bindings: clocks: imx8mp: Add ids for audiomix clocks
clk: imx: Add audiomix clock controller support
arm64: dts: imx8mp: Add audiomix clock controller node
reset: imx: Add audiomix reset controller support
arm64: dts: imx8mp: Add audiomix reset controller node
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 37 ++++
drivers/clk/imx/Makefile | 2 +-
drivers/clk/imx/clk-audiomix.c | 237 +++++++++++++++++++++++++
drivers/clk/imx/clk-gate-shared.c | 111 ++++++++++++
drivers/clk/imx/clk-pll14xx.c | 6 +-
drivers/clk/imx/clk.h | 46 ++++-
drivers/mfd/Kconfig | 11 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/imx-mix.c | 48 +++++
drivers/reset/Kconfig | 7 +
drivers/reset/Makefile | 1 +
drivers/reset/reset-imx-audiomix.c | 122 +++++++++++++
include/dt-bindings/clock/imx8mp-clock.h | 62 +++++++
include/dt-bindings/reset/imx-audiomix-reset.h | 15 ++
14 files changed, 699 insertions(+), 7 deletions(-)
create mode 100644 drivers/clk/imx/clk-audiomix.c
create mode 100644 drivers/clk/imx/clk-gate-shared.c
create mode 100644 drivers/mfd/imx-mix.c
create mode 100644 drivers/reset/reset-imx-audiomix.c
create mode 100644 include/dt-bindings/reset/imx-audiomix-reset.h
--
2.7.4
The newer i.MX platform use some gates that have a shared control bit
between them.
Signed-off-by: Abel Vesa <[email protected]>
---
drivers/clk/imx/Makefile | 2 +-
drivers/clk/imx/clk-gate-shared.c | 111 ++++++++++++++++++++++++++++++++++++++
drivers/clk/imx/clk.h | 4 ++
3 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 drivers/clk/imx/clk-gate-shared.c
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 928f874c..799a8ef 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -27,7 +27,7 @@ obj-$(CONFIG_MXC_CLK_SCU) += \
obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o
obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o
-obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o
+obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-gate-shared.o
obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o
obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o
diff --git a/drivers/clk/imx/clk-gate-shared.c b/drivers/clk/imx/clk-gate-shared.c
new file mode 100644
index 00000000..961a0e3
--- /dev/null
+++ b/drivers/clk/imx/clk-gate-shared.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+/**
+ * struct clk_gate_shared - i.MX specific gate clock having the gate flag
+ * shared with other gate clocks
+ */
+struct clk_gate_shared {
+ struct clk_gate gate;
+ spinlock_t *lock;
+ unsigned int *share_count;
+};
+
+static int clk_gate_shared_enable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct clk_gate_shared *shgate = container_of(gate,
+ struct clk_gate_shared, gate);
+ unsigned long flags = 0;
+ int ret = 0;
+
+ spin_lock_irqsave(shgate->lock, flags);
+
+ if (shgate->share_count && (*shgate->share_count)++ > 0)
+ goto out;
+
+ ret = clk_gate_ops.enable(hw);
+out:
+ spin_unlock_irqrestore(shgate->lock, flags);
+
+ return ret;
+}
+
+static void clk_gate_shared_disable(struct clk_hw *hw)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ struct clk_gate_shared *shgate = container_of(gate,
+ struct clk_gate_shared, gate);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(shgate->lock, flags);
+
+ if (shgate->share_count) {
+ if (WARN_ON(*shgate->share_count == 0))
+ goto out;
+ else if (--(*shgate->share_count) > 0)
+ goto out;
+ }
+
+ clk_gate_ops.disable(hw);
+out:
+ spin_unlock_irqrestore(shgate->lock, flags);
+}
+
+static int clk_gate_shared_is_enabled(struct clk_hw *hw)
+{
+ return clk_gate_ops.is_enabled(hw);
+}
+
+static const struct clk_ops clk_gate_shared_ops = {
+ .enable = clk_gate_shared_enable,
+ .disable = clk_gate_shared_disable,
+ .is_enabled = clk_gate_shared_is_enabled,
+};
+
+struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, unsigned int *share_count)
+{
+ struct clk_gate_shared *shgate;
+ struct clk_gate *gate;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ shgate = kzalloc(sizeof(*shgate), GFP_KERNEL);
+ if (!shgate)
+ return ERR_PTR(-ENOMEM);
+ gate = &shgate->gate;
+
+ init.name = name;
+ init.ops = &clk_gate_shared_ops;
+ init.flags = CLK_OPS_PARENT_ENABLE;
+ init.parent_names = parent ? &parent : NULL;
+ init.num_parents = parent ? 1 : 0;
+
+ gate->reg = reg;
+ gate->bit_idx = shift;
+ gate->lock = NULL;
+ gate->hw.init = &init;
+ shgate->lock = &imx_ccm_lock;
+ shgate->share_count = share_count;
+
+ hw = &gate->hw;
+
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
+ kfree(shgate);
+ return ERR_PTR(ret);
+ }
+
+ return hw;
+}
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index f074dd8..51d6c26 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -151,6 +151,10 @@ struct clk_hw *imx_clk_hw_sscg_pll(const char *name,
void __iomem *base,
unsigned long flags);
+struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char *name,
+ const char *parent, void __iomem *reg,
+ u8 shift, unsigned int *share_count);
+
enum imx_pllv3_type {
IMX_PLLV3_GENERIC,
IMX_PLLV3_SYS,
--
2.7.4
Add the audiomix clock controller as part of the audiomix MFD.
Signed-off-by: Abel Vesa <[email protected]>
---
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
index 77d2901e..f27acf9 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
@@ -621,6 +621,17 @@
audiomix: audiomix@30e20000 {
compatible = "fsl,imx8mp-audiomix";
reg = <0x30e20000 0x10000>;
+
+ audiomix_clk: clock-controller {
+ compatible = "fsl,imx8mp-audiomix-clk";
+ #clock-cells = <1>;
+ clocks = <&clk IMX8MP_CLK_AUDIO_ROOT>,
+ <&clk IMX8MP_CLK_AUDIO_AHB>,
+ <&clk IMX8MP_CLK_AUDIO_AXI_DIV>;
+ clock-names = "audio_root",
+ "audio_ahb",
+ "audio_axi_div";
+ };
};
};
--
2.7.4
Add the audiomix reset controller as part of the audiomix MFD.
Signed-off-by: Abel Vesa <[email protected]>
---
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
index f27acf9..ca7fc73 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
@@ -632,6 +632,11 @@
"audio_ahb",
"audio_axi_div";
};
+
+ audiomix_reset: reset-controller {
+ compatible = "fsl,imx8mp-audiomix-reset";
+ #reset-cells = <1>;
+ };
};
};
--
2.7.4
The imx-mix MFD driver registers some devices, one of which, in case of
audiomix, maps correctly to a reset controller type. This driver registers
a reset controller for that. For now, only the EARC specific resets are added.
Signed-off-by: Abel Vesa <[email protected]>
Reviewed-by: Leonard Crestez <[email protected]>
---
drivers/reset/Kconfig | 7 ++
drivers/reset/Makefile | 1 +
drivers/reset/reset-imx-audiomix.c | 122 +++++++++++++++++++++++++
include/dt-bindings/reset/imx-audiomix-reset.h | 15 +++
4 files changed, 145 insertions(+)
create mode 100644 drivers/reset/reset-imx-audiomix.c
create mode 100644 include/dt-bindings/reset/imx-audiomix-reset.h
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index d9efbfd..2f8d9b3 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -81,6 +81,13 @@ config RESET_INTEL_GW
Say Y to control the reset signals provided by reset controller.
Otherwise, say N.
+config RESET_IMX_AUDIOMIX
+ bool "i.MX Audiomix Reset Driver" if COMPILE_TEST
+ depends on HAS_IOMEM
+ default ARCH_MXC
+ help
+ This enables the audiomix reset controller driver for i.MX SoCs.
+
config RESET_LANTIQ
bool "Lantiq XWAY Reset Driver" if COMPILE_TEST
default SOC_TYPE_XWAY
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 249ed35..cf23d38 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
+obj-$(CONFIG_RESET_IMX_AUDIOMIX) += reset-imx-audiomix.o
obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
diff --git a/drivers/reset/reset-imx-audiomix.c b/drivers/reset/reset-imx-audiomix.c
new file mode 100644
index 00000000..d1c62ef
--- /dev/null
+++ b/drivers/reset/reset-imx-audiomix.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <dt-bindings/reset/imx-audiomix-reset.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset-controller.h>
+
+#define IMX_AUDIOMIX_EARC_CTRL_REG 0x200
+
+#define IMX_AUDIOMIX_EARC_RESET_BIT 0x0
+#define IMX_AUDIOMIX_EARC_PHY_RESET_BIT 0x1
+
+struct imx_audiomix_reset_data {
+ void __iomem *base;
+ struct reset_controller_dev rcdev;
+ spinlock_t lock;
+};
+
+static int imx_audiomix_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx_audiomix_reset_data *drvdata = container_of(rcdev,
+ struct imx_audiomix_reset_data, rcdev);
+ void __iomem *reg_addr = drvdata->base;
+ unsigned long flags;
+ unsigned int offset;
+ u32 reg;
+
+ switch (id) {
+ case IMX_AUDIOMIX_EARC_PHY_RESET:
+ reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
+ offset = IMX_AUDIOMIX_EARC_PHY_RESET_BIT;
+ break;
+ case IMX_AUDIOMIX_EARC_RESET:
+ reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
+ offset = IMX_AUDIOMIX_EARC_RESET_BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (assert) {
+ pm_runtime_get_sync(rcdev->dev);
+ spin_lock_irqsave(&drvdata->lock, flags);
+ reg = readl(reg_addr);
+ writel(reg & ~BIT(offset), reg_addr);
+ spin_unlock_irqrestore(&drvdata->lock, flags);
+ } else {
+ spin_lock_irqsave(&drvdata->lock, flags);
+ reg = readl(reg_addr);
+ writel(reg | BIT(offset), reg_addr);
+ spin_unlock_irqrestore(&drvdata->lock, flags);
+ pm_runtime_put(rcdev->dev);
+ }
+
+ return 0;
+}
+
+static int imx_audiomix_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx_audiomix_reset_set(rcdev, id, true);
+}
+
+static int imx_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx_audiomix_reset_set(rcdev, id, false);
+}
+
+static const struct reset_control_ops imx_audiomix_reset_ops = {
+ .assert = imx_audiomix_reset_assert,
+ .deassert = imx_audiomix_reset_deassert,
+};
+
+static int imx_audiomix_reset_probe(struct platform_device *pdev)
+{
+ struct imx_audiomix_reset_data *drvdata;
+ struct device *dev = &pdev->dev;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (drvdata == NULL)
+ return -ENOMEM;
+
+ drvdata->base = dev_get_drvdata(dev->parent);
+
+ platform_set_drvdata(pdev, drvdata);
+
+ pm_runtime_enable(dev);
+
+ spin_lock_init(&drvdata->lock);
+
+ drvdata->rcdev.owner = THIS_MODULE;
+ drvdata->rcdev.nr_resets = IMX_AUDIOMIX_RESET_NUM;
+ drvdata->rcdev.ops = &imx_audiomix_reset_ops;
+ drvdata->rcdev.of_node = dev->of_node;
+ drvdata->rcdev.dev = dev;
+
+ return devm_reset_controller_register(dev, &drvdata->rcdev);
+}
+
+static const struct of_device_id imx_audiomix_reset_dt_ids[] = {
+ { .compatible = "fsl,imx8mp-audiomix-reset", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver imx_audiomix_reset_driver = {
+ .probe = imx_audiomix_reset_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = imx_audiomix_reset_dt_ids,
+ },
+};
+module_platform_driver(imx_audiomix_reset_driver);
diff --git a/include/dt-bindings/reset/imx-audiomix-reset.h b/include/dt-bindings/reset/imx-audiomix-reset.h
new file mode 100644
index 00000000..2e26878
--- /dev/null
+++ b/include/dt-bindings/reset/imx-audiomix-reset.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#ifndef DT_BINDING_RESET_IMX_AUDIOMIX_H
+#define DT_BINDING_RESET_IMX_AUDIOMIX_H
+
+#define IMX_AUDIOMIX_EARC_RESET 0x0
+#define IMX_AUDIOMIX_EARC_PHY_RESET 0x1
+
+#define IMX_AUDIOMIX_RESET_NUM 2
+
+#endif
+
--
2.7.4
The imx-mix MFD driver registers some devices, one of which, in case of
audiomix, maps correctly to a clock controller type. This driver registers
a clock controller for that.
Signed-off-by: Abel Vesa <[email protected]>
---
drivers/clk/imx/Makefile | 2 +-
drivers/clk/imx/clk-audiomix.c | 237 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 238 insertions(+), 1 deletion(-)
create mode 100644 drivers/clk/imx/clk-audiomix.c
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 799a8ef..5a8d2cb 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -27,7 +27,7 @@ obj-$(CONFIG_MXC_CLK_SCU) += \
obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o
obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o
-obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-gate-shared.o
+obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-gate-shared.o clk-audiomix.o
obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o
obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o
diff --git a/drivers/clk/imx/clk-audiomix.c b/drivers/clk/imx/clk-audiomix.c
new file mode 100644
index 00000000..8b84943
--- /dev/null
+++ b/drivers/clk/imx/clk-audiomix.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <dt-bindings/clock/imx8mp-clock.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "clk.h"
+
+static int shared_count_pdm;
+static struct clk_hw **hws;
+static struct clk_hw_onecell_data *clk_hw_data;
+static uint32_t audiomix_clk_saved_regs[14];
+static struct clk *clk_audio_root;
+static struct clk *clk_audio_ahb;
+static struct clk *clk_audio_axi_div;
+
+static const struct imx_pll14xx_rate_table imx_audiomix_sai_pll_tbl[] = {
+ PLL_1443X_RATE(650000000U, 325, 3, 2, 0),
+};
+
+static const struct imx_pll14xx_clk imx_audiomix_sai_pll = {
+ .type = PLL_1443X,
+ .rate_table = imx_audiomix_sai_pll_tbl,
+};
+
+static const char *imx_sai_mclk2_sels[] = {"sai1", "sai2", "sai3", "dummy",
+ "sai5", "sai6", "sai7", "dummy",
+ "dummy", "dummy", "dummy",
+ "dummy", "dummy", "dummy", "dummy"};
+static const char *imx_sai1_mclk1_sels[] = {"sai1", "dummy", };
+static const char *imx_sai2_mclk1_sels[] = {"sai2", "dummy", };
+static const char *imx_sai3_mclk1_sels[] = {"sai3", "dummy", };
+static const char *imx_sai5_mclk1_sels[] = {"sai5", "dummy", };
+static const char *imx_sai6_mclk1_sels[] = {"sai6", "dummy", };
+static const char *imx_sai7_mclk1_sels[] = {"sai7", "dummy", };
+static const char *imx_pdm_sels[] = {"pdm", "sai_pll_div2", "dummy", "dummy" };
+static const char *imx_sai_pll_ref_sels[] = {"osc_24m", "dummy", "dummy", "dummy", };
+static const char *imx_sai_pll_bypass_sels[] = {"sai_pll", "sai_pll_ref_sel", };
+
+static int imx_audiomix_clk_suspend(struct device *dev)
+{
+ void __iomem *base;
+
+ base = dev_get_drvdata(dev->parent);
+
+ audiomix_clk_saved_regs[0] = readl(base);
+ audiomix_clk_saved_regs[1] = readl(base + 0x4);
+
+ audiomix_clk_saved_regs[2] = readl(base + 0x300);
+ audiomix_clk_saved_regs[3] = readl(base + 0x304);
+ audiomix_clk_saved_regs[4] = readl(base + 0x308);
+ audiomix_clk_saved_regs[5] = readl(base + 0x30C);
+ audiomix_clk_saved_regs[6] = readl(base + 0x310);
+ audiomix_clk_saved_regs[7] = readl(base + 0x314);
+ audiomix_clk_saved_regs[8] = readl(base + 0x318);
+
+ audiomix_clk_saved_regs[9] = readl(base + 0x400);
+ audiomix_clk_saved_regs[10] = readl(base + 0x404);
+ audiomix_clk_saved_regs[11] = readl(base + 0x408);
+ audiomix_clk_saved_regs[12] = readl(base + 0x40C);
+ audiomix_clk_saved_regs[13] = readl(base + 0x410);
+
+ clk_disable_unprepare(clk_audio_ahb);
+ clk_disable_unprepare(clk_audio_root);
+ clk_disable_unprepare(clk_audio_axi_div);
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static int imx_audiomix_clk_resume(struct device *dev)
+{
+ void __iomem *base;
+
+ base = dev_get_drvdata(dev->parent);
+
+ pm_runtime_get(dev);
+ clk_prepare_enable(clk_audio_ahb);
+ clk_prepare_enable(clk_audio_root);
+ clk_prepare_enable(clk_audio_axi_div);
+
+ writel(audiomix_clk_saved_regs[0], base);
+ writel(audiomix_clk_saved_regs[1], base + 0x4);
+
+ writel(audiomix_clk_saved_regs[2], base + 0x300);
+ writel(audiomix_clk_saved_regs[3], base + 0x304);
+ writel(audiomix_clk_saved_regs[4], base + 0x308);
+ writel(audiomix_clk_saved_regs[5], base + 0x30C);
+ writel(audiomix_clk_saved_regs[6], base + 0x310);
+ writel(audiomix_clk_saved_regs[7], base + 0x314);
+ writel(audiomix_clk_saved_regs[8], base + 0x318);
+
+ writel(audiomix_clk_saved_regs[9], base + 0x400);
+ writel(audiomix_clk_saved_regs[10], base + 0x404);
+ writel(audiomix_clk_saved_regs[11], base + 0x408);
+ writel(audiomix_clk_saved_regs[12], base + 0x40C);
+ writel(audiomix_clk_saved_regs[13], base + 0x410);
+
+ return 0;
+}
+
+static int imx_audiomix_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ void __iomem *base;
+
+ clk_audio_root = of_clk_get_by_name(np, "audio_root");
+ if (IS_ERR(clk_audio_root))
+ return PTR_ERR(clk_audio_root);
+
+ clk_audio_ahb = of_clk_get_by_name(np, "audio_ahb");
+ if (IS_ERR(clk_audio_ahb))
+ return PTR_ERR(clk_audio_ahb);
+
+ clk_audio_axi_div = of_clk_get_by_name(np, "audio_axi_div");
+ if (IS_ERR(clk_audio_axi_div))
+ return PTR_ERR(clk_audio_axi_div);
+
+ base = dev_get_drvdata(dev->parent);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, IMX8MP_CLK_AUDIOMIX_END), GFP_KERNEL);
+ if (WARN_ON(!clk_hw_data))
+ return -ENOMEM;
+
+ clk_hw_data->num = IMX8MP_CLK_AUDIOMIX_END;
+ hws = clk_hw_data->hws;
+
+ pm_runtime_enable(dev);
+
+ hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = imx_dev_clk_hw_mux(dev, "sai_pll_ref_sel", base + 0x400, 0, 2, imx_sai_pll_ref_sels, ARRAY_SIZE(imx_sai_pll_ref_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel", base + 0x400, &imx_audiomix_sai_pll);
+
+ hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = imx_dev_clk_hw_mux_flags(dev, "sai_pll_bypass", base + 0x400, 4, 1, imx_sai_pll_bypass_sels, ARRAY_SIZE(imx_sai_pll_bypass_sels), CLK_SET_RATE_PARENT);
+
+ hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = imx_dev_clk_hw_gate(dev, "sai_pll_out", "sai_pll_bypass", base + 0x400, 13);
+
+ hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1_SEL] = imx_dev_clk_hw_mux_flags(dev, "sai1_mclk1_sel", base + 0x300, 0, 1, imx_sai1_mclk1_sels, ARRAY_SIZE(imx_sai1_mclk1_sels), CLK_SET_RATE_PARENT);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai1_mclk2_sel", base + 0x300, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK1_SEL] = imx_dev_clk_hw_mux_flags(dev, "sai2_mclk1_sel", base + 0x304, 0, 1, imx_sai2_mclk1_sels, ARRAY_SIZE(imx_sai2_mclk1_sels), CLK_SET_RATE_PARENT);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai2_mclk2_sel", base + 0x304, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK1_SEL] = imx_dev_clk_hw_mux_flags(dev, "sai3_mclk1_sel", base + 0x308, 0, 1, imx_sai3_mclk1_sels, ARRAY_SIZE(imx_sai3_mclk1_sels), CLK_SET_RATE_PARENT);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai3_mclk2_sel", base + 0x308, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK1_SEL] = imx_dev_clk_hw_mux(dev, "sai5_mclk1_sel", base + 0x30C, 0, 1, imx_sai5_mclk1_sels, ARRAY_SIZE(imx_sai5_mclk1_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai5_mclk2_sel", base + 0x30C, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK1_SEL] = imx_dev_clk_hw_mux(dev, "sai6_mclk1_sel", base + 0x310, 0, 1, imx_sai6_mclk1_sels, ARRAY_SIZE(imx_sai6_mclk1_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai6_mclk2_sel", base + 0x310, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK1_SEL] = imx_dev_clk_hw_mux(dev, "sai7_mclk1_sel", base + 0x314, 0, 1, imx_sai7_mclk1_sels, ARRAY_SIZE(imx_sai7_mclk1_sels));
+ hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai7_mclk2_sel", base + 0x314, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
+
+ hws[IMX8MP_CLK_AUDIOMIX_SAI1_IPG] = imx_dev_clk_hw_gate(dev, "sai1_ipg_clk", "ipg_audio_root", base, 0);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1] = imx_dev_clk_hw_gate(dev, "sai1_mclk1_clk", "sai1_mclk1_sel", base, 1);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK2] = imx_dev_clk_hw_gate(dev, "sai1_mclk2_clk", "sai1_mclk2_sel", base, 2);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK3] = imx_dev_clk_hw_gate(dev, "sai1_mclk3_clk", "sai_pll_out", base, 3);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI2_IPG] = imx_dev_clk_hw_gate(dev, "sai2_ipg_clk", "ipg_audio_root", base, 4);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK1] = imx_dev_clk_hw_gate(dev, "sai2_mclk1_clk", "sai2_mclk1_sel", base, 5);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK2] = imx_dev_clk_hw_gate(dev, "sai2_mclk2_clk", "sai2_mclk2_sel", base, 6);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK3] = imx_dev_clk_hw_gate(dev, "sai2_mclk3_clk", "sai_pll_out", base, 7);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI3_IPG] = imx_dev_clk_hw_gate(dev, "sai3_ipg_clk", "ipg_audio_root", base, 8);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK1] = imx_dev_clk_hw_gate(dev, "sai3_mclk1_clk", "sai3_mclk1_sel", base, 9);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK2] = imx_dev_clk_hw_gate(dev, "sai3_mclk2_clk", "sai3_mclk2_sel", base, 10);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK3] = imx_dev_clk_hw_gate(dev, "sai3_mclk3_clk", "sai_pll_out", base, 11);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI5_IPG] = imx_dev_clk_hw_gate(dev, "sai5_ipg_clk", "ipg_audio_root", base, 12);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK1] = imx_dev_clk_hw_gate(dev, "sai5_mclk1_clk", "sai5_mclk1_sel", base, 13);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK2] = imx_dev_clk_hw_gate(dev, "sai5_mclk2_clk", "sai5_mclk2_sel", base, 14);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK3] = imx_dev_clk_hw_gate(dev, "sai5_mclk3_clk", "sai_pll_out", base, 15);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI6_IPG] = imx_dev_clk_hw_gate(dev, "sai6_ipg_clk", "ipg_audio_root", base, 16);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK1] = imx_dev_clk_hw_gate(dev, "sai6_mclk1_clk", "sai6_mclk1_sel", base, 17);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK2] = imx_dev_clk_hw_gate(dev, "sai6_mclk2_clk", "sai6_mclk2_sel", base, 18);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK3] = imx_dev_clk_hw_gate(dev, "sai6_mclk3_clk", "sai_pll_out", base, 19);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI7_IPG] = imx_dev_clk_hw_gate(dev, "sai7_ipg_clk", "ipg_audio_root", base, 20);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK1] = imx_dev_clk_hw_gate(dev, "sai7_mclk1_clk", "sai7_mclk1_sel", base, 21);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK2] = imx_dev_clk_hw_gate(dev, "sai7_mclk2_clk", "sai7_mclk2_sel", base, 22);
+ hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK3] = imx_dev_clk_hw_gate(dev, "sai7_mclk3_clk", "sai_pll_out", base, 23);
+ hws[IMX8MP_CLK_AUDIOMIX_ASRC_IPG] = imx_dev_clk_hw_gate(dev, "asrc_ipg_clk", "ipg_audio_root", base, 24);
+ hws[IMX8MP_CLK_AUDIOMIX_PDM_IPG] = imx_dev_clk_hw_gate_shared(dev, "pdm_ipg_clk", "ipg_audio_root", base, 25, &shared_count_pdm);
+ hws[IMX8MP_CLK_AUDIOMIX_PDM_ROOT] = imx_dev_clk_hw_gate_shared(dev, "pdm_root_clk", "pdm", base, 25, &shared_count_pdm);
+
+ hws[IMX8MP_CLK_AUDIOMIX_SDMA2_ROOT] = imx_dev_clk_hw_gate(dev, "sdma2_root_clk", "ipg_audio_root", base, 26);
+ hws[IMX8MP_CLK_AUDIOMIX_SDMA3_ROOT] = imx_dev_clk_hw_gate(dev, "sdma3_root_clk", "ipg_audio_root", base, 27);
+ hws[IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT] = imx_dev_clk_hw_gate(dev, "spba2_root_clk", "ipg_audio_root", base, 28);
+ hws[IMX8MP_CLK_AUDIOMIX_DSP_ROOT] = imx_dev_clk_hw_gate(dev, "dsp_root_clk", "ipg_audio_root", base, 29);
+ hws[IMX8MP_CLK_AUDIOMIX_DSPDBG_ROOT] = imx_dev_clk_hw_gate(dev, "dsp_dbg_clk", "ipg_audio_root", base, 30);
+ hws[IMX8MP_CLK_AUDIOMIX_EARC_IPG] = imx_dev_clk_hw_gate(dev, "earc_ipg_clk", "ipg_audio_root", base, 31);
+
+ hws[IMX8MP_CLK_AUDIOMIX_OCRAMA_IPG] = imx_dev_clk_hw_gate(dev, "ocram_a_ipg_clk", "ipg_audio_root", base + 4, 0);
+ hws[IMX8MP_CLK_AUDIOMIX_AUD2HTX_IPG] = imx_dev_clk_hw_gate(dev, "aud2htx_ipg_clk", "ipg_audio_root", base + 4, 1);
+ hws[IMX8MP_CLK_AUDIOMIX_EDMA_ROOT] = imx_dev_clk_hw_gate(dev, "edma_root_clk", "ipg_audio_root", base + 4, 2);
+ hws[IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT] = imx_dev_clk_hw_gate(dev, "aud_pll_clk", "ipg_audio_root", base + 4, 3);
+ hws[IMX8MP_CLK_AUDIOMIX_MU2_ROOT] = imx_dev_clk_hw_gate(dev, "mu2_root_clk", "ipg_audio_root", base + 4, 4);
+ hws[IMX8MP_CLK_AUDIOMIX_MU3_ROOT] = imx_dev_clk_hw_gate(dev, "mu3_root_clk", "ipg_audio_root", base + 4, 5);
+ hws[IMX8MP_CLK_AUDIOMIX_EARC_PHY] = imx_dev_clk_hw_gate(dev, "earc_phy_clk", "ipg_audio_root", base + 4, 6);
+
+ hws[IMX8MP_CLK_AUDIOMIX_PDM_SEL] = imx_dev_clk_hw_mux(dev, "pdm_sel", base + 0x318, 1, 4, imx_pdm_sels, ARRAY_SIZE(imx_pdm_sels));
+
+ /* unbypass the pll */
+ clk_hw_set_parent(hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS], hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL]);
+
+ imx_check_clk_hws(hws, IMX8MP_CLK_AUDIOMIX_END);
+
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
+
+ return 0;
+}
+
+UNIVERSAL_DEV_PM_OPS(imx_audiomix_clk_pm_ops, imx_audiomix_clk_suspend,
+ imx_audiomix_clk_resume, imx_audiomix_clk_resume);
+
+static const struct of_device_id imx_audiomix_clk_of_match[] = {
+ { .compatible = "fsl,imx8mp-audiomix-clk" },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx_audiomix_clk_of_match);
+
+
+static struct platform_driver imx_audiomix_clk_driver = {
+ .probe = imx_audiomix_clk_probe,
+ .driver = {
+ .name = "imx-audiomix-clk",
+ .of_match_table = of_match_ptr(imx_audiomix_clk_of_match),
+ .pm = &imx_audiomix_clk_pm_ops,
+ },
+};
+module_platform_driver(imx_audiomix_clk_driver);
--
2.7.4
All the imx clocks that need to be registered by the audiomix need to pass
on the device so that the runtime PM support could work properly.
Signed-off-by: Abel Vesa <[email protected]>
---
drivers/clk/imx/clk.h | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index cb28f06..42960a9 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -321,6 +321,13 @@ static inline struct clk_hw *imx_clk_hw_gate(const char *name, const char *paren
shift, 0, &imx_ccm_lock);
}
+static inline struct clk_hw *imx_dev_clk_hw_gate(struct device *dev, const char *name,
+ const char *parent, void __iomem *reg, u8 shift)
+{
+ return clk_hw_register_gate(dev, name, parent, CLK_SET_RATE_PARENT, reg,
+ shift, 0, &imx_ccm_lock);
+}
+
static inline struct clk_hw *imx_clk_hw_gate_dis(const char *name, const char *parent,
void __iomem *reg, u8 shift)
{
@@ -422,6 +429,15 @@ static inline struct clk_hw *imx_clk_hw_mux(const char *name, void __iomem *reg,
width, 0, &imx_ccm_lock);
}
+static inline struct clk_hw *imx_dev_clk_hw_mux(struct device *dev, const char *name,
+ void __iomem *reg, u8 shift, u8 width,
+ const char * const *parents, int num_parents)
+{
+ return clk_hw_register_mux(dev, name, parents, num_parents,
+ CLK_SET_RATE_NO_REPARENT | CLK_SET_PARENT_GATE,
+ reg, shift, width, 0, &imx_ccm_lock);
+}
+
static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
u8 shift, u8 width, const char * const *parents,
int num_parents)
@@ -484,6 +500,19 @@ static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name,
reg, shift, width, 0, &imx_ccm_lock);
}
+static inline struct clk_hw *imx_dev_clk_hw_mux_flags(struct device *dev,
+ const char *name,
+ void __iomem *reg, u8 shift,
+ u8 width,
+ const char * const *parents,
+ int num_parents,
+ unsigned long flags)
+{
+ return clk_hw_register_mux(dev, name, parents, num_parents,
+ flags | CLK_SET_RATE_NO_REPARENT,
+ reg, shift, width, 0, &imx_ccm_lock);
+}
+
struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name,
struct clk *div, struct clk *mux, struct clk *pll,
struct clk *step);
--
2.7.4
In order to allow runtime PM, the device needs to be passed on
to the register function. Audiomix clock controller, used on
i.MX8MP and future platforms, registers a pll14xx and has runtime
PM support.
Signed-off-by: Abel Vesa <[email protected]>
---
drivers/clk/imx/clk-pll14xx.c | 6 +++---
drivers/clk/imx/clk.h | 13 ++++++++++---
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c
index a83bbbe..2fbc28c 100644
--- a/drivers/clk/imx/clk-pll14xx.c
+++ b/drivers/clk/imx/clk-pll14xx.c
@@ -378,9 +378,9 @@ static const struct clk_ops clk_pll1443x_ops = {
.set_rate = clk_pll1443x_set_rate,
};
-struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name,
- void __iomem *base,
- const struct imx_pll14xx_clk *pll_clk)
+struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name,
+ const char *parent_name, void __iomem *base,
+ const struct imx_pll14xx_clk *pll_clk)
{
struct clk_pll14xx *pll;
struct clk_hw *hw;
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 51d6c26..cb28f06 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -131,9 +131,9 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
#define imx_clk_pll14xx(name, parent_name, base, pll_clk) \
to_clk(imx_clk_hw_pll14xx(name, parent_name, base, pll_clk))
-struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name,
- void __iomem *base,
- const struct imx_pll14xx_clk *pll_clk);
+struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name,
+ const char *parent_name, void __iomem *base,
+ const struct imx_pll14xx_clk *pll_clk);
struct clk_hw *imx_clk_hw_pllv1(enum imx_pllv1_type type, const char *name,
const char *parent, void __iomem *base);
@@ -244,6 +244,13 @@ static inline struct clk *to_clk(struct clk_hw *hw)
return hw->clk;
}
+static inline struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name,
+ void __iomem *base,
+ const struct imx_pll14xx_clk *pll_clk)
+{
+ return imx_dev_clk_hw_pll14xx(NULL, name, parent_name, base, pll_clk);
+}
+
static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate)
{
return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
--
2.7.4
Audiomix is a mix of multiple functionalities controlled by the audio IPs.
In order to split the functionality between the rightfull drivers, it
will be probled by the imx-mix MFD driver.
Signed-off-by: Abel Vesa <[email protected]>
---
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
index a997ca7..77d2901e 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
@@ -617,6 +617,11 @@
#address-cells = <1>;
#size-cells = <1>;
ranges;
+
+ audiomix: audiomix@30e20000 {
+ compatible = "fsl,imx8mp-audiomix";
+ reg = <0x30e20000 0x10000>;
+ };
};
gic: interrupt-controller@38800000 {
--
2.7.4
Some of the i.MX SoCs have a IP for interfacing the dedicated IPs with
clocks, resets and interrupts, plus some other specific control registers.
To allow the functionality to be split between drivers, this MFD driver is
added that has only two purposes: register the devices and map the entire
register addresses. Everything else is left to the dedicated drivers that will
bind to the registered devices.
Signed-off-by: Abel Vesa <[email protected]>
---
drivers/mfd/Kconfig | 11 +++++++++++
drivers/mfd/Makefile | 1 +
drivers/mfd/imx-mix.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 60 insertions(+)
create mode 100644 drivers/mfd/imx-mix.c
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3c547ed..3c89288 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -460,6 +460,17 @@ config MFD_MX25_TSADC
i.MX25 processors. They consist of a conversion queue for general
purpose ADC and a queue for Touchscreens.
+config MFD_IMX_MIX
+ tristate "NXP i.MX Generic Mix Control Driver"
+ depends on OF || COMPILE_TEST
+ help
+ Enable generic mixes support. On some i.MX platforms, there are
+ devices that are a mix of multiple functionalities like reset
+ controllers, clock controllers and some others. In order to split
+ those functionalities between the right drivers, this MFD populates
+ with virtual devices based on what's found in the devicetree node,
+ leaving the rest of the behavior control to the dedicated driver.
+
config MFD_HI6421_PMIC
tristate "HiSilicon Hi6421 PMU/Codec IC"
depends on OF
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f935d10..5b2ae5d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -113,6 +113,7 @@ obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6040_CORE) += twl6040.o
obj-$(CONFIG_MFD_MX25_TSADC) += fsl-imx25-tsadc.o
+obj-$(CONFIG_MFD_IMX_MIX) += imx-mix.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
diff --git a/drivers/mfd/imx-mix.c b/drivers/mfd/imx-mix.c
new file mode 100644
index 00000000..d3f8c71
--- /dev/null
+++ b/drivers/mfd/imx-mix.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+
+#include <linux/mfd/core.h>
+
+static int imx_audiomix_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *base;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dev_set_drvdata(dev, base);
+
+ return devm_of_platform_populate(dev);
+}
+
+static const struct of_device_id imx_audiomix_of_match[] = {
+ { .compatible = "fsl,imx8mp-audiomix" },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx_audiomix_of_match);
+
+static struct platform_driver imx_audiomix_driver = {
+ .probe = imx_audiomix_probe,
+ .driver = {
+ .name = "imx-audiomix",
+ .of_match_table = of_match_ptr(imx_audiomix_of_match),
+ },
+};
+module_platform_driver(imx_audiomix_driver);
--
2.7.4
Add all the clock ids for the audiomix clocks.
Signed-off-by: Abel Vesa <[email protected]>
---
include/dt-bindings/clock/imx8mp-clock.h | 62 ++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/include/dt-bindings/clock/imx8mp-clock.h b/include/dt-bindings/clock/imx8mp-clock.h
index 47ab082..305433f 100644
--- a/include/dt-bindings/clock/imx8mp-clock.h
+++ b/include/dt-bindings/clock/imx8mp-clock.h
@@ -298,4 +298,66 @@
#define IMX8MP_CLK_END 289
+#define IMX8MP_CLK_AUDIOMIX_SAI1_IPG 0
+#define IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1 1
+#define IMX8MP_CLK_AUDIOMIX_SAI1_MCLK2 2
+#define IMX8MP_CLK_AUDIOMIX_SAI1_MCLK3 3
+#define IMX8MP_CLK_AUDIOMIX_SAI2_IPG 4
+#define IMX8MP_CLK_AUDIOMIX_SAI2_MCLK1 5
+#define IMX8MP_CLK_AUDIOMIX_SAI2_MCLK2 6
+#define IMX8MP_CLK_AUDIOMIX_SAI2_MCLK3 7
+#define IMX8MP_CLK_AUDIOMIX_SAI3_IPG 8
+#define IMX8MP_CLK_AUDIOMIX_SAI3_MCLK1 9
+#define IMX8MP_CLK_AUDIOMIX_SAI3_MCLK2 10
+#define IMX8MP_CLK_AUDIOMIX_SAI3_MCLK3 11
+#define IMX8MP_CLK_AUDIOMIX_SAI5_IPG 12
+#define IMX8MP_CLK_AUDIOMIX_SAI5_MCLK1 13
+#define IMX8MP_CLK_AUDIOMIX_SAI5_MCLK2 14
+#define IMX8MP_CLK_AUDIOMIX_SAI5_MCLK3 15
+#define IMX8MP_CLK_AUDIOMIX_SAI6_IPG 16
+#define IMX8MP_CLK_AUDIOMIX_SAI6_MCLK1 17
+#define IMX8MP_CLK_AUDIOMIX_SAI6_MCLK2 18
+#define IMX8MP_CLK_AUDIOMIX_SAI6_MCLK3 19
+#define IMX8MP_CLK_AUDIOMIX_SAI7_IPG 20
+#define IMX8MP_CLK_AUDIOMIX_SAI7_MCLK1 21
+#define IMX8MP_CLK_AUDIOMIX_SAI7_MCLK2 22
+#define IMX8MP_CLK_AUDIOMIX_SAI7_MCLK3 23
+#define IMX8MP_CLK_AUDIOMIX_ASRC_IPG 24
+#define IMX8MP_CLK_AUDIOMIX_PDM_IPG 25
+#define IMX8MP_CLK_AUDIOMIX_SDMA2_ROOT 26
+#define IMX8MP_CLK_AUDIOMIX_SDMA3_ROOT 27
+#define IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT 28
+#define IMX8MP_CLK_AUDIOMIX_DSP_ROOT 29
+#define IMX8MP_CLK_AUDIOMIX_DSPDBG_ROOT 30
+#define IMX8MP_CLK_AUDIOMIX_EARC_IPG 31
+#define IMX8MP_CLK_AUDIOMIX_OCRAMA_IPG 32
+#define IMX8MP_CLK_AUDIOMIX_AUD2HTX_IPG 33
+#define IMX8MP_CLK_AUDIOMIX_EDMA_ROOT 34
+#define IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT 35
+#define IMX8MP_CLK_AUDIOMIX_MU2_ROOT 36
+#define IMX8MP_CLK_AUDIOMIX_MU3_ROOT 37
+#define IMX8MP_CLK_AUDIOMIX_EARC_PHY 38
+#define IMX8MP_CLK_AUDIOMIX_PDM_ROOT 39
+#define IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1_SEL 40
+#define IMX8MP_CLK_AUDIOMIX_SAI1_MCLK2_SEL 41
+#define IMX8MP_CLK_AUDIOMIX_SAI2_MCLK1_SEL 42
+#define IMX8MP_CLK_AUDIOMIX_SAI2_MCLK2_SEL 43
+#define IMX8MP_CLK_AUDIOMIX_SAI3_MCLK1_SEL 44
+#define IMX8MP_CLK_AUDIOMIX_SAI3_MCLK2_SEL 45
+#define IMX8MP_CLK_AUDIOMIX_SAI4_MCLK1_SEL 46
+#define IMX8MP_CLK_AUDIOMIX_SAI4_MCLK2_SEL 47
+#define IMX8MP_CLK_AUDIOMIX_SAI5_MCLK1_SEL 48
+#define IMX8MP_CLK_AUDIOMIX_SAI5_MCLK2_SEL 49
+#define IMX8MP_CLK_AUDIOMIX_SAI6_MCLK1_SEL 50
+#define IMX8MP_CLK_AUDIOMIX_SAI6_MCLK2_SEL 51
+#define IMX8MP_CLK_AUDIOMIX_SAI7_MCLK1_SEL 52
+#define IMX8MP_CLK_AUDIOMIX_SAI7_MCLK2_SEL 53
+#define IMX8MP_CLK_AUDIOMIX_PDM_SEL 54
+#define IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL 55
+#define IMX8MP_CLK_AUDIOMIX_SAI_PLL 56
+#define IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS 57
+#define IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT 58
+
+#define IMX8MP_CLK_AUDIOMIX_END 59
+
#endif
--
2.7.4
There are 5 AIPS maps in total, according to the RM. Add the missing ones here.
Signed-off-by: Abel Vesa <[email protected]>
---
arch/arm64/boot/dts/freescale/imx8mp.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
index 71b0c8f..a997ca7 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
@@ -603,6 +603,22 @@
};
};
+ aips4: bus@32c00000 {
+ compatible = "simple-bus";
+ reg = <0x32c00000 0x400000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ };
+
+ aips5: bus@30c00000 {
+ compatible = "simple-bus";
+ reg = <0x30c00000 0x400000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ };
+
gic: interrupt-controller@38800000 {
compatible = "arm,gic-v3";
reg = <0x38800000 0x10000>,
--
2.7.4
Hi Abel,
On Tue, 2020-03-03 at 11:03 +0200, Abel Vesa wrote:
> The imx-mix MFD driver registers some devices, one of which, in case of
> audiomix, maps correctly to a reset controller type. This driver registers
> a reset controller for that. For now, only the EARC specific resets are added.
>
> Signed-off-by: Abel Vesa <[email protected]>
> Reviewed-by: Leonard Crestez <[email protected]>
> ---
> drivers/reset/Kconfig | 7 ++
> drivers/reset/Makefile | 1 +
> drivers/reset/reset-imx-audiomix.c | 122 +++++++++++++++++++++++++
> include/dt-bindings/reset/imx-audiomix-reset.h | 15 +++
> 4 files changed, 145 insertions(+)
> create mode 100644 drivers/reset/reset-imx-audiomix.c
> create mode 100644 include/dt-bindings/reset/imx-audiomix-reset.h
>
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index d9efbfd..2f8d9b3 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -81,6 +81,13 @@ config RESET_INTEL_GW
> Say Y to control the reset signals provided by reset controller.
> Otherwise, say N.
>
> +config RESET_IMX_AUDIOMIX
> + bool "i.MX Audiomix Reset Driver" if COMPILE_TEST
> + depends on HAS_IOMEM
> + default ARCH_MXC
> + help
> + This enables the audiomix reset controller driver for i.MX SoCs.
> +
> config RESET_LANTIQ
> bool "Lantiq XWAY Reset Driver" if COMPILE_TEST
> default SOC_TYPE_XWAY
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 249ed35..cf23d38 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
> obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
> obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
> obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
> +obj-$(CONFIG_RESET_IMX_AUDIOMIX) += reset-imx-audiomix.o
> obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
> obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
> obj-$(CONFIG_RESET_MESON) += reset-meson.o
> diff --git a/drivers/reset/reset-imx-audiomix.c b/drivers/reset/reset-imx-audiomix.c
> new file mode 100644
> index 00000000..d1c62ef
> --- /dev/null
> +++ b/drivers/reset/reset-imx-audiomix.c
> @@ -0,0 +1,122 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP.
> + */
> +
> +#include <dt-bindings/reset/imx-audiomix-reset.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/reset-controller.h>
> +
> +#define IMX_AUDIOMIX_EARC_CTRL_REG 0x200
> +
> +#define IMX_AUDIOMIX_EARC_RESET_BIT 0x0
> +#define IMX_AUDIOMIX_EARC_PHY_RESET_BIT 0x1
> +
> +struct imx_audiomix_reset_data {
> + void __iomem *base;
> + struct reset_controller_dev rcdev;
> + spinlock_t lock;
> +};
> +
> +static int imx_audiomix_reset_set(struct reset_controller_dev *rcdev,
> + unsigned long id, bool assert)
> +{
> + struct imx_audiomix_reset_data *drvdata = container_of(rcdev,
> + struct imx_audiomix_reset_data, rcdev);
> + void __iomem *reg_addr = drvdata->base;
> + unsigned long flags;
> + unsigned int offset;
> + u32 reg;
> +
> + switch (id) {
> + case IMX_AUDIOMIX_EARC_PHY_RESET:
> + reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
> + offset = IMX_AUDIOMIX_EARC_PHY_RESET_BIT;
> + break;
> + case IMX_AUDIOMIX_EARC_RESET:
> + reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
> + offset = IMX_AUDIOMIX_EARC_RESET_BIT;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (assert) {
> + pm_runtime_get_sync(rcdev->dev);
This seems wrong. Why is the runtime PM reference count incremented when
a reset is asserted ...
> + spin_lock_irqsave(&drvdata->lock, flags);
> + reg = readl(reg_addr);
> + writel(reg & ~BIT(offset), reg_addr);
> + spin_unlock_irqrestore(&drvdata->lock, flags);
> + } else {
> + spin_lock_irqsave(&drvdata->lock, flags);
> + reg = readl(reg_addr);
> + writel(reg | BIT(offset), reg_addr);
> + spin_unlock_irqrestore(&drvdata->lock, flags);
> + pm_runtime_put(rcdev->dev);
... and decremented when a reset is deasserted?
Apart from the runtime PM handling this looks like it could reuse reset-
simple.
> + }
> +
> + return 0;
> +}
> +
> +static int imx_audiomix_reset_assert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + return imx_audiomix_reset_set(rcdev, id, true);
> +}
> +
> +static int imx_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
> + unsigned long id)
> +{
> + return imx_audiomix_reset_set(rcdev, id, false);
> +}
> +
> +static const struct reset_control_ops imx_audiomix_reset_ops = {
> + .assert = imx_audiomix_reset_assert,
> + .deassert = imx_audiomix_reset_deassert,
> +};
> +
> +static int imx_audiomix_reset_probe(struct platform_device *pdev)
> +{
> + struct imx_audiomix_reset_data *drvdata;
> + struct device *dev = &pdev->dev;
> +
> + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
> + if (drvdata == NULL)
> + return -ENOMEM;
> +
> + drvdata->base = dev_get_drvdata(dev->parent);
> +
> + platform_set_drvdata(pdev, drvdata);
> +
> + pm_runtime_enable(dev);
> +
> + spin_lock_init(&drvdata->lock);
> +
> + drvdata->rcdev.owner = THIS_MODULE;
> + drvdata->rcdev.nr_resets = IMX_AUDIOMIX_RESET_NUM;
> + drvdata->rcdev.ops = &imx_audiomix_reset_ops;
> + drvdata->rcdev.of_node = dev->of_node;
> + drvdata->rcdev.dev = dev;
> +
> + return devm_reset_controller_register(dev, &drvdata->rcdev);
> +}
> +
> +static const struct of_device_id imx_audiomix_reset_dt_ids[] = {
> + { .compatible = "fsl,imx8mp-audiomix-reset", },
> + { /* sentinel */ },
> +};
> +
> +static struct platform_driver imx_audiomix_reset_driver = {
> + .probe = imx_audiomix_reset_probe,
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = imx_audiomix_reset_dt_ids,
> + },
> +};
> +module_platform_driver(imx_audiomix_reset_driver);
> diff --git a/include/dt-bindings/reset/imx-audiomix-reset.h b/include/dt-bindings/reset/imx-audiomix-reset.h
> new file mode 100644
> index 00000000..2e26878
> --- /dev/null
> +++ b/include/dt-bindings/reset/imx-audiomix-reset.h
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP.
> + */
> +
> +#ifndef DT_BINDING_RESET_IMX_AUDIOMIX_H
> +#define DT_BINDING_RESET_IMX_AUDIOMIX_H
> +
> +#define IMX_AUDIOMIX_EARC_RESET 0x0
> +#define IMX_AUDIOMIX_EARC_PHY_RESET 0x1
> +
> +#define IMX_AUDIOMIX_RESET_NUM 2
> +
> +#endif
> +
The imx-audiomix-reset.h change should go into a separate patch,
together with the binding docs for fsl,imx8mp-audiomix-reset.
regards
Philipp
On Tue, 3 Mar 2020 11:03:22 +0200, Abel Vesa wrote:
> Add all the clock ids for the audiomix clocks.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
> include/dt-bindings/clock/imx8mp-clock.h | 62 ++++++++++++++++++++++++++++++++
> 1 file changed, 62 insertions(+)
>
Acked-by: Rob Herring <[email protected]>
On Tue, Mar 03, 2020 at 11:03:25AM +0200, Abel Vesa wrote:
> The imx-mix MFD driver registers some devices, one of which, in case of
> audiomix, maps correctly to a reset controller type. This driver registers
> a reset controller for that. For now, only the EARC specific resets are added.
>
> Signed-off-by: Abel Vesa <[email protected]>
> Reviewed-by: Leonard Crestez <[email protected]>
> ---
> drivers/reset/Kconfig | 7 ++
> drivers/reset/Makefile | 1 +
> drivers/reset/reset-imx-audiomix.c | 122 +++++++++++++++++++++++++
> include/dt-bindings/reset/imx-audiomix-reset.h | 15 +++
This should be in a binding patch which makes me wonder where is the
binding patch?
Rob
> Subject: [RFC 02/11] arm64: dts: imx8mp: Add AIPS 4 and 5
>
> There are 5 AIPS maps in total, according to the RM. Add the missing ones
> here.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
> arch/arm64/boot/dts/freescale/imx8mp.dtsi | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> index 71b0c8f..a997ca7 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> @@ -603,6 +603,22 @@
> };
> };
>
> + aips4: bus@32c00000 {
> + compatible = "simple-bus";
"fsl,aips-bus", "simple-bus";
> + reg = <0x32c00000 0x400000>;
Size is 64KB
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> + };
> +
> + aips5: bus@30c00000 {
> + compatible = "simple-bus";
> + reg = <0x30c00000 0x400000>;
Ditto. Please correct compatible and reg.
Without this, I think there is no need to only
add bus here? It might be better to also include
subnodes under aips bus.
Regards,
Peng.
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> + };
> +
> gic: interrupt-controller@38800000 {
> compatible = "arm,gic-v3";
> reg = <0x38800000 0x10000>,
> --
> 2.7.4
> Subject: [RFC 04/11] clk: imx: Add gate shared for i.MX8MP audiomix
>
> The newer i.MX platform use some gates that have a shared control bit
> between them.
Could the existing clk_hw_register_gate2 handle your case?
Thanks,
Peng.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
> drivers/clk/imx/Makefile | 2 +-
> drivers/clk/imx/clk-gate-shared.c | 111
> ++++++++++++++++++++++++++++++++++++++
> drivers/clk/imx/clk.h | 4 ++
> 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644
> drivers/clk/imx/clk-gate-shared.c
>
> diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index
> 928f874c..799a8ef 100644
> --- a/drivers/clk/imx/Makefile
> +++ b/drivers/clk/imx/Makefile
> @@ -27,7 +27,7 @@ obj-$(CONFIG_MXC_CLK_SCU) += \
>
> obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o
> obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o
> -obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o
> +obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-gate-shared.o
> obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o
> obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o
>
> diff --git a/drivers/clk/imx/clk-gate-shared.c
> b/drivers/clk/imx/clk-gate-shared.c
> new file mode 100644
> index 00000000..961a0e3
> --- /dev/null
> +++ b/drivers/clk/imx/clk-gate-shared.c
> @@ -0,0 +1,111 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include "clk.h"
> +
> +/**
> + * struct clk_gate_shared - i.MX specific gate clock having the gate
> +flag
> + * shared with other gate clocks
> + */
> +struct clk_gate_shared {
> + struct clk_gate gate;
> + spinlock_t *lock;
> + unsigned int *share_count;
> +};
> +
> +static int clk_gate_shared_enable(struct clk_hw *hw) {
> + struct clk_gate *gate = to_clk_gate(hw);
> + struct clk_gate_shared *shgate = container_of(gate,
> + struct clk_gate_shared, gate);
> + unsigned long flags = 0;
> + int ret = 0;
> +
> + spin_lock_irqsave(shgate->lock, flags);
> +
> + if (shgate->share_count && (*shgate->share_count)++ > 0)
> + goto out;
> +
> + ret = clk_gate_ops.enable(hw);
> +out:
> + spin_unlock_irqrestore(shgate->lock, flags);
> +
> + return ret;
> +}
> +
> +static void clk_gate_shared_disable(struct clk_hw *hw) {
> + struct clk_gate *gate = to_clk_gate(hw);
> + struct clk_gate_shared *shgate = container_of(gate,
> + struct clk_gate_shared, gate);
> + unsigned long flags = 0;
> +
> + spin_lock_irqsave(shgate->lock, flags);
> +
> + if (shgate->share_count) {
> + if (WARN_ON(*shgate->share_count == 0))
> + goto out;
> + else if (--(*shgate->share_count) > 0)
> + goto out;
> + }
> +
> + clk_gate_ops.disable(hw);
> +out:
> + spin_unlock_irqrestore(shgate->lock, flags); }
> +
> +static int clk_gate_shared_is_enabled(struct clk_hw *hw) {
> + return clk_gate_ops.is_enabled(hw);
> +}
> +
> +static const struct clk_ops clk_gate_shared_ops = {
> + .enable = clk_gate_shared_enable,
> + .disable = clk_gate_shared_disable,
> + .is_enabled = clk_gate_shared_is_enabled, };
> +
> +struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char
> *name,
> + const char *parent, void __iomem *reg,
> + u8 shift, unsigned int *share_count) {
> + struct clk_gate_shared *shgate;
> + struct clk_gate *gate;
> + struct clk_hw *hw;
> + struct clk_init_data init;
> + int ret;
> +
> + shgate = kzalloc(sizeof(*shgate), GFP_KERNEL);
> + if (!shgate)
> + return ERR_PTR(-ENOMEM);
> + gate = &shgate->gate;
> +
> + init.name = name;
> + init.ops = &clk_gate_shared_ops;
> + init.flags = CLK_OPS_PARENT_ENABLE;
> + init.parent_names = parent ? &parent : NULL;
> + init.num_parents = parent ? 1 : 0;
> +
> + gate->reg = reg;
> + gate->bit_idx = shift;
> + gate->lock = NULL;
> + gate->hw.init = &init;
> + shgate->lock = &imx_ccm_lock;
> + shgate->share_count = share_count;
> +
> + hw = &gate->hw;
> +
> + ret = clk_hw_register(NULL, hw);
> + if (ret) {
> + kfree(shgate);
> + return ERR_PTR(ret);
> + }
> +
> + return hw;
> +}
> diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index
> f074dd8..51d6c26 100644
> --- a/drivers/clk/imx/clk.h
> +++ b/drivers/clk/imx/clk.h
> @@ -151,6 +151,10 @@ struct clk_hw *imx_clk_hw_sscg_pll(const char
> *name,
> void __iomem *base,
> unsigned long flags);
>
> +struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char
> *name,
> + const char *parent, void __iomem *reg,
> + u8 shift, unsigned int *share_count);
> +
> enum imx_pllv3_type {
> IMX_PLLV3_GENERIC,
> IMX_PLLV3_SYS,
> --
> 2.7.4
> Subject: [RFC 05/11] clk: imx: pll14xx: Add the device as argument when
> registering
>
> In order to allow runtime PM, the device needs to be passed on to the register
> function. Audiomix clock controller, used on i.MX8MP and future platforms,
> registers a pll14xx and has runtime PM support.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
> drivers/clk/imx/clk-pll14xx.c | 6 +++---
> drivers/clk/imx/clk.h | 13 ++++++++++---
> 2 files changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index
> a83bbbe..2fbc28c 100644
> --- a/drivers/clk/imx/clk-pll14xx.c
> +++ b/drivers/clk/imx/clk-pll14xx.c
> @@ -378,9 +378,9 @@ static const struct clk_ops clk_pll1443x_ops = {
> .set_rate = clk_pll1443x_set_rate,
> };
>
> -struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char
> *parent_name,
> - void __iomem *base,
> - const struct imx_pll14xx_clk *pll_clk)
> +struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char
> *name,
> + const char *parent_name, void __iomem
> *base,
> + const struct imx_pll14xx_clk *pll_clk)
> {
Should the pointer dev be passed to clk_hw_register?
Thanks,
Peng.
> struct clk_pll14xx *pll;
> struct clk_hw *hw;
> diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index
> 51d6c26..cb28f06 100644
> --- a/drivers/clk/imx/clk.h
> +++ b/drivers/clk/imx/clk.h
> @@ -131,9 +131,9 @@ struct clk *imx_clk_pll14xx(const char *name, const
> char *parent_name, #define imx_clk_pll14xx(name, parent_name, base,
> pll_clk) \
> to_clk(imx_clk_hw_pll14xx(name, parent_name, base, pll_clk))
>
> -struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char
> *parent_name,
> - void __iomem *base,
> - const struct imx_pll14xx_clk *pll_clk);
> +struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char
> *name,
> + const char *parent_name, void __iomem
> *base,
> + const struct imx_pll14xx_clk *pll_clk);
>
> struct clk_hw *imx_clk_hw_pllv1(enum imx_pllv1_type type, const char
> *name,
> const char *parent, void __iomem *base); @@ -244,6 +244,13 @@
> static inline struct clk *to_clk(struct clk_hw *hw)
> return hw->clk;
> }
>
> +static inline struct clk_hw *imx_clk_hw_pll14xx(const char *name, const
> char *parent_name,
> + void __iomem *base,
> + const struct imx_pll14xx_clk *pll_clk) {
> + return imx_dev_clk_hw_pll14xx(NULL, name, parent_name, base,
> pll_clk);
> +}
> +
> static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate) {
> return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
> --
> 2.7.4
> Subject: [RFC 06/11] clk: imx: Add helpers for passing the device as argument
>
> All the imx clocks that need to be registered by the audiomix need to pass on
> the device so that the runtime PM support could work properly.
>
> Signed-off-by: Abel Vesa <[email protected]>
Reviewed-by: Peng Fan <[email protected]>
> ---
> drivers/clk/imx/clk.h | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index
> cb28f06..42960a9 100644
> --- a/drivers/clk/imx/clk.h
> +++ b/drivers/clk/imx/clk.h
> @@ -321,6 +321,13 @@ static inline struct clk_hw *imx_clk_hw_gate(const
> char *name, const char *paren
> shift, 0, &imx_ccm_lock);
> }
>
> +static inline struct clk_hw *imx_dev_clk_hw_gate(struct device *dev, const
> char *name,
> + const char *parent, void __iomem *reg, u8 shift)
> {
> + return clk_hw_register_gate(dev, name, parent, CLK_SET_RATE_PARENT,
> reg,
> + shift, 0, &imx_ccm_lock);
> +}
> +
> static inline struct clk_hw *imx_clk_hw_gate_dis(const char *name, const
> char *parent,
> void __iomem *reg, u8 shift)
> {
> @@ -422,6 +429,15 @@ static inline struct clk_hw *imx_clk_hw_mux(const
> char *name, void __iomem *reg,
> width, 0, &imx_ccm_lock);
> }
>
> +static inline struct clk_hw *imx_dev_clk_hw_mux(struct device *dev, const
> char *name,
> + void __iomem *reg, u8 shift, u8 width,
> + const char * const *parents, int num_parents)
> {
> + return clk_hw_register_mux(dev, name, parents, num_parents,
> + CLK_SET_RATE_NO_REPARENT |
> CLK_SET_PARENT_GATE,
> + reg, shift, width, 0, &imx_ccm_lock); }
> +
> static inline struct clk *imx_clk_mux2(const char *name, void __iomem
> *reg,
> u8 shift, u8 width, const char * const *parents,
> int num_parents)
> @@ -484,6 +500,19 @@ static inline struct clk_hw
> *imx_clk_hw_mux_flags(const char *name,
> reg, shift, width, 0, &imx_ccm_lock); }
>
> +static inline struct clk_hw *imx_dev_clk_hw_mux_flags(struct device *dev,
> + const char *name,
> + void __iomem *reg, u8 shift,
> + u8 width,
> + const char * const *parents,
> + int num_parents,
> + unsigned long flags)
> +{
> + return clk_hw_register_mux(dev, name, parents, num_parents,
> + flags | CLK_SET_RATE_NO_REPARENT,
> + reg, shift, width, 0, &imx_ccm_lock); }
> +
> struct clk_hw *imx_clk_hw_cpu(const char *name, const char
> *parent_name,
> struct clk *div, struct clk *mux, struct clk *pll,
> struct clk *step);
> --
> 2.7.4
On 20-03-04 12:41:33, Philipp Zabel wrote:
> Hi Abel,
>
> On Tue, 2020-03-03 at 11:03 +0200, Abel Vesa wrote:
> > The imx-mix MFD driver registers some devices, one of which, in case of
> > audiomix, maps correctly to a reset controller type. This driver registers
> > a reset controller for that. For now, only the EARC specific resets are added.
> >
> > Signed-off-by: Abel Vesa <[email protected]>
> > Reviewed-by: Leonard Crestez <[email protected]>
> > ---
> > drivers/reset/Kconfig | 7 ++
> > drivers/reset/Makefile | 1 +
> > drivers/reset/reset-imx-audiomix.c | 122 +++++++++++++++++++++++++
> > include/dt-bindings/reset/imx-audiomix-reset.h | 15 +++
> > 4 files changed, 145 insertions(+)
> > create mode 100644 drivers/reset/reset-imx-audiomix.c
> > create mode 100644 include/dt-bindings/reset/imx-audiomix-reset.h
> >
> > diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> > index d9efbfd..2f8d9b3 100644
> > --- a/drivers/reset/Kconfig
> > +++ b/drivers/reset/Kconfig
> > @@ -81,6 +81,13 @@ config RESET_INTEL_GW
> > Say Y to control the reset signals provided by reset controller.
> > Otherwise, say N.
> >
> > +config RESET_IMX_AUDIOMIX
> > + bool "i.MX Audiomix Reset Driver" if COMPILE_TEST
> > + depends on HAS_IOMEM
> > + default ARCH_MXC
> > + help
> > + This enables the audiomix reset controller driver for i.MX SoCs.
> > +
> > config RESET_LANTIQ
> > bool "Lantiq XWAY Reset Driver" if COMPILE_TEST
> > default SOC_TYPE_XWAY
> > diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> > index 249ed35..cf23d38 100644
> > --- a/drivers/reset/Makefile
> > +++ b/drivers/reset/Makefile
> > @@ -12,6 +12,7 @@ obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
> > obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
> > obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
> > obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
> > +obj-$(CONFIG_RESET_IMX_AUDIOMIX) += reset-imx-audiomix.o
> > obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
> > obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
> > obj-$(CONFIG_RESET_MESON) += reset-meson.o
> > diff --git a/drivers/reset/reset-imx-audiomix.c b/drivers/reset/reset-imx-audiomix.c
> > new file mode 100644
> > index 00000000..d1c62ef
> > --- /dev/null
> > +++ b/drivers/reset/reset-imx-audiomix.c
> > @@ -0,0 +1,122 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2019 NXP.
> > + */
> > +
> > +#include <dt-bindings/reset/imx-audiomix-reset.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/reset-controller.h>
> > +
> > +#define IMX_AUDIOMIX_EARC_CTRL_REG 0x200
> > +
> > +#define IMX_AUDIOMIX_EARC_RESET_BIT 0x0
> > +#define IMX_AUDIOMIX_EARC_PHY_RESET_BIT 0x1
> > +
> > +struct imx_audiomix_reset_data {
> > + void __iomem *base;
> > + struct reset_controller_dev rcdev;
> > + spinlock_t lock;
> > +};
> > +
> > +static int imx_audiomix_reset_set(struct reset_controller_dev *rcdev,
> > + unsigned long id, bool assert)
> > +{
> > + struct imx_audiomix_reset_data *drvdata = container_of(rcdev,
> > + struct imx_audiomix_reset_data, rcdev);
> > + void __iomem *reg_addr = drvdata->base;
> > + unsigned long flags;
> > + unsigned int offset;
> > + u32 reg;
> > +
> > + switch (id) {
> > + case IMX_AUDIOMIX_EARC_PHY_RESET:
> > + reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
> > + offset = IMX_AUDIOMIX_EARC_PHY_RESET_BIT;
> > + break;
> > + case IMX_AUDIOMIX_EARC_RESET:
> > + reg_addr += IMX_AUDIOMIX_EARC_CTRL_REG;
> > + offset = IMX_AUDIOMIX_EARC_RESET_BIT;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + if (assert) {
> > + pm_runtime_get_sync(rcdev->dev);
>
> This seems wrong. Why is the runtime PM reference count incremented when
> a reset is asserted ...
The audiomix IP has its own power domain.
The way I see it, when the last deassert is done, there is no point
in keeping the audiomix on. So, unless the clock controller part of it does it,
the audiomix will be powered down.
>
> > + spin_lock_irqsave(&drvdata->lock, flags);
> > + reg = readl(reg_addr);
> > + writel(reg & ~BIT(offset), reg_addr);
> > + spin_unlock_irqrestore(&drvdata->lock, flags);
> > + } else {
> > + spin_lock_irqsave(&drvdata->lock, flags);
> > + reg = readl(reg_addr);
> > + writel(reg | BIT(offset), reg_addr);
> > + spin_unlock_irqrestore(&drvdata->lock, flags);
> > + pm_runtime_put(rcdev->dev);
>
> ... and decremented when a reset is deasserted?
>
> Apart from the runtime PM handling this looks like it could reuse reset-
> simple.
>
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int imx_audiomix_reset_assert(struct reset_controller_dev *rcdev,
> > + unsigned long id)
> > +{
> > + return imx_audiomix_reset_set(rcdev, id, true);
> > +}
> > +
> > +static int imx_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
> > + unsigned long id)
> > +{
> > + return imx_audiomix_reset_set(rcdev, id, false);
> > +}
> > +
> > +static const struct reset_control_ops imx_audiomix_reset_ops = {
> > + .assert = imx_audiomix_reset_assert,
> > + .deassert = imx_audiomix_reset_deassert,
> > +};
> > +
> > +static int imx_audiomix_reset_probe(struct platform_device *pdev)
> > +{
> > + struct imx_audiomix_reset_data *drvdata;
> > + struct device *dev = &pdev->dev;
> > +
> > + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
> > + if (drvdata == NULL)
> > + return -ENOMEM;
> > +
> > + drvdata->base = dev_get_drvdata(dev->parent);
> > +
> > + platform_set_drvdata(pdev, drvdata);
> > +
> > + pm_runtime_enable(dev);
> > +
> > + spin_lock_init(&drvdata->lock);
> > +
> > + drvdata->rcdev.owner = THIS_MODULE;
> > + drvdata->rcdev.nr_resets = IMX_AUDIOMIX_RESET_NUM;
> > + drvdata->rcdev.ops = &imx_audiomix_reset_ops;
> > + drvdata->rcdev.of_node = dev->of_node;
> > + drvdata->rcdev.dev = dev;
> > +
> > + return devm_reset_controller_register(dev, &drvdata->rcdev);
> > +}
> > +
> > +static const struct of_device_id imx_audiomix_reset_dt_ids[] = {
> > + { .compatible = "fsl,imx8mp-audiomix-reset", },
> > + { /* sentinel */ },
> > +};
> > +
> > +static struct platform_driver imx_audiomix_reset_driver = {
> > + .probe = imx_audiomix_reset_probe,
> > + .driver = {
> > + .name = KBUILD_MODNAME,
> > + .of_match_table = imx_audiomix_reset_dt_ids,
> > + },
> > +};
> > +module_platform_driver(imx_audiomix_reset_driver);
> > diff --git a/include/dt-bindings/reset/imx-audiomix-reset.h b/include/dt-bindings/reset/imx-audiomix-reset.h
> > new file mode 100644
> > index 00000000..2e26878
> > --- /dev/null
> > +++ b/include/dt-bindings/reset/imx-audiomix-reset.h
> > @@ -0,0 +1,15 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2019 NXP.
> > + */
> > +
> > +#ifndef DT_BINDING_RESET_IMX_AUDIOMIX_H
> > +#define DT_BINDING_RESET_IMX_AUDIOMIX_H
> > +
> > +#define IMX_AUDIOMIX_EARC_RESET 0x0
> > +#define IMX_AUDIOMIX_EARC_PHY_RESET 0x1
> > +
> > +#define IMX_AUDIOMIX_RESET_NUM 2
> > +
> > +#endif
> > +
>
> The imx-audiomix-reset.h change should go into a separate patch,
> together with the binding docs for fsl,imx8mp-audiomix-reset.
>
> regards
> Philipp
On Fri, 2020-03-13 at 16:16 +0200, Abel Vesa wrote:
[...]
> > > + if (assert) {
> > > + pm_runtime_get_sync(rcdev->dev);
> >
> > This seems wrong. Why is the runtime PM reference count incremented when
> > a reset is asserted ...
>
> The audiomix IP has its own power domain.
The reset controller does not control the power domain for its
consumers. The consumer of this reset should implement runtime PM.
> The way I see it, when the last deassert is done, there is no point
> in keeping the audiomix on. So, unless the clock controller part of it does it,
> the audiomix will be powered down.
You mean when the last assert is done? Presumably the driver wants to
use the hardware after deasserting the reset and asserts the reset when
it is done.
regards
Philipp
On 20-03-13 16:55:47, Philipp Zabel wrote:
> On Fri, 2020-03-13 at 16:16 +0200, Abel Vesa wrote:
> [...]
> > > > + if (assert) {
> > > > + pm_runtime_get_sync(rcdev->dev);
> > >
> > > This seems wrong. Why is the runtime PM reference count incremented when
> > > a reset is asserted ...
> >
> > The audiomix IP has its own power domain.
>
> The reset controller does not control the power domain for its
> consumers. The consumer of this reset should implement runtime PM.
>
No, the reset controller itself is part of a more complex IP called audiomix
that has its own power domain.
> > The way I see it, when the last deassert is done, there is no point
> > in keeping the audiomix on. So, unless the clock controller part of it does it,
> > the audiomix will be powered down.
>
> You mean when the last assert is done? Presumably the driver wants to
> use the hardware after deasserting the reset and asserts the reset when
> it is done.
No, I mean deassert. If there is no reset asserted anymore, then the audiomix
can power down, if nothing else (I'm talking about the other stuff that's
in the audiomix, like clock controller) keeping it on.
The reset controller needs to be on only when there is an assertion of at least
one reset bit going on.
>
> regards
> Philipp
Quoting Abel Vesa (2020-03-03 01:03:20)
> In order to allow runtime PM, the device needs to be passed on
> to the register function. Audiomix clock controller, used on
> i.MX8MP and future platforms, registers a pll14xx and has runtime
> PM support.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
Reviewed-by: Stephen Boyd <[email protected]>
Quoting Abel Vesa (2020-03-03 01:03:21)
> All the imx clocks that need to be registered by the audiomix need to pass
> on the device so that the runtime PM support could work properly.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
Reviewed-by: Stephen Boyd <[email protected]>
Quoting Abel Vesa (2020-03-03 01:03:22)
> Add all the clock ids for the audiomix clocks.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
Reviewed-by: Stephen Boyd <[email protected]>
Quoting Abel Vesa (2020-03-03 01:03:23)
> diff --git a/drivers/clk/imx/clk-audiomix.c b/drivers/clk/imx/clk-audiomix.c
> new file mode 100644
> index 00000000..8b84943
> --- /dev/null
> +++ b/drivers/clk/imx/clk-audiomix.c
> @@ -0,0 +1,237 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP.
> + */
> +
> +#include <dt-bindings/clock/imx8mp-clock.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include "clk.h"
> +
> +static int shared_count_pdm;
> +static struct clk_hw **hws;
> +static struct clk_hw_onecell_data *clk_hw_data;
> +static uint32_t audiomix_clk_saved_regs[14];
> +static struct clk *clk_audio_root;
> +static struct clk *clk_audio_ahb;
> +static struct clk *clk_audio_axi_div;
> +
> +static const struct imx_pll14xx_rate_table imx_audiomix_sai_pll_tbl[] = {
> + PLL_1443X_RATE(650000000U, 325, 3, 2, 0),
> +};
> +
> +static const struct imx_pll14xx_clk imx_audiomix_sai_pll = {
> + .type = PLL_1443X,
> + .rate_table = imx_audiomix_sai_pll_tbl,
> +};
> +
> +static const char *imx_sai_mclk2_sels[] = {"sai1", "sai2", "sai3", "dummy",
> + "sai5", "sai6", "sai7", "dummy",
> + "dummy", "dummy", "dummy",
> + "dummy", "dummy", "dummy", "dummy"};
> +static const char *imx_sai1_mclk1_sels[] = {"sai1", "dummy", };
> +static const char *imx_sai2_mclk1_sels[] = {"sai2", "dummy", };
> +static const char *imx_sai3_mclk1_sels[] = {"sai3", "dummy", };
> +static const char *imx_sai5_mclk1_sels[] = {"sai5", "dummy", };
> +static const char *imx_sai6_mclk1_sels[] = {"sai6", "dummy", };
> +static const char *imx_sai7_mclk1_sels[] = {"sai7", "dummy", };
> +static const char *imx_pdm_sels[] = {"pdm", "sai_pll_div2", "dummy", "dummy" };
> +static const char *imx_sai_pll_ref_sels[] = {"osc_24m", "dummy", "dummy", "dummy", };
> +static const char *imx_sai_pll_bypass_sels[] = {"sai_pll", "sai_pll_ref_sel", };
> +
> +static int imx_audiomix_clk_suspend(struct device *dev)
> +{
> + void __iomem *base;
> +
> + base = dev_get_drvdata(dev->parent);
> +
> + audiomix_clk_saved_regs[0] = readl(base);
> + audiomix_clk_saved_regs[1] = readl(base + 0x4);
> +
> + audiomix_clk_saved_regs[2] = readl(base + 0x300);
> + audiomix_clk_saved_regs[3] = readl(base + 0x304);
> + audiomix_clk_saved_regs[4] = readl(base + 0x308);
> + audiomix_clk_saved_regs[5] = readl(base + 0x30C);
> + audiomix_clk_saved_regs[6] = readl(base + 0x310);
> + audiomix_clk_saved_regs[7] = readl(base + 0x314);
> + audiomix_clk_saved_regs[8] = readl(base + 0x318);
> +
> + audiomix_clk_saved_regs[9] = readl(base + 0x400);
> + audiomix_clk_saved_regs[10] = readl(base + 0x404);
> + audiomix_clk_saved_regs[11] = readl(base + 0x408);
> + audiomix_clk_saved_regs[12] = readl(base + 0x40C);
> + audiomix_clk_saved_regs[13] = readl(base + 0x410);
Maybe use three loops that have an 'i' and an offset and then += 4 all
the time? Would be a little more compact.
> +
> + clk_disable_unprepare(clk_audio_ahb);
> + clk_disable_unprepare(clk_audio_root);
> + clk_disable_unprepare(clk_audio_axi_div);
> + pm_runtime_put(dev);
> +
> + return 0;
> +}
> +
> +static int imx_audiomix_clk_resume(struct device *dev)
> +{
> + void __iomem *base;
> +
> + base = dev_get_drvdata(dev->parent);
> +
> + pm_runtime_get(dev);
> + clk_prepare_enable(clk_audio_ahb);
> + clk_prepare_enable(clk_audio_root);
> + clk_prepare_enable(clk_audio_axi_div);
> +
> + writel(audiomix_clk_saved_regs[0], base);
> + writel(audiomix_clk_saved_regs[1], base + 0x4);
> +
> + writel(audiomix_clk_saved_regs[2], base + 0x300);
> + writel(audiomix_clk_saved_regs[3], base + 0x304);
> + writel(audiomix_clk_saved_regs[4], base + 0x308);
> + writel(audiomix_clk_saved_regs[5], base + 0x30C);
> + writel(audiomix_clk_saved_regs[6], base + 0x310);
> + writel(audiomix_clk_saved_regs[7], base + 0x314);
> + writel(audiomix_clk_saved_regs[8], base + 0x318);
> +
> + writel(audiomix_clk_saved_regs[9], base + 0x400);
> + writel(audiomix_clk_saved_regs[10], base + 0x404);
> + writel(audiomix_clk_saved_regs[11], base + 0x408);
> + writel(audiomix_clk_saved_regs[12], base + 0x40C);
> + writel(audiomix_clk_saved_regs[13], base + 0x410);
> +
> + return 0;
> +}
> +
> +static int imx_audiomix_clk_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + void __iomem *base;
> +
> + clk_audio_root = of_clk_get_by_name(np, "audio_root");
Any reason devm_clk_get() can't be used?
> + if (IS_ERR(clk_audio_root))
> + return PTR_ERR(clk_audio_root);
> +
> + clk_audio_ahb = of_clk_get_by_name(np, "audio_ahb");
> + if (IS_ERR(clk_audio_ahb))
> + return PTR_ERR(clk_audio_ahb);
> +
> + clk_audio_axi_div = of_clk_get_by_name(np, "audio_axi_div");
> + if (IS_ERR(clk_audio_axi_div))
> + return PTR_ERR(clk_audio_axi_div);
> +
> + base = dev_get_drvdata(dev->parent);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, IMX8MP_CLK_AUDIOMIX_END), GFP_KERNEL);
This line is long. Please limit length.
> + if (WARN_ON(!clk_hw_data))
> + return -ENOMEM;
> +
> + clk_hw_data->num = IMX8MP_CLK_AUDIOMIX_END;
> + hws = clk_hw_data->hws;
> +
> + pm_runtime_enable(dev);
> +
> + hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = imx_dev_clk_hw_mux(dev, "sai_pll_ref_sel", base + 0x400, 0, 2, imx_sai_pll_ref_sels, ARRAY_SIZE(imx_sai_pll_ref_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel", base + 0x400, &imx_audiomix_sai_pll);
> +
> + hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = imx_dev_clk_hw_mux_flags(dev, "sai_pll_bypass", base + 0x400, 4, 1, imx_sai_pll_bypass_sels, ARRAY_SIZE(imx_sai_pll_bypass_sels), CLK_SET_RATE_PARENT);
> +
> + hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = imx_dev_clk_hw_gate(dev, "sai_pll_out", "sai_pll_bypass", base + 0x400, 13);
> +
> + hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1_SEL] = imx_dev_clk_hw_mux_flags(dev, "sai1_mclk1_sel", base + 0x300, 0, 1, imx_sai1_mclk1_sels, ARRAY_SIZE(imx_sai1_mclk1_sels), CLK_SET_RATE_PARENT);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai1_mclk2_sel", base + 0x300, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK1_SEL] = imx_dev_clk_hw_mux_flags(dev, "sai2_mclk1_sel", base + 0x304, 0, 1, imx_sai2_mclk1_sels, ARRAY_SIZE(imx_sai2_mclk1_sels), CLK_SET_RATE_PARENT);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai2_mclk2_sel", base + 0x304, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK1_SEL] = imx_dev_clk_hw_mux_flags(dev, "sai3_mclk1_sel", base + 0x308, 0, 1, imx_sai3_mclk1_sels, ARRAY_SIZE(imx_sai3_mclk1_sels), CLK_SET_RATE_PARENT);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai3_mclk2_sel", base + 0x308, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK1_SEL] = imx_dev_clk_hw_mux(dev, "sai5_mclk1_sel", base + 0x30C, 0, 1, imx_sai5_mclk1_sels, ARRAY_SIZE(imx_sai5_mclk1_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai5_mclk2_sel", base + 0x30C, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK1_SEL] = imx_dev_clk_hw_mux(dev, "sai6_mclk1_sel", base + 0x310, 0, 1, imx_sai6_mclk1_sels, ARRAY_SIZE(imx_sai6_mclk1_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai6_mclk2_sel", base + 0x310, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK1_SEL] = imx_dev_clk_hw_mux(dev, "sai7_mclk1_sel", base + 0x314, 0, 1, imx_sai7_mclk1_sels, ARRAY_SIZE(imx_sai7_mclk1_sels));
> + hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK2_SEL] = imx_dev_clk_hw_mux(dev, "sai7_mclk2_sel", base + 0x314, 1, 4, imx_sai_mclk2_sels, ARRAY_SIZE(imx_sai_mclk2_sels));
> +
> + hws[IMX8MP_CLK_AUDIOMIX_SAI1_IPG] = imx_dev_clk_hw_gate(dev, "sai1_ipg_clk", "ipg_audio_root", base, 0);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK1] = imx_dev_clk_hw_gate(dev, "sai1_mclk1_clk", "sai1_mclk1_sel", base, 1);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK2] = imx_dev_clk_hw_gate(dev, "sai1_mclk2_clk", "sai1_mclk2_sel", base, 2);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI1_MCLK3] = imx_dev_clk_hw_gate(dev, "sai1_mclk3_clk", "sai_pll_out", base, 3);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI2_IPG] = imx_dev_clk_hw_gate(dev, "sai2_ipg_clk", "ipg_audio_root", base, 4);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK1] = imx_dev_clk_hw_gate(dev, "sai2_mclk1_clk", "sai2_mclk1_sel", base, 5);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK2] = imx_dev_clk_hw_gate(dev, "sai2_mclk2_clk", "sai2_mclk2_sel", base, 6);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI2_MCLK3] = imx_dev_clk_hw_gate(dev, "sai2_mclk3_clk", "sai_pll_out", base, 7);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI3_IPG] = imx_dev_clk_hw_gate(dev, "sai3_ipg_clk", "ipg_audio_root", base, 8);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK1] = imx_dev_clk_hw_gate(dev, "sai3_mclk1_clk", "sai3_mclk1_sel", base, 9);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK2] = imx_dev_clk_hw_gate(dev, "sai3_mclk2_clk", "sai3_mclk2_sel", base, 10);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI3_MCLK3] = imx_dev_clk_hw_gate(dev, "sai3_mclk3_clk", "sai_pll_out", base, 11);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI5_IPG] = imx_dev_clk_hw_gate(dev, "sai5_ipg_clk", "ipg_audio_root", base, 12);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK1] = imx_dev_clk_hw_gate(dev, "sai5_mclk1_clk", "sai5_mclk1_sel", base, 13);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK2] = imx_dev_clk_hw_gate(dev, "sai5_mclk2_clk", "sai5_mclk2_sel", base, 14);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI5_MCLK3] = imx_dev_clk_hw_gate(dev, "sai5_mclk3_clk", "sai_pll_out", base, 15);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI6_IPG] = imx_dev_clk_hw_gate(dev, "sai6_ipg_clk", "ipg_audio_root", base, 16);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK1] = imx_dev_clk_hw_gate(dev, "sai6_mclk1_clk", "sai6_mclk1_sel", base, 17);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK2] = imx_dev_clk_hw_gate(dev, "sai6_mclk2_clk", "sai6_mclk2_sel", base, 18);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI6_MCLK3] = imx_dev_clk_hw_gate(dev, "sai6_mclk3_clk", "sai_pll_out", base, 19);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI7_IPG] = imx_dev_clk_hw_gate(dev, "sai7_ipg_clk", "ipg_audio_root", base, 20);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK1] = imx_dev_clk_hw_gate(dev, "sai7_mclk1_clk", "sai7_mclk1_sel", base, 21);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK2] = imx_dev_clk_hw_gate(dev, "sai7_mclk2_clk", "sai7_mclk2_sel", base, 22);
> + hws[IMX8MP_CLK_AUDIOMIX_SAI7_MCLK3] = imx_dev_clk_hw_gate(dev, "sai7_mclk3_clk", "sai_pll_out", base, 23);
> + hws[IMX8MP_CLK_AUDIOMIX_ASRC_IPG] = imx_dev_clk_hw_gate(dev, "asrc_ipg_clk", "ipg_audio_root", base, 24);
> + hws[IMX8MP_CLK_AUDIOMIX_PDM_IPG] = imx_dev_clk_hw_gate_shared(dev, "pdm_ipg_clk", "ipg_audio_root", base, 25, &shared_count_pdm);
> + hws[IMX8MP_CLK_AUDIOMIX_PDM_ROOT] = imx_dev_clk_hw_gate_shared(dev, "pdm_root_clk", "pdm", base, 25, &shared_count_pdm);
> +
> + hws[IMX8MP_CLK_AUDIOMIX_SDMA2_ROOT] = imx_dev_clk_hw_gate(dev, "sdma2_root_clk", "ipg_audio_root", base, 26);
> + hws[IMX8MP_CLK_AUDIOMIX_SDMA3_ROOT] = imx_dev_clk_hw_gate(dev, "sdma3_root_clk", "ipg_audio_root", base, 27);
> + hws[IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT] = imx_dev_clk_hw_gate(dev, "spba2_root_clk", "ipg_audio_root", base, 28);
> + hws[IMX8MP_CLK_AUDIOMIX_DSP_ROOT] = imx_dev_clk_hw_gate(dev, "dsp_root_clk", "ipg_audio_root", base, 29);
> + hws[IMX8MP_CLK_AUDIOMIX_DSPDBG_ROOT] = imx_dev_clk_hw_gate(dev, "dsp_dbg_clk", "ipg_audio_root", base, 30);
> + hws[IMX8MP_CLK_AUDIOMIX_EARC_IPG] = imx_dev_clk_hw_gate(dev, "earc_ipg_clk", "ipg_audio_root", base, 31);
> +
> + hws[IMX8MP_CLK_AUDIOMIX_OCRAMA_IPG] = imx_dev_clk_hw_gate(dev, "ocram_a_ipg_clk", "ipg_audio_root", base + 4, 0);
> + hws[IMX8MP_CLK_AUDIOMIX_AUD2HTX_IPG] = imx_dev_clk_hw_gate(dev, "aud2htx_ipg_clk", "ipg_audio_root", base + 4, 1);
> + hws[IMX8MP_CLK_AUDIOMIX_EDMA_ROOT] = imx_dev_clk_hw_gate(dev, "edma_root_clk", "ipg_audio_root", base + 4, 2);
> + hws[IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT] = imx_dev_clk_hw_gate(dev, "aud_pll_clk", "ipg_audio_root", base + 4, 3);
> + hws[IMX8MP_CLK_AUDIOMIX_MU2_ROOT] = imx_dev_clk_hw_gate(dev, "mu2_root_clk", "ipg_audio_root", base + 4, 4);
> + hws[IMX8MP_CLK_AUDIOMIX_MU3_ROOT] = imx_dev_clk_hw_gate(dev, "mu3_root_clk", "ipg_audio_root", base + 4, 5);
> + hws[IMX8MP_CLK_AUDIOMIX_EARC_PHY] = imx_dev_clk_hw_gate(dev, "earc_phy_clk", "ipg_audio_root", base + 4, 6);
These ones are OK because they're basically setting up an array and we
can't help it.
> +
> + hws[IMX8MP_CLK_AUDIOMIX_PDM_SEL] = imx_dev_clk_hw_mux(dev, "pdm_sel", base + 0x318, 1, 4, imx_pdm_sels, ARRAY_SIZE(imx_pdm_sels));
> +
> + /* unbypass the pll */
> + clk_hw_set_parent(hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS], hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL]);
But this is long again so please don't.
> +
> + imx_check_clk_hws(hws, IMX8MP_CLK_AUDIOMIX_END);
> +
> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
> +
> + return 0;
> +}
> +
> +UNIVERSAL_DEV_PM_OPS(imx_audiomix_clk_pm_ops, imx_audiomix_clk_suspend,
> + imx_audiomix_clk_resume, imx_audiomix_clk_resume);
> +
> +static const struct of_device_id imx_audiomix_clk_of_match[] = {
> + { .compatible = "fsl,imx8mp-audiomix-clk" },
> + { /* Sentinel */ },
Nitpick: Drop comma after sentinel so that nothing can come after
without causing compile error.
> +};
On 20-03-13 07:44:43, Peng Fan wrote:
> > Subject: [RFC 02/11] arm64: dts: imx8mp: Add AIPS 4 and 5
> >
> > There are 5 AIPS maps in total, according to the RM. Add the missing ones
> > here.
> >
> > Signed-off-by: Abel Vesa <[email protected]>
> > ---
> > arch/arm64/boot/dts/freescale/imx8mp.dtsi | 16 ++++++++++++++++
> > 1 file changed, 16 insertions(+)
> >
> > diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> > b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> > index 71b0c8f..a997ca7 100644
> > --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> > +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> > @@ -603,6 +603,22 @@
> > };
> > };
> >
> > + aips4: bus@32c00000 {
> > + compatible = "simple-bus";
>
> "fsl,aips-bus", "simple-bus";
>
> > + reg = <0x32c00000 0x400000>;
>
> Size is 64KB
>
> > + #address-cells = <1>;
> > + #size-cells = <1>;
> > + ranges;
> > + };
> > +
> > + aips5: bus@30c00000 {
> > + compatible = "simple-bus";
> > + reg = <0x30c00000 0x400000>;
>
> Ditto. Please correct compatible and reg.
>
Will do in the next version.
> Without this, I think there is no need to only
> add bus here? It might be better to also include
> subnodes under aips bus.
AIPS 5 is needed by the next patch in this series.
So it wouldn't make sense to have a patch that adds
only the fifth one, skipping the fourth one.
>
> Regards,
> Peng.
>
> > + #address-cells = <1>;
> > + #size-cells = <1>;
> > + ranges;
> > + };
> > +
> > gic: interrupt-controller@38800000 {
> > compatible = "arm,gic-v3";
> > reg = <0x38800000 0x10000>,
> > --
> > 2.7.4
>
On Tue, 03 Mar 2020, Abel Vesa wrote:
> Some of the i.MX SoCs have a IP for interfacing the dedicated IPs with
> clocks, resets and interrupts, plus some other specific control registers.
> To allow the functionality to be split between drivers, this MFD driver is
> added that has only two purposes: register the devices and map the entire
> register addresses. Everything else is left to the dedicated drivers that will
> bind to the registered devices.
>
> Signed-off-by: Abel Vesa <[email protected]>
> ---
> drivers/mfd/Kconfig | 11 +++++++++++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/imx-mix.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 60 insertions(+)
> create mode 100644 drivers/mfd/imx-mix.c
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3c547ed..3c89288 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -460,6 +460,17 @@ config MFD_MX25_TSADC
> i.MX25 processors. They consist of a conversion queue for general
> purpose ADC and a queue for Touchscreens.
>
> +config MFD_IMX_MIX
> + tristate "NXP i.MX Generic Mix Control Driver"
> + depends on OF || COMPILE_TEST
> + help
> + Enable generic mixes support. On some i.MX platforms, there are
> + devices that are a mix of multiple functionalities like reset
> + controllers, clock controllers and some others. In order to split
> + those functionalities between the right drivers, this MFD populates
> + with virtual devices based on what's found in the devicetree node,
> + leaving the rest of the behavior control to the dedicated driver.
> +
> config MFD_HI6421_PMIC
> tristate "HiSilicon Hi6421 PMU/Codec IC"
> depends on OF
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index f935d10..5b2ae5d 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -113,6 +113,7 @@ obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
> obj-$(CONFIG_TWL6040_CORE) += twl6040.o
>
> obj-$(CONFIG_MFD_MX25_TSADC) += fsl-imx25-tsadc.o
> +obj-$(CONFIG_MFD_IMX_MIX) += imx-mix.o
>
> obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
> obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
> diff --git a/drivers/mfd/imx-mix.c b/drivers/mfd/imx-mix.c
> new file mode 100644
> index 00000000..d3f8c71
> --- /dev/null
> +++ b/drivers/mfd/imx-mix.c
> @@ -0,0 +1,48 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_platform.h>
> +
> +#include <linux/mfd/core.h>
> +
> +static int imx_audiomix_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + void __iomem *base;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + dev_set_drvdata(dev, base);
> +
> + return devm_of_platform_populate(dev);
> +}
> +
> +static const struct of_device_id imx_audiomix_of_match[] = {
> + { .compatible = "fsl,imx8mp-audiomix" },
> + { /* Sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, imx_audiomix_of_match);
This needs DT binding documentation.
Do the sub-device memory ranges overlap?
> +static struct platform_driver imx_audiomix_driver = {
> + .probe = imx_audiomix_probe,
> + .driver = {
> + .name = "imx-audiomix",
> + .of_match_table = of_match_ptr(imx_audiomix_of_match),
> + },
> +};
> +module_platform_driver(imx_audiomix_driver);
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
On 20-03-26 11:03:06, Lee Jones wrote:
> On Tue, 03 Mar 2020, Abel Vesa wrote:
>
> > Some of the i.MX SoCs have a IP for interfacing the dedicated IPs with
> > clocks, resets and interrupts, plus some other specific control registers.
> > To allow the functionality to be split between drivers, this MFD driver is
> > added that has only two purposes: register the devices and map the entire
> > register addresses. Everything else is left to the dedicated drivers that will
> > bind to the registered devices.
> >
> > Signed-off-by: Abel Vesa <[email protected]>
> > ---
> > drivers/mfd/Kconfig | 11 +++++++++++
> > drivers/mfd/Makefile | 1 +
> > drivers/mfd/imx-mix.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 60 insertions(+)
> > create mode 100644 drivers/mfd/imx-mix.c
> >
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 3c547ed..3c89288 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -460,6 +460,17 @@ config MFD_MX25_TSADC
> > i.MX25 processors. They consist of a conversion queue for general
> > purpose ADC and a queue for Touchscreens.
> >
> > +config MFD_IMX_MIX
> > + tristate "NXP i.MX Generic Mix Control Driver"
> > + depends on OF || COMPILE_TEST
> > + help
> > + Enable generic mixes support. On some i.MX platforms, there are
> > + devices that are a mix of multiple functionalities like reset
> > + controllers, clock controllers and some others. In order to split
> > + those functionalities between the right drivers, this MFD populates
> > + with virtual devices based on what's found in the devicetree node,
> > + leaving the rest of the behavior control to the dedicated driver.
> > +
> > config MFD_HI6421_PMIC
> > tristate "HiSilicon Hi6421 PMU/Codec IC"
> > depends on OF
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index f935d10..5b2ae5d 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -113,6 +113,7 @@ obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
> > obj-$(CONFIG_TWL6040_CORE) += twl6040.o
> >
> > obj-$(CONFIG_MFD_MX25_TSADC) += fsl-imx25-tsadc.o
> > +obj-$(CONFIG_MFD_IMX_MIX) += imx-mix.o
> >
> > obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
> > obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
> > diff --git a/drivers/mfd/imx-mix.c b/drivers/mfd/imx-mix.c
> > new file mode 100644
> > index 00000000..d3f8c71
> > --- /dev/null
> > +++ b/drivers/mfd/imx-mix.c
> > @@ -0,0 +1,48 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2019 NXP.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/err.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of_address.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/types.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/of_platform.h>
> > +
> > +#include <linux/mfd/core.h>
> > +
> > +static int imx_audiomix_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct resource *res;
> > + void __iomem *base;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + base = devm_ioremap_resource(dev, res);
> > + if (IS_ERR(base))
> > + return PTR_ERR(base);
> > +
> > + dev_set_drvdata(dev, base);
> > +
> > + return devm_of_platform_populate(dev);
> > +}
> > +
> > +static const struct of_device_id imx_audiomix_of_match[] = {
> > + { .compatible = "fsl,imx8mp-audiomix" },
> > + { /* Sentinel */ },
> > +};
> > +MODULE_DEVICE_TABLE(of, imx_audiomix_of_match);
>
> This needs DT binding documentation.
>
> Do the sub-device memory ranges overlap?
>
Yes, they do.
Resent another version of this series yesterday.
> > +static struct platform_driver imx_audiomix_driver = {
> > + .probe = imx_audiomix_probe,
> > + .driver = {
> > + .name = "imx-audiomix",
> > + .of_match_table = of_match_ptr(imx_audiomix_of_match),
> > + },
> > +};
> > +module_platform_driver(imx_audiomix_driver);
>
> --
> Lee Jones [李琼斯]
> Linaro Services Technical Lead
> Linaro.org │ Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog
On Thu, Apr 16, 2020 at 1:06 PM Arnd Bergmann <[email protected]> wrote:
>
> On Tue, Mar 3, 2020 at 10:04 AM Abel Vesa <[email protected]> wrote:
> >
> > The i.MX8MP has some new IPs called mixes. They are formed usually by some
> > GPRs that can be split into different functionalities. The first example
> > here is the audiomix which has dedicated registers that can be registered
> > as a clock controller and some other registers that can be registered as
> > a reset controller, plus some dedicated ones that will be registered as
> > syscon and used by each dedicated audio IP.
> >
> > More mixes to be following the same structure are to come, like hdmimix,
> > dispmix and mediamix. They will all be populated and registered by the MFD
> > imx-mix generic driver.
>
> Can you enumerate what functionality is in each one?
>
> I'm not convinced that using an MFD driver is the best solution here,
> compared to e.g. a clk driver with a few extra bits in it, if most of the
> code for the child drivers ends up being for the clk subsystem.
>
> Lee suggested maybe having a generic (platform independent) driver
> for it, which may help here, as it would let others share the trivial
> mfd portion.
>
> Another option that we are using on several platforms today is to
> have a single syscon node and have other drivers that reference
> that one using a phandle to get at the regmap.
Sorry I replied to the wrong thread, I meant to reply to the v3 version.
Arnd
On Tue, Mar 3, 2020 at 10:04 AM Abel Vesa <[email protected]> wrote:
>
> The i.MX8MP has some new IPs called mixes. They are formed usually by some
> GPRs that can be split into different functionalities. The first example
> here is the audiomix which has dedicated registers that can be registered
> as a clock controller and some other registers that can be registered as
> a reset controller, plus some dedicated ones that will be registered as
> syscon and used by each dedicated audio IP.
>
> More mixes to be following the same structure are to come, like hdmimix,
> dispmix and mediamix. They will all be populated and registered by the MFD
> imx-mix generic driver.
Can you enumerate what functionality is in each one?
I'm not convinced that using an MFD driver is the best solution here,
compared to e.g. a clk driver with a few extra bits in it, if most of the
code for the child drivers ends up being for the clk subsystem.
Lee suggested maybe having a generic (platform independent) driver
for it, which may help here, as it would let others share the trivial
mfd portion.
Another option that we are using on several platforms today is to
have a single syscon node and have other drivers that reference
that one using a phandle to get at the regmap.
Arnd