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 struct prcm_data { 16 int nsubdevs; 17 const struct mfd_cell *subdevs; 18 }; 19 20 static const struct resource sun6i_a31_ar100_clk_res[] = { 21 { 22 .start = 0x0, 23 .end = 0x3, 24 .flags = IORESOURCE_MEM, 25 }, 26 }; 27 28 static const struct resource sun6i_a31_apb0_clk_res[] = { 29 { 30 .start = 0xc, 31 .end = 0xf, 32 .flags = IORESOURCE_MEM, 33 }, 34 }; 35 36 static const struct resource sun6i_a31_apb0_gates_clk_res[] = { 37 { 38 .start = 0x28, 39 .end = 0x2b, 40 .flags = IORESOURCE_MEM, 41 }, 42 }; 43 44 static const struct resource sun6i_a31_ir_clk_res[] = { 45 { 46 .start = 0x54, 47 .end = 0x57, 48 .flags = IORESOURCE_MEM, 49 }, 50 }; 51 52 static const struct resource sun6i_a31_apb0_rstc_res[] = { 53 { 54 .start = 0xb0, 55 .end = 0xb3, 56 .flags = IORESOURCE_MEM, 57 }, 58 }; 59 60 static const struct mfd_cell sun6i_a31_prcm_subdevs[] = { 61 { 62 .name = "sun6i-a31-ar100-clk", 63 .of_compatible = "allwinner,sun6i-a31-ar100-clk", 64 .num_resources = ARRAY_SIZE(sun6i_a31_ar100_clk_res), 65 .resources = sun6i_a31_ar100_clk_res, 66 }, 67 { 68 .name = "sun6i-a31-apb0-clk", 69 .of_compatible = "allwinner,sun6i-a31-apb0-clk", 70 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res), 71 .resources = sun6i_a31_apb0_clk_res, 72 }, 73 { 74 .name = "sun6i-a31-apb0-gates-clk", 75 .of_compatible = "allwinner,sun6i-a31-apb0-gates-clk", 76 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), 77 .resources = sun6i_a31_apb0_gates_clk_res, 78 }, 79 { 80 .name = "sun6i-a31-ir-clk", 81 .of_compatible = "allwinner,sun4i-a10-mod0-clk", 82 .num_resources = ARRAY_SIZE(sun6i_a31_ir_clk_res), 83 .resources = sun6i_a31_ir_clk_res, 84 }, 85 { 86 .name = "sun6i-a31-apb0-clock-reset", 87 .of_compatible = "allwinner,sun6i-a31-clock-reset", 88 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), 89 .resources = sun6i_a31_apb0_rstc_res, 90 }, 91 }; 92 93 static const struct mfd_cell sun8i_a23_prcm_subdevs[] = { 94 { 95 .name = "sun8i-a23-apb0-clk", 96 .of_compatible = "allwinner,sun8i-a23-apb0-clk", 97 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_clk_res), 98 .resources = sun6i_a31_apb0_clk_res, 99 }, 100 { 101 .name = "sun6i-a31-apb0-gates-clk", 102 .of_compatible = "allwinner,sun8i-a23-apb0-gates-clk", 103 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_gates_clk_res), 104 .resources = sun6i_a31_apb0_gates_clk_res, 105 }, 106 { 107 .name = "sun6i-a31-apb0-clock-reset", 108 .of_compatible = "allwinner,sun6i-a31-clock-reset", 109 .num_resources = ARRAY_SIZE(sun6i_a31_apb0_rstc_res), 110 .resources = sun6i_a31_apb0_rstc_res, 111 }, 112 }; 113 114 static const struct prcm_data sun6i_a31_prcm_data = { 115 .nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs), 116 .subdevs = sun6i_a31_prcm_subdevs, 117 }; 118 119 static const struct prcm_data sun8i_a23_prcm_data = { 120 .nsubdevs = ARRAY_SIZE(sun8i_a23_prcm_subdevs), 121 .subdevs = sun8i_a23_prcm_subdevs, 122 }; 123 124 static const struct of_device_id sun6i_prcm_dt_ids[] = { 125 { 126 .compatible = "allwinner,sun6i-a31-prcm", 127 .data = &sun6i_a31_prcm_data, 128 }, 129 { 130 .compatible = "allwinner,sun8i-a23-prcm", 131 .data = &sun8i_a23_prcm_data, 132 }, 133 { /* sentinel */ }, 134 }; 135 136 static int sun6i_prcm_probe(struct platform_device *pdev) 137 { 138 struct device_node *np = pdev->dev.of_node; 139 const struct of_device_id *match; 140 const struct prcm_data *data; 141 struct resource *res; 142 int ret; 143 144 match = of_match_node(sun6i_prcm_dt_ids, np); 145 if (!match) 146 return -EINVAL; 147 148 data = match->data; 149 150 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 151 if (!res) { 152 dev_err(&pdev->dev, "no prcm memory region provided\n"); 153 return -ENOENT; 154 } 155 156 ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs, 157 res, -1, NULL); 158 if (ret) { 159 dev_err(&pdev->dev, "failed to add subdevices\n"); 160 return ret; 161 } 162 163 return 0; 164 } 165 166 static struct platform_driver sun6i_prcm_driver = { 167 .driver = { 168 .name = "sun6i-prcm", 169 .of_match_table = sun6i_prcm_dt_ids, 170 }, 171 .probe = sun6i_prcm_probe, 172 }; 173 builtin_platform_driver(sun6i_prcm_driver); 174