cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-imx8qxp-lpcg.c (15342B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright 2018 NXP
      4 *	Dong Aisheng <aisheng.dong@nxp.com>
      5 */
      6
      7#include <linux/clk-provider.h>
      8#include <linux/err.h>
      9#include <linux/io.h>
     10#include <linux/module.h>
     11#include <linux/of.h>
     12#include <linux/of_address.h>
     13#include <linux/of_device.h>
     14#include <linux/platform_device.h>
     15#include <linux/pm_runtime.h>
     16#include <linux/slab.h>
     17
     18#include "clk-scu.h"
     19#include "clk-imx8qxp-lpcg.h"
     20
     21#include <dt-bindings/clock/imx8-clock.h>
     22
     23/*
     24 * struct imx8qxp_lpcg_data - Description of one LPCG clock
     25 * @id: clock ID
     26 * @name: clock name
     27 * @parent: parent clock name
     28 * @flags: common clock flags
     29 * @offset: offset of this LPCG clock
     30 * @bit_idx: bit index of this LPCG clock
     31 * @hw_gate: whether supports HW autogate
     32 *
     33 * This structure describes one LPCG clock
     34 */
     35struct imx8qxp_lpcg_data {
     36	int id;
     37	char *name;
     38	char *parent;
     39	unsigned long flags;
     40	u32 offset;
     41	u8 bit_idx;
     42	bool hw_gate;
     43};
     44
     45/*
     46 * struct imx8qxp_ss_lpcg - Description of one subsystem LPCG clocks
     47 * @lpcg: LPCG clocks array of one subsystem
     48 * @num_lpcg: the number of LPCG clocks
     49 * @num_max: the maximum number of LPCG clocks
     50 *
     51 * This structure describes each subsystem LPCG clocks information
     52 * which then will be used to create respective LPCGs clocks
     53 */
     54struct imx8qxp_ss_lpcg {
     55	const struct imx8qxp_lpcg_data *lpcg;
     56	u8 num_lpcg;
     57	u8 num_max;
     58};
     59
     60static const struct imx8qxp_lpcg_data imx8qxp_lpcg_adma[] = {
     61	{ IMX_ADMA_LPCG_UART0_IPG_CLK, "uart0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_0_LPCG, 16, 0, },
     62	{ IMX_ADMA_LPCG_UART0_BAUD_CLK, "uart0_lpcg_baud_clk", "uart0_clk", 0, ADMA_LPUART_0_LPCG, 0, 0, },
     63	{ IMX_ADMA_LPCG_UART1_IPG_CLK, "uart1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_1_LPCG, 16, 0, },
     64	{ IMX_ADMA_LPCG_UART1_BAUD_CLK, "uart1_lpcg_baud_clk", "uart1_clk", 0, ADMA_LPUART_1_LPCG, 0, 0, },
     65	{ IMX_ADMA_LPCG_UART2_IPG_CLK, "uart2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_2_LPCG, 16, 0, },
     66	{ IMX_ADMA_LPCG_UART2_BAUD_CLK, "uart2_lpcg_baud_clk", "uart2_clk", 0, ADMA_LPUART_2_LPCG, 0, 0, },
     67	{ IMX_ADMA_LPCG_UART3_IPG_CLK, "uart3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_3_LPCG, 16, 0, },
     68	{ IMX_ADMA_LPCG_UART3_BAUD_CLK, "uart3_lpcg_baud_clk", "uart3_clk", 0, ADMA_LPUART_3_LPCG, 0, 0, },
     69	{ IMX_ADMA_LPCG_I2C0_IPG_CLK, "i2c0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_0_LPCG, 16, 0, },
     70	{ IMX_ADMA_LPCG_I2C0_CLK, "i2c0_lpcg_clk", "i2c0_clk", 0, ADMA_LPI2C_0_LPCG, 0, 0, },
     71	{ IMX_ADMA_LPCG_I2C1_IPG_CLK, "i2c1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_1_LPCG, 16, 0, },
     72	{ IMX_ADMA_LPCG_I2C1_CLK, "i2c1_lpcg_clk", "i2c1_clk", 0, ADMA_LPI2C_1_LPCG, 0, 0, },
     73	{ IMX_ADMA_LPCG_I2C2_IPG_CLK, "i2c2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_2_LPCG, 16, 0, },
     74	{ IMX_ADMA_LPCG_I2C2_CLK, "i2c2_lpcg_clk", "i2c2_clk", 0, ADMA_LPI2C_2_LPCG, 0, 0, },
     75	{ IMX_ADMA_LPCG_I2C3_IPG_CLK, "i2c3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_3_LPCG, 16, 0, },
     76	{ IMX_ADMA_LPCG_I2C3_CLK, "i2c3_lpcg_clk", "i2c3_clk", 0, ADMA_LPI2C_3_LPCG, 0, 0, },
     77
     78	{ IMX_ADMA_LPCG_DSP_CORE_CLK, "dsp_lpcg_core_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 28, 0, },
     79	{ IMX_ADMA_LPCG_DSP_IPG_CLK, "dsp_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 20, 0, },
     80	{ IMX_ADMA_LPCG_DSP_ADB_CLK, "dsp_lpcg_adb_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 16, 0, },
     81	{ IMX_ADMA_LPCG_OCRAM_IPG_CLK, "ocram_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_OCRAM_LPCG, 16, 0, },
     82};
     83
     84static const struct imx8qxp_ss_lpcg imx8qxp_ss_adma = {
     85	.lpcg = imx8qxp_lpcg_adma,
     86	.num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_adma),
     87	.num_max = IMX_ADMA_LPCG_CLK_END,
     88};
     89
     90static const struct imx8qxp_lpcg_data imx8qxp_lpcg_conn[] = {
     91	{ IMX_CONN_LPCG_SDHC0_PER_CLK, "sdhc0_lpcg_per_clk", "sdhc0_clk", 0, CONN_USDHC_0_LPCG, 0, 0, },
     92	{ IMX_CONN_LPCG_SDHC0_IPG_CLK, "sdhc0_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_0_LPCG, 16, 0, },
     93	{ IMX_CONN_LPCG_SDHC0_HCLK, "sdhc0_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_0_LPCG, 20, 0, },
     94	{ IMX_CONN_LPCG_SDHC1_PER_CLK, "sdhc1_lpcg_per_clk", "sdhc1_clk", 0, CONN_USDHC_1_LPCG, 0, 0, },
     95	{ IMX_CONN_LPCG_SDHC1_IPG_CLK, "sdhc1_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_1_LPCG, 16, 0, },
     96	{ IMX_CONN_LPCG_SDHC1_HCLK, "sdhc1_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_1_LPCG, 20, 0, },
     97	{ IMX_CONN_LPCG_SDHC2_PER_CLK, "sdhc2_lpcg_per_clk", "sdhc2_clk", 0, CONN_USDHC_2_LPCG, 0, 0, },
     98	{ IMX_CONN_LPCG_SDHC2_IPG_CLK, "sdhc2_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_2_LPCG, 16, 0, },
     99	{ IMX_CONN_LPCG_SDHC2_HCLK, "sdhc2_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_2_LPCG, 20, 0, },
    100	{ IMX_CONN_LPCG_ENET0_ROOT_CLK, "enet0_ipg_root_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 0, 0, },
    101	{ IMX_CONN_LPCG_ENET0_TX_CLK, "enet0_tx_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 4, 0, },
    102	{ IMX_CONN_LPCG_ENET0_AHB_CLK, "enet0_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_0_LPCG, 8, 0, },
    103	{ IMX_CONN_LPCG_ENET0_IPG_S_CLK, "enet0_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_0_LPCG, 20, 0, },
    104	{ IMX_CONN_LPCG_ENET0_IPG_CLK, "enet0_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_0_LPCG, 16, 0, },
    105	{ IMX_CONN_LPCG_ENET1_ROOT_CLK, "enet1_ipg_root_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 0, 0, },
    106	{ IMX_CONN_LPCG_ENET1_TX_CLK, "enet1_tx_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 4, 0, },
    107	{ IMX_CONN_LPCG_ENET1_AHB_CLK, "enet1_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_1_LPCG, 8, 0, },
    108	{ IMX_CONN_LPCG_ENET1_IPG_S_CLK, "enet1_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_1_LPCG, 20, 0, },
    109	{ IMX_CONN_LPCG_ENET1_IPG_CLK, "enet1_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_1_LPCG, 16, 0, },
    110};
    111
    112static const struct imx8qxp_ss_lpcg imx8qxp_ss_conn = {
    113	.lpcg = imx8qxp_lpcg_conn,
    114	.num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_conn),
    115	.num_max = IMX_CONN_LPCG_CLK_END,
    116};
    117
    118static const struct imx8qxp_lpcg_data imx8qxp_lpcg_lsio[] = {
    119	{ IMX_LSIO_LPCG_PWM0_IPG_CLK, "pwm0_lpcg_ipg_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 0, 0, },
    120	{ IMX_LSIO_LPCG_PWM0_IPG_HF_CLK, "pwm0_lpcg_ipg_hf_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 4, 0, },
    121	{ IMX_LSIO_LPCG_PWM0_IPG_S_CLK, "pwm0_lpcg_ipg_s_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 16, 0, },
    122	{ IMX_LSIO_LPCG_PWM0_IPG_SLV_CLK, "pwm0_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_0_LPCG, 20, 0, },
    123	{ IMX_LSIO_LPCG_PWM0_IPG_MSTR_CLK, "pwm0_lpcg_ipg_mstr_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 24, 0, },
    124	{ IMX_LSIO_LPCG_PWM1_IPG_CLK, "pwm1_lpcg_ipg_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 0, 0, },
    125	{ IMX_LSIO_LPCG_PWM1_IPG_HF_CLK, "pwm1_lpcg_ipg_hf_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 4, 0, },
    126	{ IMX_LSIO_LPCG_PWM1_IPG_S_CLK, "pwm1_lpcg_ipg_s_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 16, 0, },
    127	{ IMX_LSIO_LPCG_PWM1_IPG_SLV_CLK, "pwm1_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_1_LPCG, 20, 0, },
    128	{ IMX_LSIO_LPCG_PWM1_IPG_MSTR_CLK, "pwm1_lpcg_ipg_mstr_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 24, 0, },
    129	{ IMX_LSIO_LPCG_PWM2_IPG_CLK, "pwm2_lpcg_ipg_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 0, 0, },
    130	{ IMX_LSIO_LPCG_PWM2_IPG_HF_CLK, "pwm2_lpcg_ipg_hf_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 4, 0, },
    131	{ IMX_LSIO_LPCG_PWM2_IPG_S_CLK, "pwm2_lpcg_ipg_s_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 16, 0, },
    132	{ IMX_LSIO_LPCG_PWM2_IPG_SLV_CLK, "pwm2_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_2_LPCG, 20, 0, },
    133	{ IMX_LSIO_LPCG_PWM2_IPG_MSTR_CLK, "pwm2_lpcg_ipg_mstr_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 24, 0, },
    134	{ IMX_LSIO_LPCG_PWM3_IPG_CLK, "pwm3_lpcg_ipg_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 0, 0, },
    135	{ IMX_LSIO_LPCG_PWM3_IPG_HF_CLK, "pwm3_lpcg_ipg_hf_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 4, 0, },
    136	{ IMX_LSIO_LPCG_PWM3_IPG_S_CLK, "pwm3_lpcg_ipg_s_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 16, 0, },
    137	{ IMX_LSIO_LPCG_PWM3_IPG_SLV_CLK, "pwm3_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_3_LPCG, 20, 0, },
    138	{ IMX_LSIO_LPCG_PWM3_IPG_MSTR_CLK, "pwm3_lpcg_ipg_mstr_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 24, 0, },
    139	{ IMX_LSIO_LPCG_PWM4_IPG_CLK, "pwm4_lpcg_ipg_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 0, 0, },
    140	{ IMX_LSIO_LPCG_PWM4_IPG_HF_CLK, "pwm4_lpcg_ipg_hf_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 4, 0, },
    141	{ IMX_LSIO_LPCG_PWM4_IPG_S_CLK, "pwm4_lpcg_ipg_s_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 16, 0, },
    142	{ IMX_LSIO_LPCG_PWM4_IPG_SLV_CLK, "pwm4_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_4_LPCG, 20, 0, },
    143	{ IMX_LSIO_LPCG_PWM4_IPG_MSTR_CLK, "pwm4_lpcg_ipg_mstr_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 24, 0, },
    144	{ IMX_LSIO_LPCG_PWM5_IPG_CLK, "pwm5_lpcg_ipg_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 0, 0, },
    145	{ IMX_LSIO_LPCG_PWM5_IPG_HF_CLK, "pwm5_lpcg_ipg_hf_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 4, 0, },
    146	{ IMX_LSIO_LPCG_PWM5_IPG_S_CLK, "pwm5_lpcg_ipg_s_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 16, 0, },
    147	{ IMX_LSIO_LPCG_PWM5_IPG_SLV_CLK, "pwm5_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_5_LPCG, 20, 0, },
    148	{ IMX_LSIO_LPCG_PWM5_IPG_MSTR_CLK, "pwm5_lpcg_ipg_mstr_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 24, 0, },
    149	{ IMX_LSIO_LPCG_PWM6_IPG_CLK, "pwm6_lpcg_ipg_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 0, 0, },
    150	{ IMX_LSIO_LPCG_PWM6_IPG_HF_CLK, "pwm6_lpcg_ipg_hf_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 4, 0, },
    151	{ IMX_LSIO_LPCG_PWM6_IPG_S_CLK, "pwm6_lpcg_ipg_s_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 16, 0, },
    152	{ IMX_LSIO_LPCG_PWM6_IPG_SLV_CLK, "pwm6_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_6_LPCG, 20, 0, },
    153	{ IMX_LSIO_LPCG_PWM6_IPG_MSTR_CLK, "pwm6_lpcg_ipg_mstr_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 24, 0, },
    154};
    155
    156static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = {
    157	.lpcg = imx8qxp_lpcg_lsio,
    158	.num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_lsio),
    159	.num_max = IMX_LSIO_LPCG_CLK_END,
    160};
    161
    162#define IMX_LPCG_MAX_CLKS	8
    163
    164static struct clk_hw *imx_lpcg_of_clk_src_get(struct of_phandle_args *clkspec,
    165					      void *data)
    166{
    167	struct clk_hw_onecell_data *hw_data = data;
    168	unsigned int idx = clkspec->args[0] / 4;
    169
    170	if (idx >= hw_data->num) {
    171		pr_err("%s: invalid index %u\n", __func__, idx);
    172		return ERR_PTR(-EINVAL);
    173	}
    174
    175	return hw_data->hws[idx];
    176}
    177
    178static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev,
    179				       struct device_node *np)
    180{
    181	const char *output_names[IMX_LPCG_MAX_CLKS];
    182	const char *parent_names[IMX_LPCG_MAX_CLKS];
    183	unsigned int bit_offset[IMX_LPCG_MAX_CLKS];
    184	struct clk_hw_onecell_data *clk_data;
    185	struct clk_hw **clk_hws;
    186	struct resource *res;
    187	void __iomem *base;
    188	int count;
    189	int idx;
    190	int ret;
    191	int i;
    192
    193	if (!of_device_is_compatible(np, "fsl,imx8qxp-lpcg"))
    194		return -EINVAL;
    195
    196	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    197	base = devm_ioremap_resource(&pdev->dev, res);
    198	if (IS_ERR(base))
    199		return PTR_ERR(base);
    200
    201	count = of_property_count_u32_elems(np, "clock-indices");
    202	if (count < 0) {
    203		dev_err(&pdev->dev, "failed to count clocks\n");
    204		return -EINVAL;
    205	}
    206
    207	/*
    208	 * A trick here is that we set the num of clks to the MAX instead
    209	 * of the count from clock-indices because one LPCG supports up to
    210	 * 8 clock outputs which each of them is fixed to 4 bits. Then we can
    211	 * easily get the clock by clk-indices (bit-offset) / 4.
    212	 * And the cost is very limited few pointers.
    213	 */
    214
    215	clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws,
    216				IMX_LPCG_MAX_CLKS), GFP_KERNEL);
    217	if (!clk_data)
    218		return -ENOMEM;
    219
    220	clk_data->num = IMX_LPCG_MAX_CLKS;
    221	clk_hws = clk_data->hws;
    222
    223	ret = of_property_read_u32_array(np, "clock-indices", bit_offset,
    224					 count);
    225	if (ret < 0) {
    226		dev_err(&pdev->dev, "failed to read clock-indices\n");
    227		return -EINVAL;
    228	}
    229
    230	ret = of_clk_parent_fill(np, parent_names, count);
    231	if (ret != count) {
    232		dev_err(&pdev->dev, "failed to get clock parent names\n");
    233		return count;
    234	}
    235
    236	ret = of_property_read_string_array(np, "clock-output-names",
    237					    output_names, count);
    238	if (ret != count) {
    239		dev_err(&pdev->dev, "failed to read clock-output-names\n");
    240		return -EINVAL;
    241	}
    242
    243	pm_runtime_get_noresume(&pdev->dev);
    244	pm_runtime_set_active(&pdev->dev);
    245	pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
    246	pm_runtime_use_autosuspend(&pdev->dev);
    247	pm_runtime_enable(&pdev->dev);
    248
    249	for (i = 0; i < count; i++) {
    250		idx = bit_offset[i] / 4;
    251		if (idx >= IMX_LPCG_MAX_CLKS) {
    252			dev_warn(&pdev->dev, "invalid bit offset of clock %d\n",
    253				 i);
    254			ret = -EINVAL;
    255			goto unreg;
    256		}
    257
    258		clk_hws[idx] = imx_clk_lpcg_scu_dev(&pdev->dev, output_names[i],
    259						    parent_names[i], 0, base,
    260						    bit_offset[i], false);
    261		if (IS_ERR(clk_hws[idx])) {
    262			dev_warn(&pdev->dev, "failed to register clock %d\n",
    263				 idx);
    264			ret = PTR_ERR(clk_hws[idx]);
    265			goto unreg;
    266		}
    267	}
    268
    269	ret = devm_of_clk_add_hw_provider(&pdev->dev, imx_lpcg_of_clk_src_get,
    270					  clk_data);
    271	if (ret)
    272		goto unreg;
    273
    274	pm_runtime_mark_last_busy(&pdev->dev);
    275	pm_runtime_put_autosuspend(&pdev->dev);
    276
    277	return 0;
    278
    279unreg:
    280	while (--i >= 0) {
    281		idx = bit_offset[i] / 4;
    282		if (clk_hws[idx])
    283			imx_clk_lpcg_scu_unregister(clk_hws[idx]);
    284	}
    285
    286	pm_runtime_disable(&pdev->dev);
    287
    288	return ret;
    289}
    290
    291static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev)
    292{
    293	struct device *dev = &pdev->dev;
    294	struct device_node *np = dev->of_node;
    295	struct clk_hw_onecell_data *clk_data;
    296	const struct imx8qxp_ss_lpcg *ss_lpcg;
    297	const struct imx8qxp_lpcg_data *lpcg;
    298	struct resource *res;
    299	struct clk_hw **clks;
    300	void __iomem *base;
    301	int ret;
    302	int i;
    303
    304	/* try new binding to parse clocks from device tree first */
    305	ret = imx_lpcg_parse_clks_from_dt(pdev, np);
    306	if (!ret)
    307		return 0;
    308
    309	ss_lpcg = of_device_get_match_data(dev);
    310	if (!ss_lpcg)
    311		return -ENODEV;
    312
    313	/*
    314	 * Please don't replace this with devm_platform_ioremap_resource.
    315	 *
    316	 * devm_platform_ioremap_resource calls devm_ioremap_resource which
    317	 * differs from devm_ioremap by also calling devm_request_mem_region
    318	 * and preventing other mappings in the same area.
    319	 *
    320	 * On imx8 the LPCG nodes map entire subsystems and overlap
    321	 * peripherals, this means that using devm_platform_ioremap_resource
    322	 * will cause many devices to fail to probe including serial ports.
    323	 */
    324	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    325	if (!res)
    326		return -EINVAL;
    327	base = devm_ioremap(dev, res->start, resource_size(res));
    328	if (!base)
    329		return -ENOMEM;
    330
    331	clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws,
    332				ss_lpcg->num_max), GFP_KERNEL);
    333	if (!clk_data)
    334		return -ENOMEM;
    335
    336	clk_data->num = ss_lpcg->num_max;
    337	clks = clk_data->hws;
    338
    339	for (i = 0; i < ss_lpcg->num_lpcg; i++) {
    340		lpcg = ss_lpcg->lpcg + i;
    341		clks[lpcg->id] = imx_clk_lpcg_scu(lpcg->name, lpcg->parent,
    342						  lpcg->flags, base + lpcg->offset,
    343						  lpcg->bit_idx, lpcg->hw_gate);
    344	}
    345
    346	for (i = 0; i < clk_data->num; i++) {
    347		if (IS_ERR(clks[i]))
    348			pr_warn("i.MX clk %u: register failed with %ld\n",
    349				i, PTR_ERR(clks[i]));
    350	}
    351
    352	return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
    353}
    354
    355static const struct of_device_id imx8qxp_lpcg_match[] = {
    356	{ .compatible = "fsl,imx8qxp-lpcg-adma", &imx8qxp_ss_adma, },
    357	{ .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, },
    358	{ .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, },
    359	{ .compatible = "fsl,imx8qxp-lpcg", NULL },
    360	{ /* sentinel */ }
    361};
    362
    363static struct platform_driver imx8qxp_lpcg_clk_driver = {
    364	.driver = {
    365		.name = "imx8qxp-lpcg-clk",
    366		.of_match_table = imx8qxp_lpcg_match,
    367		.pm = &imx_clk_lpcg_scu_pm_ops,
    368		.suppress_bind_attrs = true,
    369	},
    370	.probe = imx8qxp_lpcg_clk_probe,
    371};
    372
    373module_platform_driver(imx8qxp_lpcg_clk_driver);
    374
    375MODULE_AUTHOR("Aisheng Dong <aisheng.dong@nxp.com>");
    376MODULE_DESCRIPTION("NXP i.MX8QXP LPCG clock driver");
    377MODULE_LICENSE("GPL v2");