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