xref: /openbmc/linux/drivers/mfd/sun6i-prcm.c (revision 65417d9f)
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 	struct device_node *np = pdev->dev.of_node;
152 	const struct of_device_id *match;
153 	const struct prcm_data *data;
154 	struct resource *res;
155 	int ret;
156 
157 	match = of_match_node(sun6i_prcm_dt_ids, np);
158 	if (!match)
159 		return -EINVAL;
160 
161 	data = match->data;
162 
163 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
164 	if (!res) {
165 		dev_err(&pdev->dev, "no prcm memory region provided\n");
166 		return -ENOENT;
167 	}
168 
169 	ret = mfd_add_devices(&pdev->dev, 0, data->subdevs, data->nsubdevs,
170 			      res, -1, NULL);
171 	if (ret) {
172 		dev_err(&pdev->dev, "failed to add subdevices\n");
173 		return ret;
174 	}
175 
176 	return 0;
177 }
178 
179 static struct platform_driver sun6i_prcm_driver = {
180 	.driver = {
181 		.name = "sun6i-prcm",
182 		.of_match_table = sun6i_prcm_dt_ids,
183 	},
184 	.probe = sun6i_prcm_probe,
185 };
186 builtin_platform_driver(sun6i_prcm_driver);
187