11e3121bfSAisheng Dong // SPDX-License-Identifier: GPL-2.0+ 21e3121bfSAisheng Dong /* 31e3121bfSAisheng Dong * Copyright 2018 NXP 41e3121bfSAisheng Dong * Dong Aisheng <aisheng.dong@nxp.com> 51e3121bfSAisheng Dong */ 61e3121bfSAisheng Dong 71e3121bfSAisheng Dong #include <linux/clk-provider.h> 81e3121bfSAisheng Dong #include <linux/err.h> 91e3121bfSAisheng Dong #include <linux/io.h> 101e3121bfSAisheng Dong #include <linux/module.h> 111e3121bfSAisheng Dong #include <linux/of.h> 12d5f1e6a2SDong Aisheng #include <linux/of_address.h> 131e3121bfSAisheng Dong #include <linux/of_device.h> 141e3121bfSAisheng Dong #include <linux/platform_device.h> 1518cdbad4SDong Aisheng #include <linux/pm_runtime.h> 161e3121bfSAisheng Dong #include <linux/slab.h> 171e3121bfSAisheng Dong 181e3121bfSAisheng Dong #include "clk-scu.h" 191e3121bfSAisheng Dong #include "clk-imx8qxp-lpcg.h" 201e3121bfSAisheng Dong 2108972760SAisheng Dong #include <dt-bindings/clock/imx8-clock.h> 221e3121bfSAisheng Dong 231e3121bfSAisheng Dong /* 241e3121bfSAisheng Dong * struct imx8qxp_lpcg_data - Description of one LPCG clock 251e3121bfSAisheng Dong * @id: clock ID 261e3121bfSAisheng Dong * @name: clock name 271e3121bfSAisheng Dong * @parent: parent clock name 281e3121bfSAisheng Dong * @flags: common clock flags 291e3121bfSAisheng Dong * @offset: offset of this LPCG clock 301e3121bfSAisheng Dong * @bit_idx: bit index of this LPCG clock 311e3121bfSAisheng Dong * @hw_gate: whether supports HW autogate 321e3121bfSAisheng Dong * 331e3121bfSAisheng Dong * This structure describes one LPCG clock 341e3121bfSAisheng Dong */ 351e3121bfSAisheng Dong struct imx8qxp_lpcg_data { 361e3121bfSAisheng Dong int id; 371e3121bfSAisheng Dong char *name; 381e3121bfSAisheng Dong char *parent; 391e3121bfSAisheng Dong unsigned long flags; 401e3121bfSAisheng Dong u32 offset; 411e3121bfSAisheng Dong u8 bit_idx; 421e3121bfSAisheng Dong bool hw_gate; 431e3121bfSAisheng Dong }; 441e3121bfSAisheng Dong 451e3121bfSAisheng Dong /* 461e3121bfSAisheng Dong * struct imx8qxp_ss_lpcg - Description of one subsystem LPCG clocks 471e3121bfSAisheng Dong * @lpcg: LPCG clocks array of one subsystem 481e3121bfSAisheng Dong * @num_lpcg: the number of LPCG clocks 491e3121bfSAisheng Dong * @num_max: the maximum number of LPCG clocks 501e3121bfSAisheng Dong * 511e3121bfSAisheng Dong * This structure describes each subsystem LPCG clocks information 521e3121bfSAisheng Dong * which then will be used to create respective LPCGs clocks 531e3121bfSAisheng Dong */ 541e3121bfSAisheng Dong struct imx8qxp_ss_lpcg { 551e3121bfSAisheng Dong const struct imx8qxp_lpcg_data *lpcg; 561e3121bfSAisheng Dong u8 num_lpcg; 571e3121bfSAisheng Dong u8 num_max; 581e3121bfSAisheng Dong }; 591e3121bfSAisheng Dong 601e3121bfSAisheng Dong static const struct imx8qxp_lpcg_data imx8qxp_lpcg_adma[] = { 6108972760SAisheng Dong { IMX_ADMA_LPCG_UART0_IPG_CLK, "uart0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_0_LPCG, 16, 0, }, 6208972760SAisheng Dong { IMX_ADMA_LPCG_UART0_BAUD_CLK, "uart0_lpcg_baud_clk", "uart0_clk", 0, ADMA_LPUART_0_LPCG, 0, 0, }, 6308972760SAisheng Dong { IMX_ADMA_LPCG_UART1_IPG_CLK, "uart1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_1_LPCG, 16, 0, }, 6408972760SAisheng Dong { IMX_ADMA_LPCG_UART1_BAUD_CLK, "uart1_lpcg_baud_clk", "uart1_clk", 0, ADMA_LPUART_1_LPCG, 0, 0, }, 6508972760SAisheng Dong { IMX_ADMA_LPCG_UART2_IPG_CLK, "uart2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_2_LPCG, 16, 0, }, 6608972760SAisheng Dong { IMX_ADMA_LPCG_UART2_BAUD_CLK, "uart2_lpcg_baud_clk", "uart2_clk", 0, ADMA_LPUART_2_LPCG, 0, 0, }, 6708972760SAisheng Dong { IMX_ADMA_LPCG_UART3_IPG_CLK, "uart3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_3_LPCG, 16, 0, }, 6808972760SAisheng Dong { IMX_ADMA_LPCG_UART3_BAUD_CLK, "uart3_lpcg_baud_clk", "uart3_clk", 0, ADMA_LPUART_3_LPCG, 0, 0, }, 6908972760SAisheng Dong { IMX_ADMA_LPCG_I2C0_IPG_CLK, "i2c0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_0_LPCG, 16, 0, }, 7008972760SAisheng Dong { IMX_ADMA_LPCG_I2C0_CLK, "i2c0_lpcg_clk", "i2c0_clk", 0, ADMA_LPI2C_0_LPCG, 0, 0, }, 7108972760SAisheng Dong { IMX_ADMA_LPCG_I2C1_IPG_CLK, "i2c1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_1_LPCG, 16, 0, }, 7208972760SAisheng Dong { IMX_ADMA_LPCG_I2C1_CLK, "i2c1_lpcg_clk", "i2c1_clk", 0, ADMA_LPI2C_1_LPCG, 0, 0, }, 7308972760SAisheng Dong { IMX_ADMA_LPCG_I2C2_IPG_CLK, "i2c2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_2_LPCG, 16, 0, }, 7408972760SAisheng Dong { IMX_ADMA_LPCG_I2C2_CLK, "i2c2_lpcg_clk", "i2c2_clk", 0, ADMA_LPI2C_2_LPCG, 0, 0, }, 7508972760SAisheng Dong { IMX_ADMA_LPCG_I2C3_IPG_CLK, "i2c3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_3_LPCG, 16, 0, }, 7608972760SAisheng Dong { IMX_ADMA_LPCG_I2C3_CLK, "i2c3_lpcg_clk", "i2c3_clk", 0, ADMA_LPI2C_3_LPCG, 0, 0, }, 776ad7cb71SDaniel Baluta 786ad7cb71SDaniel Baluta { IMX_ADMA_LPCG_DSP_CORE_CLK, "dsp_lpcg_core_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 28, 0, }, 796ad7cb71SDaniel Baluta { IMX_ADMA_LPCG_DSP_IPG_CLK, "dsp_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 20, 0, }, 806ad7cb71SDaniel Baluta { IMX_ADMA_LPCG_DSP_ADB_CLK, "dsp_lpcg_adb_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 16, 0, }, 816ad7cb71SDaniel Baluta { IMX_ADMA_LPCG_OCRAM_IPG_CLK, "ocram_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_OCRAM_LPCG, 16, 0, }, 821e3121bfSAisheng Dong }; 831e3121bfSAisheng Dong 841e3121bfSAisheng Dong static const struct imx8qxp_ss_lpcg imx8qxp_ss_adma = { 851e3121bfSAisheng Dong .lpcg = imx8qxp_lpcg_adma, 861e3121bfSAisheng Dong .num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_adma), 8708972760SAisheng Dong .num_max = IMX_ADMA_LPCG_CLK_END, 881e3121bfSAisheng Dong }; 891e3121bfSAisheng Dong 901e3121bfSAisheng Dong static const struct imx8qxp_lpcg_data imx8qxp_lpcg_conn[] = { 9108972760SAisheng Dong { IMX_CONN_LPCG_SDHC0_PER_CLK, "sdhc0_lpcg_per_clk", "sdhc0_clk", 0, CONN_USDHC_0_LPCG, 0, 0, }, 9208972760SAisheng Dong { IMX_CONN_LPCG_SDHC0_IPG_CLK, "sdhc0_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_0_LPCG, 16, 0, }, 9308972760SAisheng Dong { IMX_CONN_LPCG_SDHC0_HCLK, "sdhc0_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_0_LPCG, 20, 0, }, 9408972760SAisheng Dong { IMX_CONN_LPCG_SDHC1_PER_CLK, "sdhc1_lpcg_per_clk", "sdhc1_clk", 0, CONN_USDHC_1_LPCG, 0, 0, }, 9508972760SAisheng Dong { IMX_CONN_LPCG_SDHC1_IPG_CLK, "sdhc1_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_1_LPCG, 16, 0, }, 9608972760SAisheng Dong { IMX_CONN_LPCG_SDHC1_HCLK, "sdhc1_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_1_LPCG, 20, 0, }, 9708972760SAisheng Dong { IMX_CONN_LPCG_SDHC2_PER_CLK, "sdhc2_lpcg_per_clk", "sdhc2_clk", 0, CONN_USDHC_2_LPCG, 0, 0, }, 9808972760SAisheng Dong { IMX_CONN_LPCG_SDHC2_IPG_CLK, "sdhc2_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_2_LPCG, 16, 0, }, 9908972760SAisheng Dong { IMX_CONN_LPCG_SDHC2_HCLK, "sdhc2_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_2_LPCG, 20, 0, }, 10008972760SAisheng Dong { IMX_CONN_LPCG_ENET0_ROOT_CLK, "enet0_ipg_root_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 0, 0, }, 10108972760SAisheng Dong { IMX_CONN_LPCG_ENET0_TX_CLK, "enet0_tx_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 4, 0, }, 10208972760SAisheng Dong { IMX_CONN_LPCG_ENET0_AHB_CLK, "enet0_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_0_LPCG, 8, 0, }, 10308972760SAisheng Dong { IMX_CONN_LPCG_ENET0_IPG_S_CLK, "enet0_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_0_LPCG, 20, 0, }, 10408972760SAisheng Dong { IMX_CONN_LPCG_ENET0_IPG_CLK, "enet0_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_0_LPCG, 16, 0, }, 10508972760SAisheng Dong { IMX_CONN_LPCG_ENET1_ROOT_CLK, "enet1_ipg_root_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 0, 0, }, 10608972760SAisheng Dong { IMX_CONN_LPCG_ENET1_TX_CLK, "enet1_tx_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 4, 0, }, 10708972760SAisheng Dong { IMX_CONN_LPCG_ENET1_AHB_CLK, "enet1_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_1_LPCG, 8, 0, }, 10808972760SAisheng Dong { IMX_CONN_LPCG_ENET1_IPG_S_CLK, "enet1_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_1_LPCG, 20, 0, }, 10908972760SAisheng Dong { IMX_CONN_LPCG_ENET1_IPG_CLK, "enet1_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_1_LPCG, 16, 0, }, 1101e3121bfSAisheng Dong }; 1111e3121bfSAisheng Dong 1121e3121bfSAisheng Dong static const struct imx8qxp_ss_lpcg imx8qxp_ss_conn = { 1131e3121bfSAisheng Dong .lpcg = imx8qxp_lpcg_conn, 1141e3121bfSAisheng Dong .num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_conn), 11508972760SAisheng Dong .num_max = IMX_CONN_LPCG_CLK_END, 1161e3121bfSAisheng Dong }; 1171e3121bfSAisheng Dong 1181e3121bfSAisheng Dong static const struct imx8qxp_lpcg_data imx8qxp_lpcg_lsio[] = { 11908972760SAisheng Dong { IMX_LSIO_LPCG_PWM0_IPG_CLK, "pwm0_lpcg_ipg_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 0, 0, }, 12008972760SAisheng Dong { IMX_LSIO_LPCG_PWM0_IPG_HF_CLK, "pwm0_lpcg_ipg_hf_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 4, 0, }, 12108972760SAisheng Dong { IMX_LSIO_LPCG_PWM0_IPG_S_CLK, "pwm0_lpcg_ipg_s_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 16, 0, }, 12208972760SAisheng Dong { IMX_LSIO_LPCG_PWM0_IPG_SLV_CLK, "pwm0_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_0_LPCG, 20, 0, }, 12308972760SAisheng Dong { IMX_LSIO_LPCG_PWM0_IPG_MSTR_CLK, "pwm0_lpcg_ipg_mstr_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 24, 0, }, 12408972760SAisheng Dong { IMX_LSIO_LPCG_PWM1_IPG_CLK, "pwm1_lpcg_ipg_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 0, 0, }, 12508972760SAisheng Dong { IMX_LSIO_LPCG_PWM1_IPG_HF_CLK, "pwm1_lpcg_ipg_hf_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 4, 0, }, 12608972760SAisheng Dong { IMX_LSIO_LPCG_PWM1_IPG_S_CLK, "pwm1_lpcg_ipg_s_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 16, 0, }, 12708972760SAisheng Dong { IMX_LSIO_LPCG_PWM1_IPG_SLV_CLK, "pwm1_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_1_LPCG, 20, 0, }, 12808972760SAisheng Dong { IMX_LSIO_LPCG_PWM1_IPG_MSTR_CLK, "pwm1_lpcg_ipg_mstr_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 24, 0, }, 12908972760SAisheng Dong { IMX_LSIO_LPCG_PWM2_IPG_CLK, "pwm2_lpcg_ipg_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 0, 0, }, 13008972760SAisheng Dong { IMX_LSIO_LPCG_PWM2_IPG_HF_CLK, "pwm2_lpcg_ipg_hf_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 4, 0, }, 13108972760SAisheng Dong { IMX_LSIO_LPCG_PWM2_IPG_S_CLK, "pwm2_lpcg_ipg_s_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 16, 0, }, 13208972760SAisheng Dong { IMX_LSIO_LPCG_PWM2_IPG_SLV_CLK, "pwm2_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_2_LPCG, 20, 0, }, 13308972760SAisheng Dong { IMX_LSIO_LPCG_PWM2_IPG_MSTR_CLK, "pwm2_lpcg_ipg_mstr_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 24, 0, }, 13408972760SAisheng Dong { IMX_LSIO_LPCG_PWM3_IPG_CLK, "pwm3_lpcg_ipg_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 0, 0, }, 13508972760SAisheng Dong { IMX_LSIO_LPCG_PWM3_IPG_HF_CLK, "pwm3_lpcg_ipg_hf_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 4, 0, }, 13608972760SAisheng Dong { IMX_LSIO_LPCG_PWM3_IPG_S_CLK, "pwm3_lpcg_ipg_s_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 16, 0, }, 13708972760SAisheng Dong { IMX_LSIO_LPCG_PWM3_IPG_SLV_CLK, "pwm3_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_3_LPCG, 20, 0, }, 13808972760SAisheng Dong { IMX_LSIO_LPCG_PWM3_IPG_MSTR_CLK, "pwm3_lpcg_ipg_mstr_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 24, 0, }, 13908972760SAisheng Dong { IMX_LSIO_LPCG_PWM4_IPG_CLK, "pwm4_lpcg_ipg_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 0, 0, }, 14008972760SAisheng Dong { IMX_LSIO_LPCG_PWM4_IPG_HF_CLK, "pwm4_lpcg_ipg_hf_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 4, 0, }, 14108972760SAisheng Dong { IMX_LSIO_LPCG_PWM4_IPG_S_CLK, "pwm4_lpcg_ipg_s_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 16, 0, }, 14208972760SAisheng Dong { IMX_LSIO_LPCG_PWM4_IPG_SLV_CLK, "pwm4_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_4_LPCG, 20, 0, }, 14308972760SAisheng Dong { IMX_LSIO_LPCG_PWM4_IPG_MSTR_CLK, "pwm4_lpcg_ipg_mstr_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 24, 0, }, 14408972760SAisheng Dong { IMX_LSIO_LPCG_PWM5_IPG_CLK, "pwm5_lpcg_ipg_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 0, 0, }, 14508972760SAisheng Dong { IMX_LSIO_LPCG_PWM5_IPG_HF_CLK, "pwm5_lpcg_ipg_hf_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 4, 0, }, 14608972760SAisheng Dong { IMX_LSIO_LPCG_PWM5_IPG_S_CLK, "pwm5_lpcg_ipg_s_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 16, 0, }, 14708972760SAisheng Dong { IMX_LSIO_LPCG_PWM5_IPG_SLV_CLK, "pwm5_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_5_LPCG, 20, 0, }, 14808972760SAisheng Dong { IMX_LSIO_LPCG_PWM5_IPG_MSTR_CLK, "pwm5_lpcg_ipg_mstr_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 24, 0, }, 14908972760SAisheng Dong { IMX_LSIO_LPCG_PWM6_IPG_CLK, "pwm6_lpcg_ipg_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 0, 0, }, 15008972760SAisheng Dong { IMX_LSIO_LPCG_PWM6_IPG_HF_CLK, "pwm6_lpcg_ipg_hf_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 4, 0, }, 15108972760SAisheng Dong { IMX_LSIO_LPCG_PWM6_IPG_S_CLK, "pwm6_lpcg_ipg_s_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 16, 0, }, 15208972760SAisheng Dong { IMX_LSIO_LPCG_PWM6_IPG_SLV_CLK, "pwm6_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_6_LPCG, 20, 0, }, 15308972760SAisheng Dong { IMX_LSIO_LPCG_PWM6_IPG_MSTR_CLK, "pwm6_lpcg_ipg_mstr_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 24, 0, }, 1541e3121bfSAisheng Dong }; 1551e3121bfSAisheng Dong 1561e3121bfSAisheng Dong static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = { 1571e3121bfSAisheng Dong .lpcg = imx8qxp_lpcg_lsio, 1581e3121bfSAisheng Dong .num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_lsio), 15908972760SAisheng Dong .num_max = IMX_LSIO_LPCG_CLK_END, 1601e3121bfSAisheng Dong }; 1611e3121bfSAisheng Dong 162d5f1e6a2SDong Aisheng #define IMX_LPCG_MAX_CLKS 8 163d5f1e6a2SDong Aisheng 164d5f1e6a2SDong Aisheng static struct clk_hw *imx_lpcg_of_clk_src_get(struct of_phandle_args *clkspec, 165d5f1e6a2SDong Aisheng void *data) 166d5f1e6a2SDong Aisheng { 167d5f1e6a2SDong Aisheng struct clk_hw_onecell_data *hw_data = data; 168d5f1e6a2SDong Aisheng unsigned int idx = clkspec->args[0] / 4; 169d5f1e6a2SDong Aisheng 170d5f1e6a2SDong Aisheng if (idx >= hw_data->num) { 171d5f1e6a2SDong Aisheng pr_err("%s: invalid index %u\n", __func__, idx); 172d5f1e6a2SDong Aisheng return ERR_PTR(-EINVAL); 173d5f1e6a2SDong Aisheng } 174d5f1e6a2SDong Aisheng 175d5f1e6a2SDong Aisheng return hw_data->hws[idx]; 176d5f1e6a2SDong Aisheng } 177d5f1e6a2SDong Aisheng 178d5f1e6a2SDong Aisheng static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev, 179d5f1e6a2SDong Aisheng struct device_node *np) 180d5f1e6a2SDong Aisheng { 181d5f1e6a2SDong Aisheng const char *output_names[IMX_LPCG_MAX_CLKS]; 182d5f1e6a2SDong Aisheng const char *parent_names[IMX_LPCG_MAX_CLKS]; 183d5f1e6a2SDong Aisheng unsigned int bit_offset[IMX_LPCG_MAX_CLKS]; 184d5f1e6a2SDong Aisheng struct clk_hw_onecell_data *clk_data; 185d5f1e6a2SDong Aisheng struct clk_hw **clk_hws; 186d5f1e6a2SDong Aisheng struct resource *res; 187d5f1e6a2SDong Aisheng void __iomem *base; 188d5f1e6a2SDong Aisheng int count; 189d5f1e6a2SDong Aisheng int idx; 190d5f1e6a2SDong Aisheng int ret; 191d5f1e6a2SDong Aisheng int i; 192d5f1e6a2SDong Aisheng 193d5f1e6a2SDong Aisheng if (!of_device_is_compatible(np, "fsl,imx8qxp-lpcg")) 194d5f1e6a2SDong Aisheng return -EINVAL; 195d5f1e6a2SDong Aisheng 196d5f1e6a2SDong Aisheng res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 197d5f1e6a2SDong Aisheng base = devm_ioremap_resource(&pdev->dev, res); 198d5f1e6a2SDong Aisheng if (IS_ERR(base)) 199d5f1e6a2SDong Aisheng return PTR_ERR(base); 200d5f1e6a2SDong Aisheng 201d5f1e6a2SDong Aisheng count = of_property_count_u32_elems(np, "clock-indices"); 202d5f1e6a2SDong Aisheng if (count < 0) { 203d5f1e6a2SDong Aisheng dev_err(&pdev->dev, "failed to count clocks\n"); 204d5f1e6a2SDong Aisheng return -EINVAL; 205d5f1e6a2SDong Aisheng } 206d5f1e6a2SDong Aisheng 207d5f1e6a2SDong Aisheng /* 208d5f1e6a2SDong Aisheng * A trick here is that we set the num of clks to the MAX instead 209d5f1e6a2SDong Aisheng * of the count from clock-indices because one LPCG supports up to 210d5f1e6a2SDong Aisheng * 8 clock outputs which each of them is fixed to 4 bits. Then we can 211d5f1e6a2SDong Aisheng * easily get the clock by clk-indices (bit-offset) / 4. 212d5f1e6a2SDong Aisheng * And the cost is very limited few pointers. 213d5f1e6a2SDong Aisheng */ 214d5f1e6a2SDong Aisheng 215d5f1e6a2SDong Aisheng clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws, 216d5f1e6a2SDong Aisheng IMX_LPCG_MAX_CLKS), GFP_KERNEL); 217d5f1e6a2SDong Aisheng if (!clk_data) 218d5f1e6a2SDong Aisheng return -ENOMEM; 219d5f1e6a2SDong Aisheng 220d5f1e6a2SDong Aisheng clk_data->num = IMX_LPCG_MAX_CLKS; 221d5f1e6a2SDong Aisheng clk_hws = clk_data->hws; 222d5f1e6a2SDong Aisheng 223d5f1e6a2SDong Aisheng ret = of_property_read_u32_array(np, "clock-indices", bit_offset, 224d5f1e6a2SDong Aisheng count); 225d5f1e6a2SDong Aisheng if (ret < 0) { 226d5f1e6a2SDong Aisheng dev_err(&pdev->dev, "failed to read clock-indices\n"); 227d5f1e6a2SDong Aisheng return -EINVAL; 228d5f1e6a2SDong Aisheng } 229d5f1e6a2SDong Aisheng 230d5f1e6a2SDong Aisheng ret = of_clk_parent_fill(np, parent_names, count); 231d5f1e6a2SDong Aisheng if (ret != count) { 232d5f1e6a2SDong Aisheng dev_err(&pdev->dev, "failed to get clock parent names\n"); 233d5f1e6a2SDong Aisheng return count; 234d5f1e6a2SDong Aisheng } 235d5f1e6a2SDong Aisheng 236d5f1e6a2SDong Aisheng ret = of_property_read_string_array(np, "clock-output-names", 237d5f1e6a2SDong Aisheng output_names, count); 238d5f1e6a2SDong Aisheng if (ret != count) { 239d5f1e6a2SDong Aisheng dev_err(&pdev->dev, "failed to read clock-output-names\n"); 240d5f1e6a2SDong Aisheng return -EINVAL; 241d5f1e6a2SDong Aisheng } 242d5f1e6a2SDong Aisheng 24318cdbad4SDong Aisheng pm_runtime_get_noresume(&pdev->dev); 24418cdbad4SDong Aisheng pm_runtime_set_active(&pdev->dev); 24518cdbad4SDong Aisheng pm_runtime_set_autosuspend_delay(&pdev->dev, 500); 24618cdbad4SDong Aisheng pm_runtime_use_autosuspend(&pdev->dev); 24718cdbad4SDong Aisheng pm_runtime_enable(&pdev->dev); 24818cdbad4SDong Aisheng 249d5f1e6a2SDong Aisheng for (i = 0; i < count; i++) { 250d5f1e6a2SDong Aisheng idx = bit_offset[i] / 4; 251d5f1e6a2SDong Aisheng if (idx > IMX_LPCG_MAX_CLKS) { 252d5f1e6a2SDong Aisheng dev_warn(&pdev->dev, "invalid bit offset of clock %d\n", 253d5f1e6a2SDong Aisheng i); 254d5f1e6a2SDong Aisheng ret = -EINVAL; 255d5f1e6a2SDong Aisheng goto unreg; 256d5f1e6a2SDong Aisheng } 257d5f1e6a2SDong Aisheng 25818cdbad4SDong Aisheng clk_hws[idx] = imx_clk_lpcg_scu_dev(&pdev->dev, output_names[i], 259d5f1e6a2SDong Aisheng parent_names[i], 0, base, 260d5f1e6a2SDong Aisheng bit_offset[i], false); 261d5f1e6a2SDong Aisheng if (IS_ERR(clk_hws[idx])) { 262d5f1e6a2SDong Aisheng dev_warn(&pdev->dev, "failed to register clock %d\n", 263d5f1e6a2SDong Aisheng idx); 264d5f1e6a2SDong Aisheng ret = PTR_ERR(clk_hws[idx]); 265d5f1e6a2SDong Aisheng goto unreg; 266d5f1e6a2SDong Aisheng } 267d5f1e6a2SDong Aisheng } 268d5f1e6a2SDong Aisheng 269d5f1e6a2SDong Aisheng ret = devm_of_clk_add_hw_provider(&pdev->dev, imx_lpcg_of_clk_src_get, 270d5f1e6a2SDong Aisheng clk_data); 27118cdbad4SDong Aisheng if (ret) 27218cdbad4SDong Aisheng goto unreg; 27318cdbad4SDong Aisheng 27418cdbad4SDong Aisheng pm_runtime_mark_last_busy(&pdev->dev); 27518cdbad4SDong Aisheng pm_runtime_put_autosuspend(&pdev->dev); 27618cdbad4SDong Aisheng 277d5f1e6a2SDong Aisheng return 0; 278d5f1e6a2SDong Aisheng 279d5f1e6a2SDong Aisheng unreg: 280d5f1e6a2SDong Aisheng while (--i >= 0) { 281d5f1e6a2SDong Aisheng idx = bit_offset[i] / 4; 282d5f1e6a2SDong Aisheng if (clk_hws[idx]) 283d5f1e6a2SDong Aisheng imx_clk_lpcg_scu_unregister(clk_hws[idx]); 284d5f1e6a2SDong Aisheng } 285d5f1e6a2SDong Aisheng 28618cdbad4SDong Aisheng pm_runtime_disable(&pdev->dev); 28718cdbad4SDong Aisheng 288d5f1e6a2SDong Aisheng return ret; 289d5f1e6a2SDong Aisheng } 290d5f1e6a2SDong Aisheng 2911e3121bfSAisheng Dong static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev) 2921e3121bfSAisheng Dong { 2931e3121bfSAisheng Dong struct device *dev = &pdev->dev; 2941e3121bfSAisheng Dong struct device_node *np = dev->of_node; 2951e3121bfSAisheng Dong struct clk_hw_onecell_data *clk_data; 2961e3121bfSAisheng Dong const struct imx8qxp_ss_lpcg *ss_lpcg; 2971e3121bfSAisheng Dong const struct imx8qxp_lpcg_data *lpcg; 2981e3121bfSAisheng Dong struct resource *res; 2991e3121bfSAisheng Dong struct clk_hw **clks; 3001e3121bfSAisheng Dong void __iomem *base; 301d5f1e6a2SDong Aisheng int ret; 3021e3121bfSAisheng Dong int i; 3031e3121bfSAisheng Dong 304d5f1e6a2SDong Aisheng /* try new binding to parse clocks from device tree first */ 305d5f1e6a2SDong Aisheng ret = imx_lpcg_parse_clks_from_dt(pdev, np); 306d5f1e6a2SDong Aisheng if (!ret) 307d5f1e6a2SDong Aisheng return 0; 308d5f1e6a2SDong Aisheng 3091e3121bfSAisheng Dong ss_lpcg = of_device_get_match_data(dev); 3101e3121bfSAisheng Dong if (!ss_lpcg) 3111e3121bfSAisheng Dong return -ENODEV; 3121e3121bfSAisheng Dong 313249fce6fSLeonard Crestez /* 314249fce6fSLeonard Crestez * Please don't replace this with devm_platform_ioremap_resource. 315249fce6fSLeonard Crestez * 316249fce6fSLeonard Crestez * devm_platform_ioremap_resource calls devm_ioremap_resource which 317249fce6fSLeonard Crestez * differs from devm_ioremap by also calling devm_request_mem_region 318249fce6fSLeonard Crestez * and preventing other mappings in the same area. 319249fce6fSLeonard Crestez * 320249fce6fSLeonard Crestez * On imx8 the LPCG nodes map entire subsystems and overlap 321249fce6fSLeonard Crestez * peripherals, this means that using devm_platform_ioremap_resource 322249fce6fSLeonard Crestez * will cause many devices to fail to probe including serial ports. 323249fce6fSLeonard Crestez */ 3241e3121bfSAisheng Dong res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 32548504619SWei Yongjun if (!res) 32648504619SWei Yongjun return -EINVAL; 3271e3121bfSAisheng Dong base = devm_ioremap(dev, res->start, resource_size(res)); 3281e3121bfSAisheng Dong if (!base) 3291e3121bfSAisheng Dong return -ENOMEM; 3301e3121bfSAisheng Dong 3311e3121bfSAisheng Dong clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws, 3321e3121bfSAisheng Dong ss_lpcg->num_max), GFP_KERNEL); 3331e3121bfSAisheng Dong if (!clk_data) 3341e3121bfSAisheng Dong return -ENOMEM; 3351e3121bfSAisheng Dong 3361e3121bfSAisheng Dong clk_data->num = ss_lpcg->num_max; 3371e3121bfSAisheng Dong clks = clk_data->hws; 3381e3121bfSAisheng Dong 3391e3121bfSAisheng Dong for (i = 0; i < ss_lpcg->num_lpcg; i++) { 3401e3121bfSAisheng Dong lpcg = ss_lpcg->lpcg + i; 3411e3121bfSAisheng Dong clks[lpcg->id] = imx_clk_lpcg_scu(lpcg->name, lpcg->parent, 3421e3121bfSAisheng Dong lpcg->flags, base + lpcg->offset, 3431e3121bfSAisheng Dong lpcg->bit_idx, lpcg->hw_gate); 3441e3121bfSAisheng Dong } 3451e3121bfSAisheng Dong 3461e3121bfSAisheng Dong for (i = 0; i < clk_data->num; i++) { 3471e3121bfSAisheng Dong if (IS_ERR(clks[i])) 3481e3121bfSAisheng Dong pr_warn("i.MX clk %u: register failed with %ld\n", 3491e3121bfSAisheng Dong i, PTR_ERR(clks[i])); 3501e3121bfSAisheng Dong } 3511e3121bfSAisheng Dong 3521e3121bfSAisheng Dong return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); 3531e3121bfSAisheng Dong } 3541e3121bfSAisheng Dong 3551e3121bfSAisheng Dong static const struct of_device_id imx8qxp_lpcg_match[] = { 3561e3121bfSAisheng Dong { .compatible = "fsl,imx8qxp-lpcg-adma", &imx8qxp_ss_adma, }, 3571e3121bfSAisheng Dong { .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, }, 3581e3121bfSAisheng Dong { .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, }, 359d5f1e6a2SDong Aisheng { .compatible = "fsl,imx8qxp-lpcg", NULL }, 3601e3121bfSAisheng Dong { /* sentinel */ } 3611e3121bfSAisheng Dong }; 3621e3121bfSAisheng Dong 3631e3121bfSAisheng Dong static struct platform_driver imx8qxp_lpcg_clk_driver = { 3641e3121bfSAisheng Dong .driver = { 3651e3121bfSAisheng Dong .name = "imx8qxp-lpcg-clk", 3661e3121bfSAisheng Dong .of_match_table = imx8qxp_lpcg_match, 367*ea0c5cbaSDong Aisheng .pm = &imx_clk_lpcg_scu_pm_ops, 3681e3121bfSAisheng Dong .suppress_bind_attrs = true, 3691e3121bfSAisheng Dong }, 3701e3121bfSAisheng Dong .probe = imx8qxp_lpcg_clk_probe, 3711e3121bfSAisheng Dong }; 3721e3121bfSAisheng Dong 3731e3121bfSAisheng Dong builtin_platform_driver(imx8qxp_lpcg_clk_driver); 374e0d0d4d8SAnson Huang 375e0d0d4d8SAnson Huang MODULE_AUTHOR("Aisheng Dong <aisheng.dong@nxp.com>"); 376e0d0d4d8SAnson Huang MODULE_DESCRIPTION("NXP i.MX8QXP LPCG clock driver"); 377e0d0d4d8SAnson Huang MODULE_LICENSE("GPL v2"); 378