xref: /openbmc/linux/drivers/clk/imx/clk-imx8qxp-lpcg.c (revision ea0c5cbaf8b70fd6fe0269fb0b951965c82229cc)
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