GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / soc / bcm / bcm63xx / bcm63xx-power.c
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
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
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
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
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
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);