1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014 Free Electrons 4 * 5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 6 * 7 * Allwinner PRCM (Power/Reset/Clock Management) driver 8 */ 9 10 #include <linux/mfd/core.h> 11 #include <linux/init.h> 12 #include <linux/of.h> 13 14 #define SUN8I_CODEC_ANALOG_BASE 0x1c0 15 #define SUN8I_CODEC_ANALOG_SIZE 0x4 16 17 struct prcm_data { 18 int nsubdevs; 19 const struct mfd_cell *subdevs; 20 }; 21 22 static const struct resource sun6i_a31_ar100_clk_res[] = { 23 { 24 .start = 0x0, 25 .end = 0x3, 26 .flags = IORESOURCE_MEM, 27 }, 28 }; 29 30 static const struct resource sun6i_a31_apb0_clk_res[] = { 31 { 32 .start = 0xc, 33 .end = 0xf, 34 .flags = IORESOURCE_MEM, 35 }, 36 }; 37 38 static const struct resource sun6i_a31_apb0_gates_clk_res[] = { 39 { 40 .start = 0x28, 41 .end = 0x2b, 42 .flags = IORESOURCE_MEM, 43 }, 44 }; 45 46 static const struct resource sun6i_a31_ir_clk_res[] = { 47 { 48 .start = 0x54, 49 .end = 0x57, 50 .flags = IORESOURCE_MEM, 51 }, 52 }; 53 54 static const struct resource sun6i_a31_apb0_rstc_res[] = { 55 { 56 .start = 0xb0, 57 .end = 0xb3, 58 .flags = IORESOURCE_MEM, 59 }, 60 }; 61 62 static const struct resource sun8i_codec_analog_res[] = { 63 DEFINE_RES_MEM(SUN8I_CODEC_ANALOG_BASE, SUN8I_CODEC_ANALOG_SIZE), 64 }; 65 66 static const struct mfd_cell sun6i_a31_prcm_subdevs[] = { 67 { 68 .name = "sun6i-a31-ar100-clk", 69 .of_compatible = "allwinner,sun6i-a31-ar100-clk", 70 .num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res), 71 .resources = sun6i_a31_ar100_clk_res, 72 }, 73 { 74 .name = "sun6i-a31-apb0-clk", 75 .of_compatible = "allwinner,sun6i-a31-apb0-clk", 76 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res), 77 .resources = sun6i_a31_apb0_clk_res, 78 }, 79 { 80 .name = "sun6i-a31-apb0-gates-clk", 81 .of_compatible = "allwinner,sun6i-a31-apb0-gates-clk", 82 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), 83 .resources = sun6i_a31_apb0_gates_clk_res, 84 }, 85 { 86 .name = "sun6i-a31-ir-clk", 87 .of_compatible = "allwinner,sun4i-a10-mod0-clk", 88 .num_resources = ARRAY_SIZE(sun6i_a31_ir_clk_res), 89 .resources = sun6i_a31_ir_clk_res, 90 }, 91 { 92 .name = "sun6i-a31-apb0-clock-reset", 93 .of_compatible = "allwinner,sun6i-a31-clock-reset", 94 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), 95 .resources = sun6i_a31_apb0_rstc_res, 96 }, 97 }; 98 99 static const struct mfd_cell sun8i_a23_prcm_subdevs[] = { 100 { 101 .name = "sun8i-a23-apb0-clk", 102 .of_compatible = "allwinner,sun8i-a23-apb0-clk", 103 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res), 104 .resources = sun6i_a31_apb0_clk_res, 105 }, 106 { 107 .name = "sun6i-a31-apb0-gates-clk", 108 .of_compatible = "allwinner,sun8i-a23-apb0-gates-clk", 109 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), 110 .resources = sun6i_a31_apb0_gates_clk_res, 111 }, 112 { 113 .name = "sun6i-a31-apb0-clock-reset", 114 .of_compatible = "allwinner,sun6i-a31-clock-reset", 115 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), 116 .resources = sun6i_a31_apb0_rstc_res, 117 }, 118 { 119 .name = "sun8i-codec-analog", 120 .of_compatible = "allwinner,sun8i-a23-codec-analog", 121 .num_resources = ARRAY_SIZE(sun8i_codec_analog_res), 122 .resources = sun8i_codec_analog_res, 123 }, 124 }; 125 126 static const struct prcm_data sun6i_a31_prcm_data = { 127 .nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs), 128 .subdevs = sun6i_a31_prcm_subdevs, 129 }; 130 131 static const struct prcm_data sun8i_a23_prcm_data = { 132 .nsubdevs = ARRAY_SIZE(sun8i_a23_prcm_subdevs), 133 .subdevs = sun8i_a23_prcm_subdevs, 134 }; 135 136 static const struct of_device_id sun6i_prcm_dt_ids[] = { 137 { 138 .compatible = "allwinner,sun6i-a31-prcm", 139 .data = &sun6i_a31_prcm_data, 140 }, 141 { 142 .compatible = "allwinner,sun8i-a23-prcm", 143 .data = &sun8i_a23_prcm_data, 144 }, 145 { /* sentinel */ }, 146 }; 147 148 static int sun6i_prcm_probe(struct platform_device *pdev) 149 { 150 const struct of_device_id *match; 151 const struct prcm_data *data; 152 struct resource *res; 153 int ret; 154 155 match = of_match_node(sun6i_prcm_dt_ids, pdev->dev.of_node); 156 if (!match) 157 return -EINVAL; 158 159 data = match->data; 160 161 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 162 if (!res) { 163 dev_err(&pdev->dev, "no prcm memory region provided\n"); 164 return -ENOENT; 165 } 166 167 ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs, 168 res, -1, NULL); 169 if (ret) { 170 dev_err(&pdev->dev, "failed to add subdevices\n"); 171 return ret; 172 } 173 174 return 0; 175 } 176 177 static struct platform_driver sun6i_prcm_driver = { 178 .driver = { 179 .name = "sun6i-prcm", 180 .of_match_table = sun6i_prcm_dt_ids, 181 }, 182 .probe = sun6i_prcm_probe, 183 }; 184 builtin_platform_driver(sun6i_prcm_driver); 185