diff options
| author | Stephen Boyd <sboyd@kernel.org> | 2023-08-22 10:58:47 -0700 |
|---|---|---|
| committer | Stephen Boyd <sboyd@kernel.org> | 2023-08-22 10:58:47 -0700 |
| commit | 960535d54fffecb3802cf82992233bc4dd6fb1c1 (patch) | |
| tree | f296f16c331787259abb58cd4c8366b9f84ee9dc | |
| parent | 06c2afb862f9da8dc5efa4b6076a0e48c3fbaaa5 (diff) | |
| parent | 72d00e560d10665e6139c9431956a87ded6e9880 (diff) | |
| download | linux-960535d54fffecb3802cf82992233bc4dd6fb1c1.tar.gz linux-960535d54fffecb3802cf82992233bc4dd6fb1c1.tar.bz2 linux-960535d54fffecb3802cf82992233bc4dd6fb1c1.zip | |
Merge tag 'clk-imx-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/abelvesa/linux into clk-imx
Pull i.MX clk driver updates from Abel Vesa:
- Add the PDM IPC clock for i.MX93
- Add 519.75MHz frequency support for i.MX9 PLL
- Simplify the .determine_rate() for GPR mux
- Make the i.MX8QXP LPCG clock use devm_platform_ioremap_resource
- Add the audio mux clock to i.MX8
- Fix the SPLL2 MULT range for PLLv4
- Update the SPLL2 type in i.MX8ULP
- Fix the SAI4 clock on i.MX8MP
- Add silicon revision print for i.MX25 on clocks init
- Drop the return value from __mx25_clocks_init
- Fix the clock pauses on no-op set_rate for i.MX8M composite clock
- Drop restrictions for PLL14xx and fix its max prediv value
- Drop the 393216000 and 361267200 from PLL14xx rate table to allow
glitch free switching
* tag 'clk-imx-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/abelvesa/linux:
clk: imx: pll14xx: dynamically configure PLL for 393216000/361267200Hz
clk: imx: pll14xx: align pdiv with reference manual
clk: imx: composite-8m: fix clock pauses when set_rate would be a no-op
clk: imx25: make __mx25_clocks_init return void
clk: imx25: print silicon revision during init
dt-bindings: clocks: imx8mp: make sai4 a dummy clock
clk: imx8mp: fix sai4 clock
clk: imx: imx8ulp: update SPLL2 type
clk: imx: pllv4: Fix SPLL2 MULT range
clk: imx: imx8: add audio clock mux driver
dt-bindings: clock: fsl,imx8-acm: Add audio clock mux support
clk: imx: clk-imx8qxp-lpcg: Convert to devm_platform_ioremap_resource()
clk: imx: clk-gpr-mux: Simplify .determine_rate()
clk: imx: Add 519.75MHz frequency support for imx9 pll
clk: imx93: Add PDM IPG clk
dt-bindings: clock: imx93: Add PDM IPG clk
| -rw-r--r-- | Documentation/devicetree/bindings/clock/fsl,imx8-acm.yaml | 282 | ||||
| -rw-r--r-- | drivers/clk/imx/Makefile | 3 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-composite-8m.c | 12 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-fracn-gppll.c | 1 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-gpr-mux.c | 8 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-imx25.c | 5 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-imx8-acm.c | 476 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-imx8mp.c | 5 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-imx8qxp-lpcg.c | 4 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-imx8ulp.c | 2 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-imx93.c | 4 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-pll14xx.c | 13 | ||||
| -rw-r--r-- | drivers/clk/imx/clk-pllv4.c | 46 | ||||
| -rw-r--r-- | drivers/clk/imx/clk.h | 1 | ||||
| -rw-r--r-- | include/dt-bindings/clock/imx8-clock.h | 28 | ||||
| -rw-r--r-- | include/dt-bindings/clock/imx8mp-clock.h | 2 | ||||
| -rw-r--r-- | include/dt-bindings/clock/imx93-clock.h | 3 | ||||
| -rw-r--r-- | include/soc/imx/revision.h | 1 |
18 files changed, 851 insertions, 45 deletions
diff --git a/Documentation/devicetree/bindings/clock/fsl,imx8-acm.yaml b/Documentation/devicetree/bindings/clock/fsl,imx8-acm.yaml new file mode 100644 index 000000000000..07b9d21719c4 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/fsl,imx8-acm.yaml @@ -0,0 +1,282 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/fsl,imx8-acm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX8 Audio Clock Mux + +maintainers: + - Shengjiu Wang <shengjiu.wang@nxp.com> + +description: | + NXP i.MX8 Audio Clock Mux is dedicated clock muxing IP + used to control Audio related clock on the SoC. + +properties: + compatible: + enum: + - fsl,imx8dxl-acm + - fsl,imx8qm-acm + - fsl,imx8qxp-acm + + reg: + maxItems: 1 + + power-domains: + minItems: 13 + maxItems: 21 + + '#clock-cells': + const: 1 + description: + The clock consumer should specify the desired clock by having the clock + ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx8-clock.h + for the full list of i.MX8 ACM clock IDs. + + clocks: + minItems: 13 + maxItems: 27 + + clock-names: + minItems: 13 + maxItems: 27 + +required: + - compatible + - reg + - power-domains + - '#clock-cells' + - clocks + - clock-names + +allOf: + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qxp-acm + then: + properties: + power-domains: + items: + - description: power domain of IMX_SC_R_AUDIO_CLK_0 + - description: power domain of IMX_SC_R_AUDIO_CLK_1 + - description: power domain of IMX_SC_R_MCLK_OUT_0 + - description: power domain of IMX_SC_R_MCLK_OUT_1 + - description: power domain of IMX_SC_R_AUDIO_PLL_0 + - description: power domain of IMX_SC_R_AUDIO_PLL_1 + - description: power domain of IMX_SC_R_ASRC_0 + - description: power domain of IMX_SC_R_ASRC_1 + - description: power domain of IMX_SC_R_ESAI_0 + - description: power domain of IMX_SC_R_SAI_0 + - description: power domain of IMX_SC_R_SAI_1 + - description: power domain of IMX_SC_R_SAI_2 + - description: power domain of IMX_SC_R_SAI_3 + - description: power domain of IMX_SC_R_SAI_4 + - description: power domain of IMX_SC_R_SAI_5 + - description: power domain of IMX_SC_R_SPDIF_0 + - description: power domain of IMX_SC_R_MQS_0 + + clocks: + minItems: 18 + maxItems: 18 + + clock-names: + items: + - const: aud_rec_clk0_lpcg_clk + - const: aud_rec_clk1_lpcg_clk + - const: aud_pll_div_clk0_lpcg_clk + - const: aud_pll_div_clk1_lpcg_clk + - const: ext_aud_mclk0 + - const: ext_aud_mclk1 + - const: esai0_rx_clk + - const: esai0_rx_hf_clk + - const: esai0_tx_clk + - const: esai0_tx_hf_clk + - const: spdif0_rx + - const: sai0_rx_bclk + - const: sai0_tx_bclk + - const: sai1_rx_bclk + - const: sai1_tx_bclk + - const: sai2_rx_bclk + - const: sai3_rx_bclk + - const: sai4_rx_bclk + + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qm-acm + then: + properties: + power-domains: + items: + - description: power domain of IMX_SC_R_AUDIO_CLK_0 + - description: power domain of IMX_SC_R_AUDIO_CLK_1 + - description: power domain of IMX_SC_R_MCLK_OUT_0 + - description: power domain of IMX_SC_R_MCLK_OUT_1 + - description: power domain of IMX_SC_R_AUDIO_PLL_0 + - description: power domain of IMX_SC_R_AUDIO_PLL_1 + - description: power domain of IMX_SC_R_ASRC_0 + - description: power domain of IMX_SC_R_ASRC_1 + - description: power domain of IMX_SC_R_ESAI_0 + - description: power domain of IMX_SC_R_ESAI_1 + - description: power domain of IMX_SC_R_SAI_0 + - description: power domain of IMX_SC_R_SAI_1 + - description: power domain of IMX_SC_R_SAI_2 + - description: power domain of IMX_SC_R_SAI_3 + - description: power domain of IMX_SC_R_SAI_4 + - description: power domain of IMX_SC_R_SAI_5 + - description: power domain of IMX_SC_R_SAI_6 + - description: power domain of IMX_SC_R_SAI_7 + - description: power domain of IMX_SC_R_SPDIF_0 + - description: power domain of IMX_SC_R_SPDIF_1 + - description: power domain of IMX_SC_R_MQS_0 + + clocks: + minItems: 27 + maxItems: 27 + + clock-names: + items: + - const: aud_rec_clk0_lpcg_clk + - const: aud_rec_clk1_lpcg_clk + - const: aud_pll_div_clk0_lpcg_clk + - const: aud_pll_div_clk1_lpcg_clk + - const: mlb_clk + - const: hdmi_rx_mclk + - const: ext_aud_mclk0 + - const: ext_aud_mclk1 + - const: esai0_rx_clk + - const: esai0_rx_hf_clk + - const: esai0_tx_clk + - const: esai0_tx_hf_clk + - const: esai1_rx_clk + - const: esai1_rx_hf_clk + - const: esai1_tx_clk + - const: esai1_tx_hf_clk + - const: spdif0_rx + - const: spdif1_rx + - const: sai0_rx_bclk + - const: sai0_tx_bclk + - const: sai1_rx_bclk + - const: sai1_tx_bclk + - const: sai2_rx_bclk + - const: sai3_rx_bclk + - const: sai4_rx_bclk + - const: sai5_tx_bclk + - const: sai6_rx_bclk + + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8dxl-acm + then: + properties: + power-domains: + items: + - description: power domain of IMX_SC_R_AUDIO_CLK_0 + - description: power domain of IMX_SC_R_AUDIO_CLK_1 + - description: power domain of IMX_SC_R_MCLK_OUT_0 + - description: power domain of IMX_SC_R_MCLK_OUT_1 + - description: power domain of IMX_SC_R_AUDIO_PLL_0 + - description: power domain of IMX_SC_R_AUDIO_PLL_1 + - description: power domain of IMX_SC_R_ASRC_0 + - description: power domain of IMX_SC_R_SAI_0 + - description: power domain of IMX_SC_R_SAI_1 + - description: power domain of IMX_SC_R_SAI_2 + - description: power domain of IMX_SC_R_SAI_3 + - description: power domain of IMX_SC_R_SPDIF_0 + - description: power domain of IMX_SC_R_MQS_0 + + clocks: + minItems: 13 + maxItems: 13 + + clock-names: + items: + - const: aud_rec_clk0_lpcg_clk + - const: aud_rec_clk1_lpcg_clk + - const: aud_pll_div_clk0_lpcg_clk + - const: aud_pll_div_clk1_lpcg_clk + - const: ext_aud_mclk0 + - const: ext_aud_mclk1 + - const: spdif0_rx + - const: sai0_rx_bclk + - const: sai0_tx_bclk + - const: sai1_rx_bclk + - const: sai1_tx_bclk + - const: sai2_rx_bclk + - const: sai3_rx_bclk + +additionalProperties: false + +examples: + # Clock Control Module node: + - | + #include <dt-bindings/clock/imx8-lpcg.h> + #include <dt-bindings/firmware/imx/rsrc.h> + + clock-controller@59e00000 { + compatible = "fsl,imx8qxp-acm"; + reg = <0x59e00000 0x1d0000>; + #clock-cells = <1>; + power-domains = <&pd IMX_SC_R_AUDIO_CLK_0>, + <&pd IMX_SC_R_AUDIO_CLK_1>, + <&pd IMX_SC_R_MCLK_OUT_0>, + <&pd IMX_SC_R_MCLK_OUT_1>, + <&pd IMX_SC_R_AUDIO_PLL_0>, + <&pd IMX_SC_R_AUDIO_PLL_1>, + <&pd IMX_SC_R_ASRC_0>, + <&pd IMX_SC_R_ASRC_1>, + <&pd IMX_SC_R_ESAI_0>, + <&pd IMX_SC_R_SAI_0>, + <&pd IMX_SC_R_SAI_1>, + <&pd IMX_SC_R_SAI_2>, + <&pd IMX_SC_R_SAI_3>, + <&pd IMX_SC_R_SAI_4>, + <&pd IMX_SC_R_SAI_5>, + <&pd IMX_SC_R_SPDIF_0>, + <&pd IMX_SC_R_MQS_0>; + clocks = <&aud_rec0_lpcg IMX_LPCG_CLK_0>, + <&aud_rec1_lpcg IMX_LPCG_CLK_0>, + <&aud_pll_div0_lpcg IMX_LPCG_CLK_0>, + <&aud_pll_div1_lpcg IMX_LPCG_CLK_0>, + <&clk_ext_aud_mclk0>, + <&clk_ext_aud_mclk1>, + <&clk_esai0_rx_clk>, + <&clk_esai0_rx_hf_clk>, + <&clk_esai0_tx_clk>, + <&clk_esai0_tx_hf_clk>, + <&clk_spdif0_rx>, + <&clk_sai0_rx_bclk>, + <&clk_sai0_tx_bclk>, + <&clk_sai1_rx_bclk>, + <&clk_sai1_tx_bclk>, + <&clk_sai2_rx_bclk>, + <&clk_sai3_rx_bclk>, + <&clk_sai4_rx_bclk>; + clock-names = "aud_rec_clk0_lpcg_clk", + "aud_rec_clk1_lpcg_clk", + "aud_pll_div_clk0_lpcg_clk", + "aud_pll_div_clk1_lpcg_clk", + "ext_aud_mclk0", + "ext_aud_mclk1", + "esai0_rx_clk", + "esai0_rx_hf_clk", + "esai0_tx_clk", + "esai0_tx_hf_clk", + "spdif0_rx", + "sai0_rx_bclk", + "sai0_tx_bclk", + "sai1_rx_bclk", + "sai1_tx_bclk", + "sai2_rx_bclk", + "sai3_rx_bclk", + "sai4_rx_bclk"; + }; diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index ae9d84ef046b..d4b8e10b1970 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -32,11 +32,12 @@ obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o obj-$(CONFIG_CLK_IMX93) += clk-imx93.o -obj-$(CONFIG_MXC_CLK_SCU) += clk-imx-scu.o clk-imx-lpcg-scu.o +obj-$(CONFIG_MXC_CLK_SCU) += clk-imx-scu.o clk-imx-lpcg-scu.o clk-imx-acm.o clk-imx-scu-$(CONFIG_CLK_IMX8QXP) += clk-scu.o clk-imx8qxp.o \ clk-imx8qxp-rsrc.o clk-imx8qm-rsrc.o \ clk-imx8dxl-rsrc.o clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o +clk-imx-acm-$(CONFIG_CLK_IMX8QXP) = clk-imx8-acm.o obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 7a6e3ce97133..27a08c50ac1d 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -97,7 +97,7 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, int prediv_value; int div_value; int ret; - u32 val; + u32 orig, val; ret = imx8m_clk_composite_compute_dividers(rate, parent_rate, &prediv_value, &div_value); @@ -106,13 +106,15 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, spin_lock_irqsave(divider->lock, flags); - val = readl(divider->reg); - val &= ~((clk_div_mask(divider->width) << divider->shift) | - (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT)); + orig = readl(divider->reg); + val = orig & ~((clk_div_mask(divider->width) << divider->shift) | + (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT)); val |= (u32)(prediv_value - 1) << divider->shift; val |= (u32)(div_value - 1) << PCG_DIV_SHIFT; - writel(val, divider->reg); + + if (val != orig) + writel(val, divider->reg); spin_unlock_irqrestore(divider->lock, flags); diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index c54f9999da04..44462ab50e51 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -81,6 +81,7 @@ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { PLL_FRACN_GP(650000000U, 162, 50, 100, 0, 6), PLL_FRACN_GP(594000000U, 198, 0, 1, 0, 8), PLL_FRACN_GP(560000000U, 140, 0, 1, 0, 6), + PLL_FRACN_GP(519750000U, 173, 25, 100, 1, 8), PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8), PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), diff --git a/drivers/clk/imx/clk-gpr-mux.c b/drivers/clk/imx/clk-gpr-mux.c index 0b5a97698b47..0e14b61cba84 100644 --- a/drivers/clk/imx/clk-gpr-mux.c +++ b/drivers/clk/imx/clk-gpr-mux.c @@ -65,16 +65,10 @@ static int imx_clk_gpr_mux_set_parent(struct clk_hw *hw, u8 index) return regmap_update_bits(priv->regmap, priv->reg, priv->mask, val); } -static int imx_clk_gpr_mux_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return clk_mux_determine_rate_flags(hw, req, 0); -} - static const struct clk_ops imx_clk_gpr_mux_ops = { .get_parent = imx_clk_gpr_mux_get_parent, .set_parent = imx_clk_gpr_mux_set_parent, - .determine_rate = imx_clk_gpr_mux_determine_rate, + .determine_rate = __clk_mux_determine_rate, }; struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible, diff --git a/drivers/clk/imx/clk-imx25.c b/drivers/clk/imx/clk-imx25.c index cc013b343e62..c566be848c2d 100644 --- a/drivers/clk/imx/clk-imx25.c +++ b/drivers/clk/imx/clk-imx25.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <soc/imx/revision.h> #include "clk.h" @@ -73,7 +74,7 @@ enum mx25_clks { static struct clk *clk[clk_max]; -static int __init __mx25_clocks_init(void __iomem *ccm_base) +static void __init __mx25_clocks_init(void __iomem *ccm_base) { BUG_ON(!ccm_base); @@ -220,7 +221,7 @@ static int __init __mx25_clocks_init(void __iomem *ccm_base) imx_register_uart_clocks(); - return 0; + imx_print_silicon_rev("i.MX25", mx25_revision()); } static void __init mx25_clocks_init_dt(struct device_node *np) diff --git a/drivers/clk/imx/clk-imx8-acm.c b/drivers/clk/imx/clk-imx8-acm.c new file mode 100644 index 000000000000..1e82f72b75c6 --- /dev/null +++ b/drivers/clk/imx/clk-imx8-acm.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2023 NXP +// + +#include <dt-bindings/clock/imx8-clock.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include "clk.h" + +/** + * struct clk_imx_acm_pm_domains - structure for multi power domain + * @pd_dev: power domain device + * @pd_dev_link: power domain device link + * @num_domains: power domain nummber + */ +struct clk_imx_acm_pm_domains { + struct device **pd_dev; + struct device_link **pd_dev_link; + int num_domains; +}; + +/** + * struct clk_imx8_acm_sel - for clock mux + * @name: clock name + * @clkid: clock id + * @parents: clock parents + * @num_parents: clock parents number + * @reg: register offset + * @shift: bit shift in register + * @width: bits width + */ +struct clk_imx8_acm_sel { + const char *name; + int clkid; + const struct clk_parent_data *parents; /* For mux */ + int num_parents; + u32 reg; + u8 shift; + u8 width; +}; + +/** + * struct imx8_acm_soc_data - soc specific data + * @sels: pointer to struct clk_imx8_acm_sel + * @num_sels: numbers of items + */ +struct imx8_acm_soc_data { + struct clk_imx8_acm_sel *sels; + unsigned int num_sels; +}; + +/** + * struct imx8_acm_priv - private structure + * @dev_pm: multi power domain + * @soc_data: pointer to soc data + * @reg: base address of registers + * @regs: save registers for suspend + */ +struct imx8_acm_priv { + struct clk_imx_acm_pm_domains dev_pm; + const struct imx8_acm_soc_data *soc_data; + void __iomem *reg; + u32 regs[IMX_ADMA_ACM_CLK_END]; +}; + +static const struct clk_parent_data imx8qm_aud_clk_sels[] = { + { .fw_name = "aud_rec_clk0_lpcg_clk" }, + { .fw_name = "aud_rec_clk1_lpcg_clk" }, + { .fw_name = "mlb_clk" }, + { .fw_name = "hdmi_rx_mclk" }, + { .fw_name = "ext_aud_mclk0" }, + { .fw_name = "ext_aud_mclk1" }, + { .fw_name = "esai0_rx_clk" }, + { .fw_name = "esai0_rx_hf_clk" }, + { .fw_name = "esai0_tx_clk" }, + { .fw_name = "esai0_tx_hf_clk" }, + { .fw_name = "esai1_rx_clk" }, + { .fw_name = "esai1_rx_hf_clk" }, + { .fw_name = "esai1_tx_clk" }, + { .fw_name = "esai1_tx_hf_clk" }, + { .fw_name = "spdif0_rx" }, + { .fw_name = "spdif1_rx" }, + { .fw_name = "sai0_rx_bclk" }, + { .fw_name = "sai0_tx_bclk" }, + { .fw_name = "sai1_rx_bclk" }, + { .fw_name = "sai1_tx_bclk" }, + { .fw_name = "sai2_rx_bclk" }, + { .fw_name = "sai3_rx_bclk" }, + { .fw_name = "sai4_rx_bclk" }, +}; + +static const struct clk_parent_data imx8qm_mclk_out_sels[] = { + { .fw_name = "aud_rec_clk0_lpcg_clk" }, + { .fw_name = "aud_rec_clk1_lpcg_clk" }, + { .fw_name = "mlb_clk" }, + { .fw_name = "hdmi_rx_mclk" }, + { .fw_name = "spdif0_rx" }, + { .fw_name = "spdif1_rx" }, + { .fw_name = "sai4_rx_bclk" }, + { .fw_name = "sai6_rx_bclk" }, +}; + +static const struct clk_parent_data imx8qm_mclk_sels[] = { + { .fw_name = "aud_pll_div_clk0_lpcg_clk" }, + { .fw_name = "aud_pll_div_clk1_lpcg_clk" }, + { .fw_name = "acm_aud_clk0_sel" }, + { .fw_name = "acm_aud_clk1_sel" }, +}; + +static const struct clk_parent_data imx8qm_asrc_mux_clk_sels[] = { + { .fw_name = "sai4_rx_bclk" }, + { .fw_name = "sai5_tx_bclk" }, + { .index = -1 }, + { .fw_name = "mlb_clk" }, + +}; + +static struct clk_imx8_acm_sel imx8qm_sels[] = { + { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8qm_aud_clk_sels, ARRAY_SIZE(imx8qm_aud_clk_sels), 0x000000, 0, 5 }, + { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8qm_aud_clk_sels, ARRAY_SIZE(imx8qm_aud_clk_sels), 0x010000, 0, 5 }, + { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8qm_mclk_out_sels, ARRAY_SIZE(imx8qm_mclk_out_sels), 0x020000, 0, 3 }, + { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8qm_mclk_out_sels, ARRAY_SIZE(imx8qm_mclk_out_sels), 0x030000, 0, 3 }, + { "acm_asrc0_mclk_sel", IMX_ADMA_ACM_ASRC0_MUX_CLK_SEL, imx8qm_asrc_mux_clk_sels, ARRAY_SIZE(imx8qm_asrc_mux_clk_sels), 0x040000, 0, 2 }, + { "acm_esai0_mclk_sel", IMX_ADMA_ACM_ESAI0_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x060000, 0, 2 }, + { "acm_esai1_mclk_sel", IMX_ADMA_ACM_ESAI1_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x070000, 0, 2 }, + { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x0E0000, 0, 2 }, + { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x0F0000, 0, 2 }, + { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x100000, 0, 2 }, + { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x110000, 0, 2 }, + { "acm_sai4_mclk_sel", IMX_ADMA_ACM_SAI4_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x120000, 0, 2 }, + { "acm_sai5_mclk_sel", IMX_ADMA_ACM_SAI5_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x130000, 0, 2 }, + { "acm_sai6_mclk_sel", IMX_ADMA_ACM_SAI6_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x140000, 0, 2 }, + { "acm_sai7_mclk_sel", IMX_ADMA_ACM_SAI7_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x150000, 0, 2 }, + { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1A0000, 0, 2 }, + { "acm_spdif1_mclk_sel", IMX_ADMA_ACM_SPDIF1_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1B0000, 0, 2 }, + { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1C0000, 0, 2 }, +}; + +static const struct clk_parent_data imx8qxp_aud_clk_sels[] = { + { .fw_name = "aud_rec_clk0_lpcg_clk" }, + { .fw_name = "aud_rec_clk1_lpcg_clk" }, + { .fw_name = "ext_aud_mclk0" }, + { .fw_name = "ext_aud_mclk1" }, + { .fw_name = "esai0_rx_clk" }, + { .fw_name = "esai0_rx_hf_clk" }, + { .fw_name = "esai0_tx_clk" }, + { .fw_name = "esai0_tx_hf_clk" }, + { .fw_name = "spdif0_rx" }, + { .fw_name = "sai0_rx_bclk" }, + { .fw_name = "sai0_tx_bclk" }, + { .fw_name = "sai1_rx_bclk" }, + { .fw_name = "sai1_tx_bclk" }, + { .fw_name = "sai2_rx_bclk" }, + { .fw_name = "sai3_rx_bclk" }, +}; + +static const struct clk_parent_data imx8qxp_mclk_out_sels[] = { + { .fw_name = "aud_rec_clk0_lpcg_clk" }, + { .fw_name = "aud_rec_clk1_lpcg_clk" }, + { .index = -1 }, + { .index = -1 }, + { .fw_name = "spdif0_rx" }, + { .index = -1 }, + { .index = -1 }, + { .fw_name = "sai4_rx_bclk" }, +}; + +static const struct clk_parent_data imx8qxp_mclk_sels[] = { + { .fw_name = "aud_pll_div_clk0_lpcg_clk" }, + { .fw_name = "aud_pll_div_clk1_lpcg_clk" }, + { .fw_name = "acm_aud_clk0_sel" }, + { .fw_name = "acm_aud_clk1_sel" }, +}; + +static struct clk_imx8_acm_sel imx8qxp_sels[] = { + { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8qxp_aud_clk_sels, ARRAY_SIZE(imx8qxp_aud_clk_sels), 0x000000, 0, 5 }, + { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8qxp_aud_clk_sels, ARRAY_SIZE(imx8qxp_aud_clk_sels), 0x010000, 0, 5 }, + { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8qxp_mclk_out_sels, ARRAY_SIZE(imx8qxp_mclk_out_sels), 0x020000, 0, 3 }, + { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8qxp_mclk_out_sels, ARRAY_SIZE(imx8qxp_mclk_out_sels), 0x030000, 0, 3 }, + { "acm_esai0_mclk_sel", IMX_ADMA_ACM_ESAI0_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x060000, 0, 2 }, + { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x0E0000, 0, 2 }, + { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x0F0000, 0, 2 }, + { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x100000, 0, 2 }, + { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x110000, 0, 2 }, + { "acm_sai4_mclk_sel", IMX_ADMA_ACM_SAI4_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x140000, 0, 2 }, + { "acm_sai5_mclk_sel", IMX_ADMA_ACM_SAI5_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x150000, 0, 2 }, + { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x1A0000, 0, 2 }, + { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x1C0000, 0, 2 }, +}; + +static const struct clk_parent_data imx8dxl_aud_clk_sels[] = { + { .fw_name = "aud_rec_clk0_lpcg_clk" }, + { .fw_name = "aud_rec_clk1_lpcg_clk" }, + { .fw_name = "ext_aud_mclk0" }, + { .fw_name = "ext_aud_mclk1" }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, + { .fw_name = "spdif0_rx" }, + { .fw_name = "sai0_rx_bclk" }, + { .fw_name = "sai0_tx_bclk" }, + { .fw_name = "sai1_rx_bclk" }, + { .fw_name = "sai1_tx_bclk" }, + { .fw_name = "sai2_rx_bclk" }, + { .fw_name = "sai3_rx_bclk" }, +}; + +static const struct clk_parent_data imx8dxl_mclk_out_sels[] = { + { .fw_name = "aud_rec_clk0_lpcg_clk" }, + { .fw_name = "aud_rec_clk1_lpcg_clk" }, + { .index = -1 }, + { .index = -1 }, + { .fw_name = "spdif0_rx" }, + { .index = -1 }, + { .index = -1 }, + { .index = -1 }, +}; + +static const struct clk_parent_data imx8dxl_mclk_sels[] = { + { .fw_name = "aud_pll_div_clk0_lpcg_clk" }, + { .fw_name = "aud_pll_div_clk1_lpcg_clk" }, + { .fw_name = "acm_aud_clk0_sel" }, + { .fw_name = "acm_aud_clk1_sel" }, +}; + +static struct clk_imx8_acm_sel imx8dxl_sels[] = { + { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x000000, 0, 5 }, + { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x010000, 0, 5 }, + { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x020000, 0, 3 }, + { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x030000, 0, 3 }, + { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0E0000, 0, 2 }, + { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0F0000, 0, 2 }, + { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x100000, 0, 2 }, + { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x110000, 0, 2 }, + { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x1A0000, 0, 2 }, + { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x1C0000, 0, 2 }, +}; + +/** + * clk_imx_acm_attach_pm_domains: attach multi power domains + * @dev: device pointer + * @dev_pm: power domains for device + */ +static int clk_imx_acm_attach_pm_domains(struct device *dev, + struct clk_imx_acm_pm_domains *dev_pm) +{ + int ret; + int i; + + dev_pm->num_domains = of_count_phandle_with_args(dev->of_node, "power-domains", + "#power-domain-cells"); + if (dev_pm->num_domains <= 1) + return 0; + + dev_pm->pd_dev = devm_kmalloc_array(dev, dev_pm->num_domains, + sizeof(*dev_pm->pd_dev), + GFP_KERNEL); + if (!dev_pm->pd_dev) + return -ENOMEM; + + dev_pm->pd_dev_link = devm_kmalloc_array(dev, + dev_pm->num_domains, + sizeof(*dev_pm->pd_dev_link), + GFP_KERNEL); + if (!dev_pm->pd_dev_link) + return -ENOMEM; + + for (i = 0; i < dev_pm->num_domains; i++) { + dev_pm->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i); + if (IS_ERR(dev_pm->pd_dev[i])) + return PTR_ERR(dev_pm->pd_dev[i]); + + dev_pm->pd_dev_link[i] = device_link_add(dev, + dev_pm->pd_dev[i], + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (IS_ERR(dev_pm->pd_dev_link[i])) { + dev_pm_domain_detach(dev_pm->pd_dev[i], false); + ret = PTR_ERR(dev_pm->pd_dev_link[i]); + goto detach_pm; + } + } + return 0; + +detach_pm: + while (--i >= 0) { + device_link_del(dev_pm->pd_dev_link[i]); + dev_pm_domain_detach(dev_pm->pd_dev[i], false); + } + return ret; +} + +/** + * clk_imx_acm_detach_pm_domains: detach multi power domains + * @dev: deivice pointer + * @dev_pm: multi power domain for device + */ +static int clk_imx_acm_detach_pm_domains(struct device *dev, + struct clk_imx_acm_pm_domains *dev_pm) +{ + int i; + + if (dev_pm->num_domains <= 1) + return 0; + + for (i = 0; i < dev_pm->num_domains; i++) { + device_link_del(dev_pm->pd_dev_link[i]); + dev_pm_domain_detach(dev_pm->pd_dev[i], false); + } + + return 0; +} + +static int imx8_acm_clk_probe(struct platform_device *pdev) +{ + struct clk_hw_onecell_data *clk_hw_data; + struct device *dev = &pdev->dev; + struct clk_imx8_acm_sel *sels; + struct imx8_acm_priv *priv; + struct clk_hw **hws; + void __iomem *base; + int ret; + int i; + + base = devm_of_iomap(dev, dev->of_node, 0, NULL); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->reg = base; + priv->soc_data = of_device_get_match_data(dev); + platform_set_drvdata(pdev, priv); + + clk_hw_data = devm_kzalloc(&pdev->dev, struct_size(clk_hw_data, hws, IMX_ADMA_ACM_CLK_END), + GFP_KERNEL); + if (!clk_hw_data) + return -ENOMEM; + + clk_hw_data->num = IMX_ADMA_ACM_CLK_END; + hws = clk_hw_data->hws; + + ret = clk_imx_acm_attach_pm_domains(&pdev->dev, &priv->dev_pm); + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + sels = priv->soc_data->sels; + for (i = 0; i < priv->soc_data->num_sels; i++) { + hws[sels[i].clkid] = devm_clk_hw_register_mux_parent_data_table(dev, + sels[i].name, sels[i].parents, + sels[i].num_parents, 0, + base + sels[i].reg, + sels[i].shift, sels[i].width, + 0, NULL, NULL); + if (IS_ERR(hws[sels[i].clkid])) { + pm_runtime_disable(&pdev->dev); + goto err_clk_register; + } + } + + imx_check_clk_hws(hws, IMX_ADMA_ACM_CLK_END); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data); + if (ret < 0) { + dev_err(dev, "failed to register hws for ACM\n"); + pm_runtime_disable(&pdev->dev); + } + +err_clk_register: + + pm_runtime_put_sync(&pdev->dev); + + return ret; +} + +static int imx8_acm_clk_remove(struct platform_device *pdev) +{ + struct imx8_acm_priv *priv = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + clk_imx_acm_detach_pm_domains(&pdev->dev, &priv->dev_pm); + + return 0; +} + +stati |
