1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * BCM63xx Power Domain Controller Driver
4  *
5  * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
6  */
7 
8 #include <dt-bindings/soc/bcm6318-pm.h>
9 #include <dt-bindings/soc/bcm6328-pm.h>
10 #include <dt-bindings/soc/bcm6362-pm.h>
11 #include <dt-bindings/soc/bcm63268-pm.h>
12 #include <linux/io.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/pm_domain.h>
16 #include <linux/of.h>
17 #include <linux/of_device.h>
18 
19 struct bcm63xx_power_dev {
20 	struct generic_pm_domain genpd;
21 	struct bcm63xx_power *power;
22 	uint32_t mask;
23 };
24 
25 struct bcm63xx_power {
26 	void __iomem *base;
27 	spinlock_t lock;
28 	struct bcm63xx_power_dev *dev;
29 	struct genpd_onecell_data genpd_data;
30 	struct generic_pm_domain **genpd;
31 };
32 
33 struct bcm63xx_power_data {
34 	const char * const name;
35 	uint8_t bit;
36 	unsigned int flags;
37 };
38 
bcm63xx_power_get_state(struct bcm63xx_power_dev * pmd,bool * is_on)39 static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on)
40 {
41 	struct bcm63xx_power *power = pmd->power;
42 
43 	if (!pmd->mask) {
44 		*is_on = false;
45 		return -EINVAL;
46 	}
47 
48 	*is_on = !(__raw_readl(power->base) & pmd->mask);
49 
50 	return 0;
51 }
52 
bcm63xx_power_set_state(struct bcm63xx_power_dev * pmd,bool on)53 static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on)
54 {
55 	struct bcm63xx_power *power = pmd->power;
56 	unsigned long flags;
57 	uint32_t val;
58 
59 	if (!pmd->mask)
60 		return -EINVAL;
61 
62 	spin_lock_irqsave(&power->lock, flags);
63 	val = __raw_readl(power->base);
64 	if (on)
65 		val &= ~pmd->mask;
66 	else
67 		val |= pmd->mask;
68 	__raw_writel(val, power->base);
69 	spin_unlock_irqrestore(&power->lock, flags);
70 
71 	return 0;
72 }
73 
bcm63xx_power_on(struct generic_pm_domain * genpd)74 static int bcm63xx_power_on(struct generic_pm_domain *genpd)
75 {
76 	struct bcm63xx_power_dev *pmd = container_of(genpd,
77 		struct bcm63xx_power_dev, genpd);
78 
79 	return bcm63xx_power_set_state(pmd, true);
80 }
81 
bcm63xx_power_off(struct generic_pm_domain * genpd)82 static int bcm63xx_power_off(struct generic_pm_domain *genpd)
83 {
84 	struct bcm63xx_power_dev *pmd = container_of(genpd,
85 		struct bcm63xx_power_dev, genpd);
86 
87 	return bcm63xx_power_set_state(pmd, false);
88 }
89 
bcm63xx_power_probe(struct platform_device * pdev)90 static int bcm63xx_power_probe(struct platform_device *pdev)
91 {
92 	struct device *dev = &pdev->dev;
93 	struct device_node *np = dev->of_node;
94 	const struct bcm63xx_power_data *entry, *table;
95 	struct bcm63xx_power *power;
96 	unsigned int ndom;
97 	uint8_t max_bit = 0;
98 	int ret;
99 
100 	power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
101 	if (!power)
102 		return -ENOMEM;
103 
104 	power->base = devm_platform_ioremap_resource(pdev, 0);
105 	if (IS_ERR(power->base))
106 		return PTR_ERR(power->base);
107 
108 	table = of_device_get_match_data(dev);
109 	if (!table)
110 		return -EINVAL;
111 
112 	power->genpd_data.num_domains = 0;
113 	ndom = 0;
114 	for (entry = table; entry->name; entry++) {
115 		max_bit = max(max_bit, entry->bit);
116 		ndom++;
117 	}
118 
119 	if (!ndom)
120 		return -ENODEV;
121 
122 	power->genpd_data.num_domains = max_bit + 1;
123 
124 	power->dev = devm_kcalloc(dev, power->genpd_data.num_domains,
125 				  sizeof(struct bcm63xx_power_dev),
126 				  GFP_KERNEL);
127 	if (!power->dev)
128 		return -ENOMEM;
129 
130 	power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains,
131 				    sizeof(struct generic_pm_domain *),
132 				    GFP_KERNEL);
133 	if (!power->genpd)
134 		return -ENOMEM;
135 
136 	power->genpd_data.domains = power->genpd;
137 
138 	ndom = 0;
139 	for (entry = table; entry->name; entry++) {
140 		struct bcm63xx_power_dev *pmd = &power->dev[ndom];
141 		bool is_on;
142 
143 		pmd->power = power;
144 		pmd->mask = BIT(entry->bit);
145 		pmd->genpd.name = entry->name;
146 		pmd->genpd.flags = entry->flags;
147 
148 		ret = bcm63xx_power_get_state(pmd, &is_on);
149 		if (ret)
150 			dev_warn(dev, "unable to get current state for %s\n",
151 				 pmd->genpd.name);
152 
153 		pmd->genpd.power_on = bcm63xx_power_on;
154 		pmd->genpd.power_off = bcm63xx_power_off;
155 
156 		pm_genpd_init(&pmd->genpd, NULL, !is_on);
157 		power->genpd[entry->bit] = &pmd->genpd;
158 
159 		ndom++;
160 	}
161 
162 	spin_lock_init(&power->lock);
163 
164 	ret = of_genpd_add_provider_onecell(np, &power->genpd_data);
165 	if (ret) {
166 		dev_err(dev, "failed to register genpd driver: %d\n", ret);
167 		return ret;
168 	}
169 
170 	dev_info(dev, "registered %u power domains\n", ndom);
171 
172 	return 0;
173 }
174 
175 static const struct bcm63xx_power_data bcm6318_power_domains[] = {
176 	{
177 		.name = "pcie",
178 		.bit = BCM6318_POWER_DOMAIN_PCIE,
179 	}, {
180 		.name = "usb",
181 		.bit = BCM6318_POWER_DOMAIN_USB,
182 	}, {
183 		.name = "ephy0",
184 		.bit = BCM6318_POWER_DOMAIN_EPHY0,
185 	}, {
186 		.name = "ephy1",
187 		.bit = BCM6318_POWER_DOMAIN_EPHY1,
188 	}, {
189 		.name = "ephy2",
190 		.bit = BCM6318_POWER_DOMAIN_EPHY2,
191 	}, {
192 		.name = "ephy3",
193 		.bit = BCM6318_POWER_DOMAIN_EPHY3,
194 	}, {
195 		.name = "ldo2p5",
196 		.bit = BCM6318_POWER_DOMAIN_LDO2P5,
197 		.flags = GENPD_FLAG_ALWAYS_ON,
198 	}, {
199 		.name = "ldo2p9",
200 		.bit = BCM6318_POWER_DOMAIN_LDO2P9,
201 		.flags = GENPD_FLAG_ALWAYS_ON,
202 	}, {
203 		.name = "sw1p0",
204 		.bit = BCM6318_POWER_DOMAIN_SW1P0,
205 		.flags = GENPD_FLAG_ALWAYS_ON,
206 	}, {
207 		.name = "pad",
208 		.bit = BCM6318_POWER_DOMAIN_PAD,
209 		.flags = GENPD_FLAG_ALWAYS_ON,
210 	}, {
211 		/* sentinel */
212 	},
213 };
214 
215 static const struct bcm63xx_power_data bcm6328_power_domains[] = {
216 	{
217 		.name = "adsl2-mips",
218 		.bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS,
219 	}, {
220 		.name = "adsl2-phy",
221 		.bit = BCM6328_POWER_DOMAIN_ADSL2_PHY,
222 	}, {
223 		.name = "adsl2-afe",
224 		.bit = BCM6328_POWER_DOMAIN_ADSL2_AFE,
225 	}, {
226 		.name = "sar",
227 		.bit = BCM6328_POWER_DOMAIN_SAR,
228 	}, {
229 		.name = "pcm",
230 		.bit = BCM6328_POWER_DOMAIN_PCM,
231 	}, {
232 		.name = "usbd",
233 		.bit = BCM6328_POWER_DOMAIN_USBD,
234 	}, {
235 		.name = "usbh",
236 		.bit = BCM6328_POWER_DOMAIN_USBH,
237 	}, {
238 		.name = "pcie",
239 		.bit = BCM6328_POWER_DOMAIN_PCIE,
240 	}, {
241 		.name = "robosw",
242 		.bit = BCM6328_POWER_DOMAIN_ROBOSW,
243 	}, {
244 		.name = "ephy",
245 		.bit = BCM6328_POWER_DOMAIN_EPHY,
246 	}, {
247 		/* sentinel */
248 	},
249 };
250 
251 static const struct bcm63xx_power_data bcm6362_power_domains[] = {
252 	{
253 		.name = "sar",
254 		.bit = BCM6362_POWER_DOMAIN_SAR,
255 	}, {
256 		.name = "ipsec",
257 		.bit = BCM6362_POWER_DOMAIN_IPSEC,
258 	}, {
259 		.name = "mips",
260 		.bit = BCM6362_POWER_DOMAIN_MIPS,
261 		.flags = GENPD_FLAG_ALWAYS_ON,
262 	}, {
263 		.name = "dect",
264 		.bit = BCM6362_POWER_DOMAIN_DECT,
265 	}, {
266 		.name = "usbh",
267 		.bit = BCM6362_POWER_DOMAIN_USBH,
268 	}, {
269 		.name = "usbd",
270 		.bit = BCM6362_POWER_DOMAIN_USBD,
271 	}, {
272 		.name = "robosw",
273 		.bit = BCM6362_POWER_DOMAIN_ROBOSW,
274 	}, {
275 		.name = "pcm",
276 		.bit = BCM6362_POWER_DOMAIN_PCM,
277 	}, {
278 		.name = "periph",
279 		.bit = BCM6362_POWER_DOMAIN_PERIPH,
280 		.flags = GENPD_FLAG_ALWAYS_ON,
281 	}, {
282 		.name = "adsl-phy",
283 		.bit = BCM6362_POWER_DOMAIN_ADSL_PHY,
284 	}, {
285 		.name = "gmii-pads",
286 		.bit = BCM6362_POWER_DOMAIN_GMII_PADS,
287 	}, {
288 		.name = "fap",
289 		.bit = BCM6362_POWER_DOMAIN_FAP,
290 	}, {
291 		.name = "pcie",
292 		.bit = BCM6362_POWER_DOMAIN_PCIE,
293 	}, {
294 		.name = "wlan-pads",
295 		.bit = BCM6362_POWER_DOMAIN_WLAN_PADS,
296 	}, {
297 		/* sentinel */
298 	},
299 };
300 
301 static const struct bcm63xx_power_data bcm63268_power_domains[] = {
302 	{
303 		.name = "sar",
304 		.bit = BCM63268_POWER_DOMAIN_SAR,
305 	}, {
306 		.name = "ipsec",
307 		.bit = BCM63268_POWER_DOMAIN_IPSEC,
308 	}, {
309 		.name = "mips",
310 		.bit = BCM63268_POWER_DOMAIN_MIPS,
311 		.flags = GENPD_FLAG_ALWAYS_ON,
312 	}, {
313 		.name = "dect",
314 		.bit = BCM63268_POWER_DOMAIN_DECT,
315 	}, {
316 		.name = "usbh",
317 		.bit = BCM63268_POWER_DOMAIN_USBH,
318 	}, {
319 		.name = "usbd",
320 		.bit = BCM63268_POWER_DOMAIN_USBD,
321 	}, {
322 		.name = "robosw",
323 		.bit = BCM63268_POWER_DOMAIN_ROBOSW,
324 	}, {
325 		.name = "pcm",
326 		.bit = BCM63268_POWER_DOMAIN_PCM,
327 	}, {
328 		.name = "periph",
329 		.bit = BCM63268_POWER_DOMAIN_PERIPH,
330 		.flags = GENPD_FLAG_ALWAYS_ON,
331 	}, {
332 		.name = "vdsl-phy",
333 		.bit = BCM63268_POWER_DOMAIN_VDSL_PHY,
334 	}, {
335 		.name = "vdsl-mips",
336 		.bit = BCM63268_POWER_DOMAIN_VDSL_MIPS,
337 	}, {
338 		.name = "fap",
339 		.bit = BCM63268_POWER_DOMAIN_FAP,
340 	}, {
341 		.name = "pcie",
342 		.bit = BCM63268_POWER_DOMAIN_PCIE,
343 	}, {
344 		.name = "wlan-pads",
345 		.bit = BCM63268_POWER_DOMAIN_WLAN_PADS,
346 	}, {
347 		/* sentinel */
348 	},
349 };
350 
351 static const struct of_device_id bcm63xx_power_of_match[] = {
352 	{
353 		.compatible = "brcm,bcm6318-power-controller",
354 		.data = &bcm6318_power_domains,
355 	}, {
356 		.compatible = "brcm,bcm6328-power-controller",
357 		.data = &bcm6328_power_domains,
358 	}, {
359 		.compatible = "brcm,bcm6362-power-controller",
360 		.data = &bcm6362_power_domains,
361 	}, {
362 		.compatible = "brcm,bcm63268-power-controller",
363 		.data = &bcm63268_power_domains,
364 	}, {
365 		/* sentinel */
366 	}
367 };
368 
369 static struct platform_driver bcm63xx_power_driver = {
370 	.driver = {
371 		.name = "bcm63xx-power-controller",
372 		.of_match_table = bcm63xx_power_of_match,
373 	},
374 	.probe  = bcm63xx_power_probe,
375 };
376 builtin_platform_driver(bcm63xx_power_driver);
377