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