16cd95f7bSMarek Vasut // SPDX-License-Identifier: GPL-2.0-or-later 26cd95f7bSMarek Vasut /* 36cd95f7bSMarek Vasut * Driver for i.MX8M Plus Audio BLK_CTRL 46cd95f7bSMarek Vasut * 56cd95f7bSMarek Vasut * Copyright (C) 2022 Marek Vasut <marex@denx.de> 66cd95f7bSMarek Vasut */ 76cd95f7bSMarek Vasut 86cd95f7bSMarek Vasut #include <linux/clk-provider.h> 96cd95f7bSMarek Vasut #include <linux/device.h> 106cd95f7bSMarek Vasut #include <linux/mod_devicetable.h> 116cd95f7bSMarek Vasut #include <linux/module.h> 126cd95f7bSMarek Vasut #include <linux/of.h> 136cd95f7bSMarek Vasut #include <linux/platform_device.h> 146cd95f7bSMarek Vasut 156cd95f7bSMarek Vasut #include <dt-bindings/clock/imx8mp-clock.h> 166cd95f7bSMarek Vasut 176cd95f7bSMarek Vasut #include "clk.h" 186cd95f7bSMarek Vasut 196cd95f7bSMarek Vasut #define CLKEN0 0x000 206cd95f7bSMarek Vasut #define CLKEN1 0x004 21e0cbea9aSShengjiu Wang #define SAI1_MCLK_SEL 0x300 22e0cbea9aSShengjiu Wang #define SAI2_MCLK_SEL 0x304 23e0cbea9aSShengjiu Wang #define SAI3_MCLK_SEL 0x308 24e0cbea9aSShengjiu Wang #define SAI5_MCLK_SEL 0x30C 25e0cbea9aSShengjiu Wang #define SAI6_MCLK_SEL 0x310 26e0cbea9aSShengjiu Wang #define SAI7_MCLK_SEL 0x314 276cd95f7bSMarek Vasut #define PDM_SEL 0x318 286cd95f7bSMarek Vasut #define SAI_PLL_GNRL_CTL 0x400 296cd95f7bSMarek Vasut 306cd95f7bSMarek Vasut #define SAIn_MCLK1_PARENT(n) \ 316cd95f7bSMarek Vasut static const struct clk_parent_data \ 326cd95f7bSMarek Vasut clk_imx8mp_audiomix_sai##n##_mclk1_parents[] = { \ 336cd95f7bSMarek Vasut { \ 346cd95f7bSMarek Vasut .fw_name = "sai"__stringify(n), \ 356cd95f7bSMarek Vasut .name = "sai"__stringify(n) \ 366cd95f7bSMarek Vasut }, { \ 376cd95f7bSMarek Vasut .fw_name = "sai"__stringify(n)"_mclk", \ 386cd95f7bSMarek Vasut .name = "sai"__stringify(n)"_mclk" \ 396cd95f7bSMarek Vasut }, \ 406cd95f7bSMarek Vasut } 416cd95f7bSMarek Vasut 426cd95f7bSMarek Vasut SAIn_MCLK1_PARENT(1); 436cd95f7bSMarek Vasut SAIn_MCLK1_PARENT(2); 446cd95f7bSMarek Vasut SAIn_MCLK1_PARENT(3); 456cd95f7bSMarek Vasut SAIn_MCLK1_PARENT(5); 466cd95f7bSMarek Vasut SAIn_MCLK1_PARENT(6); 476cd95f7bSMarek Vasut SAIn_MCLK1_PARENT(7); 486cd95f7bSMarek Vasut 496cd95f7bSMarek Vasut static const struct clk_parent_data clk_imx8mp_audiomix_sai_mclk2_parents[] = { 506cd95f7bSMarek Vasut { .fw_name = "sai1", .name = "sai1" }, 516cd95f7bSMarek Vasut { .fw_name = "sai2", .name = "sai2" }, 526cd95f7bSMarek Vasut { .fw_name = "sai3", .name = "sai3" }, 536cd95f7bSMarek Vasut { .name = "dummy" }, 546cd95f7bSMarek Vasut { .fw_name = "sai5", .name = "sai5" }, 556cd95f7bSMarek Vasut { .fw_name = "sai6", .name = "sai6" }, 566cd95f7bSMarek Vasut { .fw_name = "sai7", .name = "sai7" }, 576cd95f7bSMarek Vasut { .fw_name = "sai1_mclk", .name = "sai1_mclk" }, 586cd95f7bSMarek Vasut { .fw_name = "sai2_mclk", .name = "sai2_mclk" }, 596cd95f7bSMarek Vasut { .fw_name = "sai3_mclk", .name = "sai3_mclk" }, 606cd95f7bSMarek Vasut { .name = "dummy" }, 616cd95f7bSMarek Vasut { .fw_name = "sai5_mclk", .name = "sai5_mclk" }, 626cd95f7bSMarek Vasut { .fw_name = "sai6_mclk", .name = "sai6_mclk" }, 636cd95f7bSMarek Vasut { .fw_name = "sai7_mclk", .name = "sai7_mclk" }, 646cd95f7bSMarek Vasut { .fw_name = "spdif_extclk", .name = "spdif_extclk" }, 656cd95f7bSMarek Vasut { .name = "dummy" }, 666cd95f7bSMarek Vasut }; 676cd95f7bSMarek Vasut 686cd95f7bSMarek Vasut static const struct clk_parent_data clk_imx8mp_audiomix_pdm_parents[] = { 696cd95f7bSMarek Vasut { .fw_name = "pdm", .name = "pdm" }, 706cd95f7bSMarek Vasut { .name = "sai_pll_out_div2" }, 716cd95f7bSMarek Vasut { .fw_name = "sai1_mclk", .name = "sai1_mclk" }, 726cd95f7bSMarek Vasut { .name = "dummy" }, 736cd95f7bSMarek Vasut }; 746cd95f7bSMarek Vasut 756cd95f7bSMarek Vasut 766cd95f7bSMarek Vasut static const struct clk_parent_data clk_imx8mp_audiomix_pll_parents[] = { 776cd95f7bSMarek Vasut { .fw_name = "osc_24m", .name = "osc_24m" }, 786cd95f7bSMarek Vasut { .name = "dummy" }, 796cd95f7bSMarek Vasut { .name = "dummy" }, 806cd95f7bSMarek Vasut { .name = "dummy" }, 816cd95f7bSMarek Vasut }; 826cd95f7bSMarek Vasut 836cd95f7bSMarek Vasut static const struct clk_parent_data clk_imx8mp_audiomix_pll_bypass_sels[] = { 846cd95f7bSMarek Vasut { .fw_name = "sai_pll", .name = "sai_pll" }, 856cd95f7bSMarek Vasut { .fw_name = "sai_pll_ref_sel", .name = "sai_pll_ref_sel" }, 866cd95f7bSMarek Vasut }; 876cd95f7bSMarek Vasut 886cd95f7bSMarek Vasut #define CLK_GATE(gname, cname) \ 896cd95f7bSMarek Vasut { \ 906cd95f7bSMarek Vasut gname"_cg", \ 916cd95f7bSMarek Vasut IMX8MP_CLK_AUDIOMIX_##cname, \ 926cd95f7bSMarek Vasut { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \ 936cd95f7bSMarek Vasut CLKEN0 + 4 * !!(IMX8MP_CLK_AUDIOMIX_##cname / 32), \ 946cd95f7bSMarek Vasut 1, IMX8MP_CLK_AUDIOMIX_##cname % 32 \ 956cd95f7bSMarek Vasut } 966cd95f7bSMarek Vasut 976cd95f7bSMarek Vasut #define CLK_SAIn(n) \ 986cd95f7bSMarek Vasut { \ 996cd95f7bSMarek Vasut "sai"__stringify(n)"_mclk1_sel", \ 1006cd95f7bSMarek Vasut IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1_SEL, {}, \ 1016cd95f7bSMarek Vasut clk_imx8mp_audiomix_sai##n##_mclk1_parents, \ 1026cd95f7bSMarek Vasut ARRAY_SIZE(clk_imx8mp_audiomix_sai##n##_mclk1_parents), \ 103e0cbea9aSShengjiu Wang SAI##n##_MCLK_SEL, 1, 0 \ 1046cd95f7bSMarek Vasut }, { \ 1056cd95f7bSMarek Vasut "sai"__stringify(n)"_mclk2_sel", \ 1066cd95f7bSMarek Vasut IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2_SEL, {}, \ 1076cd95f7bSMarek Vasut clk_imx8mp_audiomix_sai_mclk2_parents, \ 1086cd95f7bSMarek Vasut ARRAY_SIZE(clk_imx8mp_audiomix_sai_mclk2_parents), \ 109e0cbea9aSShengjiu Wang SAI##n##_MCLK_SEL, 4, 1 \ 1106cd95f7bSMarek Vasut }, { \ 1116cd95f7bSMarek Vasut "sai"__stringify(n)"_ipg_cg", \ 1126cd95f7bSMarek Vasut IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG, \ 1136cd95f7bSMarek Vasut { .fw_name = "ahb", .name = "ahb" }, NULL, 1, \ 1146cd95f7bSMarek Vasut CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_IPG \ 1156cd95f7bSMarek Vasut }, { \ 1166cd95f7bSMarek Vasut "sai"__stringify(n)"_mclk1_cg", \ 1176cd95f7bSMarek Vasut IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1, \ 1186cd95f7bSMarek Vasut { \ 1196cd95f7bSMarek Vasut .fw_name = "sai"__stringify(n)"_mclk1_sel", \ 1206cd95f7bSMarek Vasut .name = "sai"__stringify(n)"_mclk1_sel" \ 1216cd95f7bSMarek Vasut }, NULL, 1, \ 1226cd95f7bSMarek Vasut CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK1 \ 1236cd95f7bSMarek Vasut }, { \ 1246cd95f7bSMarek Vasut "sai"__stringify(n)"_mclk2_cg", \ 1256cd95f7bSMarek Vasut IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2, \ 1266cd95f7bSMarek Vasut { \ 1276cd95f7bSMarek Vasut .fw_name = "sai"__stringify(n)"_mclk2_sel", \ 1286cd95f7bSMarek Vasut .name = "sai"__stringify(n)"_mclk2_sel" \ 1296cd95f7bSMarek Vasut }, NULL, 1, \ 1306cd95f7bSMarek Vasut CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK2 \ 1316cd95f7bSMarek Vasut }, { \ 1326cd95f7bSMarek Vasut "sai"__stringify(n)"_mclk3_cg", \ 1336cd95f7bSMarek Vasut IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3, \ 1346cd95f7bSMarek Vasut { \ 1356cd95f7bSMarek Vasut .fw_name = "sai_pll_out_div2", \ 1366cd95f7bSMarek Vasut .name = "sai_pll_out_div2" \ 1376cd95f7bSMarek Vasut }, NULL, 1, \ 1386cd95f7bSMarek Vasut CLKEN0, 1, IMX8MP_CLK_AUDIOMIX_SAI##n##_MCLK3 \ 1396cd95f7bSMarek Vasut } 1406cd95f7bSMarek Vasut 1416cd95f7bSMarek Vasut #define CLK_PDM \ 1426cd95f7bSMarek Vasut { \ 1436cd95f7bSMarek Vasut "pdm_sel", IMX8MP_CLK_AUDIOMIX_PDM_SEL, {}, \ 1446cd95f7bSMarek Vasut clk_imx8mp_audiomix_pdm_parents, \ 1456cd95f7bSMarek Vasut ARRAY_SIZE(clk_imx8mp_audiomix_pdm_parents), \ 1466cd95f7bSMarek Vasut PDM_SEL, 2, 0 \ 1476cd95f7bSMarek Vasut } 1486cd95f7bSMarek Vasut 149*bd553be1SShengjiu Wang #define CLK_GATE_PARENT(gname, cname, pname) \ 150*bd553be1SShengjiu Wang { \ 151*bd553be1SShengjiu Wang gname"_cg", \ 152*bd553be1SShengjiu Wang IMX8MP_CLK_AUDIOMIX_##cname, \ 153*bd553be1SShengjiu Wang { .fw_name = pname, .name = pname }, NULL, 1, \ 154*bd553be1SShengjiu Wang CLKEN0 + 4 * !!(IMX8MP_CLK_AUDIOMIX_##cname / 32), \ 155*bd553be1SShengjiu Wang 1, IMX8MP_CLK_AUDIOMIX_##cname % 32 \ 156*bd553be1SShengjiu Wang } 157*bd553be1SShengjiu Wang 1586cd95f7bSMarek Vasut struct clk_imx8mp_audiomix_sel { 1596cd95f7bSMarek Vasut const char *name; 1606cd95f7bSMarek Vasut int clkid; 1616cd95f7bSMarek Vasut const struct clk_parent_data parent; /* For gate */ 1626cd95f7bSMarek Vasut const struct clk_parent_data *parents; /* For mux */ 1636cd95f7bSMarek Vasut int num_parents; 1646cd95f7bSMarek Vasut u16 reg; 1656cd95f7bSMarek Vasut u8 width; 1666cd95f7bSMarek Vasut u8 shift; 1676cd95f7bSMarek Vasut }; 1686cd95f7bSMarek Vasut 1696cd95f7bSMarek Vasut static struct clk_imx8mp_audiomix_sel sels[] = { 1706cd95f7bSMarek Vasut CLK_GATE("asrc", ASRC_IPG), 1716cd95f7bSMarek Vasut CLK_GATE("pdm", PDM_IPG), 1726cd95f7bSMarek Vasut CLK_GATE("earc", EARC_IPG), 1736cd95f7bSMarek Vasut CLK_GATE("ocrama", OCRAMA_IPG), 1746cd95f7bSMarek Vasut CLK_GATE("aud2htx", AUD2HTX_IPG), 175*bd553be1SShengjiu Wang CLK_GATE_PARENT("earc_phy", EARC_PHY, "sai_pll_out_div2"), 1766cd95f7bSMarek Vasut CLK_GATE("sdma2", SDMA2_ROOT), 1776cd95f7bSMarek Vasut CLK_GATE("sdma3", SDMA3_ROOT), 1786cd95f7bSMarek Vasut CLK_GATE("spba2", SPBA2_ROOT), 1796cd95f7bSMarek Vasut CLK_GATE("dsp", DSP_ROOT), 1806cd95f7bSMarek Vasut CLK_GATE("dspdbg", DSPDBG_ROOT), 1816cd95f7bSMarek Vasut CLK_GATE("edma", EDMA_ROOT), 182*bd553be1SShengjiu Wang CLK_GATE_PARENT("audpll", AUDPLL_ROOT, "osc_24m"), 1836cd95f7bSMarek Vasut CLK_GATE("mu2", MU2_ROOT), 1846cd95f7bSMarek Vasut CLK_GATE("mu3", MU3_ROOT), 1856cd95f7bSMarek Vasut CLK_PDM, 1866cd95f7bSMarek Vasut CLK_SAIn(1), 1876cd95f7bSMarek Vasut CLK_SAIn(2), 1886cd95f7bSMarek Vasut CLK_SAIn(3), 1896cd95f7bSMarek Vasut CLK_SAIn(5), 1906cd95f7bSMarek Vasut CLK_SAIn(6), 1916cd95f7bSMarek Vasut CLK_SAIn(7) 1926cd95f7bSMarek Vasut }; 1936cd95f7bSMarek Vasut 1946cd95f7bSMarek Vasut static int clk_imx8mp_audiomix_probe(struct platform_device *pdev) 1956cd95f7bSMarek Vasut { 1966cd95f7bSMarek Vasut struct clk_hw_onecell_data *priv; 1976cd95f7bSMarek Vasut struct device *dev = &pdev->dev; 1986cd95f7bSMarek Vasut void __iomem *base; 1996cd95f7bSMarek Vasut struct clk_hw *hw; 2006cd95f7bSMarek Vasut int i; 2016cd95f7bSMarek Vasut 2026cd95f7bSMarek Vasut priv = devm_kzalloc(dev, 2036cd95f7bSMarek Vasut struct_size(priv, hws, IMX8MP_CLK_AUDIOMIX_END), 2046cd95f7bSMarek Vasut GFP_KERNEL); 2056cd95f7bSMarek Vasut if (!priv) 2066cd95f7bSMarek Vasut return -ENOMEM; 2076cd95f7bSMarek Vasut 2086cd95f7bSMarek Vasut priv->num = IMX8MP_CLK_AUDIOMIX_END; 2096cd95f7bSMarek Vasut 2106cd95f7bSMarek Vasut base = devm_platform_ioremap_resource(pdev, 0); 2116cd95f7bSMarek Vasut if (IS_ERR(base)) 2126cd95f7bSMarek Vasut return PTR_ERR(base); 2136cd95f7bSMarek Vasut 2146cd95f7bSMarek Vasut for (i = 0; i < ARRAY_SIZE(sels); i++) { 2156cd95f7bSMarek Vasut if (sels[i].num_parents == 1) { 2166cd95f7bSMarek Vasut hw = devm_clk_hw_register_gate_parent_data(dev, 2176cd95f7bSMarek Vasut sels[i].name, &sels[i].parent, 0, 2186cd95f7bSMarek Vasut base + sels[i].reg, sels[i].shift, 0, NULL); 2196cd95f7bSMarek Vasut } else { 2206cd95f7bSMarek Vasut hw = devm_clk_hw_register_mux_parent_data_table(dev, 2216cd95f7bSMarek Vasut sels[i].name, sels[i].parents, 2226cd95f7bSMarek Vasut sels[i].num_parents, 0, 2236cd95f7bSMarek Vasut base + sels[i].reg, 2246cd95f7bSMarek Vasut sels[i].shift, sels[i].width, 2256cd95f7bSMarek Vasut 0, NULL, NULL); 2266cd95f7bSMarek Vasut } 2276cd95f7bSMarek Vasut 2286cd95f7bSMarek Vasut if (IS_ERR(hw)) 2296cd95f7bSMarek Vasut return PTR_ERR(hw); 2306cd95f7bSMarek Vasut 2316cd95f7bSMarek Vasut priv->hws[sels[i].clkid] = hw; 2326cd95f7bSMarek Vasut } 2336cd95f7bSMarek Vasut 2346cd95f7bSMarek Vasut /* SAI PLL */ 2356cd95f7bSMarek Vasut hw = devm_clk_hw_register_mux_parent_data_table(dev, 2366cd95f7bSMarek Vasut "sai_pll_ref_sel", clk_imx8mp_audiomix_pll_parents, 2376cd95f7bSMarek Vasut ARRAY_SIZE(clk_imx8mp_audiomix_pll_parents), 2386cd95f7bSMarek Vasut CLK_SET_RATE_NO_REPARENT, base + SAI_PLL_GNRL_CTL, 2396cd95f7bSMarek Vasut 0, 2, 0, NULL, NULL); 2406cd95f7bSMarek Vasut priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw; 2416cd95f7bSMarek Vasut 2426cd95f7bSMarek Vasut hw = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel", 2436cd95f7bSMarek Vasut base + 0x400, &imx_1443x_pll); 2446cd95f7bSMarek Vasut if (IS_ERR(hw)) 2456cd95f7bSMarek Vasut return PTR_ERR(hw); 2466cd95f7bSMarek Vasut priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw; 2476cd95f7bSMarek Vasut 2486cd95f7bSMarek Vasut hw = devm_clk_hw_register_mux_parent_data_table(dev, 2496cd95f7bSMarek Vasut "sai_pll_bypass", clk_imx8mp_audiomix_pll_bypass_sels, 2506cd95f7bSMarek Vasut ARRAY_SIZE(clk_imx8mp_audiomix_pll_bypass_sels), 2516cd95f7bSMarek Vasut CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 2526cd95f7bSMarek Vasut base + SAI_PLL_GNRL_CTL, 16, 1, 0, NULL, NULL); 2536cd95f7bSMarek Vasut if (IS_ERR(hw)) 2546cd95f7bSMarek Vasut return PTR_ERR(hw); 2556cd95f7bSMarek Vasut priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw; 2566cd95f7bSMarek Vasut 2576cd95f7bSMarek Vasut hw = devm_clk_hw_register_gate(dev, "sai_pll_out", "sai_pll_bypass", 2586cd95f7bSMarek Vasut 0, base + SAI_PLL_GNRL_CTL, 13, 2596cd95f7bSMarek Vasut 0, NULL); 2606cd95f7bSMarek Vasut if (IS_ERR(hw)) 2616cd95f7bSMarek Vasut return PTR_ERR(hw); 2626cd95f7bSMarek Vasut priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw; 2636cd95f7bSMarek Vasut 2646cd95f7bSMarek Vasut hw = devm_clk_hw_register_fixed_factor(dev, "sai_pll_out_div2", 2656cd95f7bSMarek Vasut "sai_pll_out", 0, 1, 2); 2666cd95f7bSMarek Vasut if (IS_ERR(hw)) 2676cd95f7bSMarek Vasut return PTR_ERR(hw); 2686cd95f7bSMarek Vasut 2696cd95f7bSMarek Vasut return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, 2706cd95f7bSMarek Vasut priv); 2716cd95f7bSMarek Vasut } 2726cd95f7bSMarek Vasut 2736cd95f7bSMarek Vasut static const struct of_device_id clk_imx8mp_audiomix_of_match[] = { 2746cd95f7bSMarek Vasut { .compatible = "fsl,imx8mp-audio-blk-ctrl" }, 2756cd95f7bSMarek Vasut { /* sentinel */ } 2766cd95f7bSMarek Vasut }; 2776cd95f7bSMarek Vasut MODULE_DEVICE_TABLE(of, clk_imx8mp_audiomix_of_match); 2786cd95f7bSMarek Vasut 2796cd95f7bSMarek Vasut static struct platform_driver clk_imx8mp_audiomix_driver = { 2806cd95f7bSMarek Vasut .probe = clk_imx8mp_audiomix_probe, 2816cd95f7bSMarek Vasut .driver = { 2826cd95f7bSMarek Vasut .name = "imx8mp-audio-blk-ctrl", 2836cd95f7bSMarek Vasut .of_match_table = clk_imx8mp_audiomix_of_match, 2846cd95f7bSMarek Vasut }, 2856cd95f7bSMarek Vasut }; 2866cd95f7bSMarek Vasut 2876cd95f7bSMarek Vasut module_platform_driver(clk_imx8mp_audiomix_driver); 2886cd95f7bSMarek Vasut 2896cd95f7bSMarek Vasut MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 2906cd95f7bSMarek Vasut MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller driver"); 2916cd95f7bSMarek Vasut MODULE_LICENSE("GPL"); 292