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