sun6i-prcm.c (4215B)
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 17struct prcm_data { 18 int nsubdevs; 19 const struct mfd_cell *subdevs; 20}; 21 22static const struct resource sun6i_a31_ar100_clk_res[] = { 23 DEFINE_RES_MEM(0x0, 4) 24}; 25 26static const struct resource sun6i_a31_apb0_clk_res[] = { 27 DEFINE_RES_MEM(0xc, 4) 28}; 29 30static const struct resource sun6i_a31_apb0_gates_clk_res[] = { 31 DEFINE_RES_MEM(0x28, 4) 32}; 33 34static const struct resource sun6i_a31_ir_clk_res[] = { 35 DEFINE_RES_MEM(0x54, 4) 36}; 37 38static const struct resource sun6i_a31_apb0_rstc_res[] = { 39 DEFINE_RES_MEM(0xb0, 4) 40}; 41 42static const struct resource sun8i_codec_analog_res[] = { 43 DEFINE_RES_MEM(SUN8I_CODEC_ANALOG_BASE, SUN8I_CODEC_ANALOG_SIZE), 44}; 45 46static 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 79static 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 106static const struct prcm_data sun6i_a31_prcm_data = { 107 .nsubdevs = ARRAY_SIZE(sun6i_a31_prcm_subdevs), 108 .subdevs = sun6i_a31_prcm_subdevs, 109}; 110 111static const struct prcm_data sun8i_a23_prcm_data = { 112 .nsubdevs = ARRAY_SIZE(sun8i_a23_prcm_subdevs), 113 .subdevs = sun8i_a23_prcm_subdevs, 114}; 115 116static 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 128static 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 157static 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}; 164builtin_platform_driver(sun6i_prcm_driver);