pmu: starfive: Fix some power domain can not be disabled
[platform/kernel/linux-starfive.git] / drivers / soc / starfive / jh7110_pmu.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * JH7110 Power Domain Controller Driver
4  *
5  * Copyright (C) 2022 StarFive Technology Co., Ltd.
6  */
7
8 #include <dt-bindings/power/jh7110-power.h>
9 #include <linux/io.h>
10 #include <linux/iopoll.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/interrupt.h>
14 #include <linux/pm_domain.h>
15 #include <linux/of.h>
16 #include <linux/of_device.h>
17
18 /* register define */
19 #define HW_EVENT_TURN_ON_MASK           0x04
20 #define HW_EVENT_TURN_OFF_MASK          0x08
21 #define SW_TURN_ON_POWER_MODE           0x0C
22 #define SW_TURN_OFF_POWER_MODE          0x10
23 #define SW_ENCOURAGE                    0x44
24 #define PMU_INT_MASK                    0x48
25 #define PCH_BYPASS                      0x4C
26 #define PCH_PSTATE                      0x50
27 #define PCH_TIMEOUT                     0x54
28 #define LP_TIMEOUT                      0x58
29 #define HW_TURN_ON_MODE                 0x5C
30 #define CURR_POWER_MODE                 0x80
31 #define PMU_EVENT_STATUS                0x88
32 #define PMU_INT_STATUS                  0x8C
33
34 /* sw encourage cfg */
35 #define SW_MODE_ENCOURAGE_EN_LO         0x05
36 #define SW_MODE_ENCOURAGE_EN_HI         0x50
37 #define SW_MODE_ENCOURAGE_DIS_LO        0x0A
38 #define SW_MODE_ENCOURAGE_DIS_HI        0xA0
39 #define SW_MODE_ENCOURAGE_ON            0xFF
40
41 /* pmu int status */
42 #define PMU_INT_SEQ_DONE                BIT(0)
43 #define PMU_INT_HW_REQ                  BIT(1)
44 #define PMU_INT_SW_FAIL                 GENMASK(3, 2)
45 #define PMU_INT_HW_FAIL                 GENMASK(5, 4)
46 #define PMU_INT_PCH_FAIL                GENMASK(8, 6)
47 #define PMU_INT_FAIL_MASK               (PMU_INT_SW_FAIL | \
48                                         PMU_INT_HW_FAIL | \
49                                         PMU_INT_PCH_FAIL)
50 #define PMU_INT_ALL_MASK                (PMU_INT_SEQ_DONE | \
51                                         PMU_INT_HW_REQ | \
52                                         PMU_INT_FAIL_MASK)
53
54 #define DELAY_US                        10
55 #define TIMEOUT_US                      100000
56
57 struct jh7110_power_dev {
58         struct generic_pm_domain genpd;
59         struct jh7110_pmu *power;
60         uint32_t mask;
61 };
62
63 struct jh7110_pmu {
64         void __iomem *base;
65         spinlock_t lock;
66         int irq;
67         struct device *pdev;
68         struct jh7110_power_dev *dev;
69         struct genpd_onecell_data genpd_data;
70         struct generic_pm_domain **genpd;
71 };
72
73 struct jh7110_pmu_data {
74         const char * const name;
75         uint8_t bit;
76         unsigned int flags;
77 };
78
79 static void __iomem *pmu_base;
80
81 static inline void pmu_writel(u32 val, u32 offset)
82 {
83         writel(val, pmu_base + offset);
84 }
85
86 void starfive_pmu_hw_event_turn_off_mask(u32 mask)
87 {
88         pmu_writel(mask, HW_EVENT_TURN_OFF_MASK);
89 }
90 EXPORT_SYMBOL(starfive_pmu_hw_event_turn_off_mask);
91
92 static int jh7110_pmu_get_state(struct jh7110_power_dev *pmd, bool *is_on)
93 {
94         struct jh7110_pmu *pmu = pmd->power;
95
96         if (!pmd->mask) {
97                 *is_on = false;
98                 return -EINVAL;
99         }
100
101         *is_on = __raw_readl(pmu->base + CURR_POWER_MODE) & pmd->mask;
102
103         return 0;
104 }
105
106 static int jh7110_pmu_set_state(struct jh7110_power_dev *pmd, bool on)
107 {
108         struct jh7110_pmu *pmu = pmd->power;
109         unsigned long flags;
110         uint32_t val;
111         uint32_t mode;
112         uint32_t encourage_lo;
113         uint32_t encourage_hi;
114         bool is_on;
115         int ret;
116
117         if (!pmd->mask)
118                 return -EINVAL;
119
120         ret = jh7110_pmu_get_state(pmd, &is_on);
121         if (ret)
122                 dev_info(pmu->pdev, "unable to get current state for %s\n",
123                                 pmd->genpd.name);
124         if (is_on == on) {
125                 dev_info(pmu->pdev, "pm domain [%s] is already %sable status.\n",
126                                 pmd->genpd.name, on ? "en" : "dis");
127                 return 0;
128         }
129
130         spin_lock_irqsave(&pmu->lock, flags);
131
132         if (on) {
133                 mode = SW_TURN_ON_POWER_MODE;
134                 encourage_lo = SW_MODE_ENCOURAGE_EN_LO;
135                 encourage_hi = SW_MODE_ENCOURAGE_EN_HI;
136         } else {
137                 mode = SW_TURN_OFF_POWER_MODE;
138                 encourage_lo = SW_MODE_ENCOURAGE_DIS_LO;
139                 encourage_hi = SW_MODE_ENCOURAGE_DIS_HI;
140         }
141
142         __raw_writel(pmd->mask, pmu->base + mode);
143
144         /* write SW_ENCOURAGE to make the configuration take effect */
145         __raw_writel(SW_MODE_ENCOURAGE_ON, pmu->base + SW_ENCOURAGE);
146         __raw_writel(encourage_lo, pmu->base + SW_ENCOURAGE);
147         __raw_writel(encourage_hi, pmu->base + SW_ENCOURAGE);
148
149         spin_unlock_irqrestore(&pmu->lock, flags);
150
151         if (on) {
152                 ret = readl_poll_timeout_atomic(pmu->base + CURR_POWER_MODE, val,
153                                                 val & pmd->mask, DELAY_US,
154                                                 TIMEOUT_US);
155                 if (ret) {
156                         dev_err(pmu->pdev, "%s power_on failed", pmd->genpd.name);
157                         return -ETIMEDOUT;
158                 }
159         } else {
160                 ret = readl_poll_timeout_atomic(pmu->base + CURR_POWER_MODE, val,
161                                                 !(val & pmd->mask), DELAY_US,
162                                                 TIMEOUT_US);
163                 if (ret) {
164                         dev_err(pmu->pdev, "%s power_off failed", pmd->genpd.name);
165                         return -ETIMEDOUT;
166                 }
167         }
168
169         return 0;
170 }
171
172 static int jh7110_pmu_on(struct generic_pm_domain *genpd)
173 {
174         struct jh7110_power_dev *pmd = container_of(genpd,
175                 struct jh7110_power_dev, genpd);
176
177         return jh7110_pmu_set_state(pmd, true);
178 }
179
180 static int jh7110_pmu_off(struct generic_pm_domain *genpd)
181 {
182         struct jh7110_power_dev *pmd = container_of(genpd,
183                 struct jh7110_power_dev, genpd);
184
185         return jh7110_pmu_set_state(pmd, false);
186 }
187
188 static void starfive_pmu_int_enable(struct jh7110_pmu *pmu, u32 mask, bool enable)
189 {
190         u32 val = __raw_readl(pmu->base + PMU_INT_MASK);
191
192         if (enable)
193                 val &= ~mask;
194         else
195                 val |= mask;
196
197         __raw_writel(val, pmu->base + PMU_INT_MASK);
198 }
199
200 static irqreturn_t starfive_pmu_interrupt(int irq, void *data)
201 {
202         struct jh7110_pmu *pmu = data;
203         unsigned long flags;
204         u32 val;
205
206         spin_lock_irqsave(&pmu->lock, flags);
207         val = __raw_readl(pmu->base + PMU_INT_STATUS);
208
209         if (val & PMU_INT_SEQ_DONE)
210                 dev_dbg(pmu->pdev, "sequence done.\n");
211         if (val & PMU_INT_HW_REQ)
212                 dev_dbg(pmu->pdev, "hardware encourage requestion.\n");
213         if (val & PMU_INT_SW_FAIL)
214                 dev_err(pmu->pdev, "software encourage fail.\n");
215         if (val & PMU_INT_HW_FAIL)
216                 dev_err(pmu->pdev, "hardware encourage fail.\n");
217         if (val & PMU_INT_PCH_FAIL)
218                 dev_err(pmu->pdev, "p-channel fail event.\n");
219
220         /* clear interrupts */
221         __raw_writel(val, pmu->base + PMU_INT_STATUS);
222         __raw_writel(val, pmu->base + PMU_EVENT_STATUS);
223
224         spin_unlock_irqrestore(&pmu->lock, flags);
225
226         return IRQ_HANDLED;
227 }
228
229 static int jh7110_pmu_probe(struct platform_device *pdev)
230 {
231         struct device *dev = &pdev->dev;
232         struct device_node *np = dev->of_node;
233         struct resource *res;
234         const struct jh7110_pmu_data *entry, *table;
235         struct jh7110_pmu *pmu;
236         unsigned int i;
237         uint8_t max_bit = 0;
238         int ret;
239
240         pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL);
241         if (!pmu)
242                 return -ENOMEM;
243
244         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
245         pmu_base = pmu->base = devm_ioremap_resource(&pdev->dev, res);
246         if (IS_ERR(pmu->base))
247                 return PTR_ERR(pmu->base);
248
249         /* initialize pmu interrupt  */
250         pmu->irq = platform_get_irq(pdev, 0);
251         if (pmu->irq < 0)
252                 return pmu->irq;
253
254         ret = devm_request_irq(dev, pmu->irq, starfive_pmu_interrupt,
255                                0, pdev->name, pmu);
256         if (ret)
257                 dev_err(dev, "request irq failed.\n");
258
259         table = of_device_get_match_data(dev);
260         if (!table)
261                 return -EINVAL;
262
263         pmu->pdev = dev;
264         pmu->genpd_data.num_domains = 0;
265         i = 0;
266         for (entry = table; entry->name; entry++) {
267                 max_bit = max(max_bit, entry->bit);
268                 i++;
269         }
270
271         if (!i)
272                 return -ENODEV;
273
274         pmu->genpd_data.num_domains = max_bit + 1;
275
276         pmu->dev = devm_kcalloc(dev, pmu->genpd_data.num_domains,
277                                   sizeof(struct jh7110_power_dev),
278                                   GFP_KERNEL);
279         if (!pmu->dev)
280                 return -ENOMEM;
281
282         pmu->genpd = devm_kcalloc(dev, pmu->genpd_data.num_domains,
283                                     sizeof(struct generic_pm_domain *),
284                                     GFP_KERNEL);
285         if (!pmu->genpd)
286                 return -ENOMEM;
287
288         pmu->genpd_data.domains = pmu->genpd;
289
290         i = 0;
291         for (entry = table; entry->name; entry++) {
292                 struct jh7110_power_dev *pmd = &pmu->dev[i];
293                 bool is_on;
294
295                 pmd->power = pmu;
296                 pmd->mask = BIT(entry->bit);
297                 pmd->genpd.name = entry->name;
298                 pmd->genpd.flags = entry->flags;
299
300                 ret = jh7110_pmu_get_state(pmd, &is_on);
301                 if (ret)
302                         dev_warn(dev, "unable to get current state for %s\n",
303                                  pmd->genpd.name);
304
305                 pmd->genpd.power_on = jh7110_pmu_on;
306                 pmd->genpd.power_off = jh7110_pmu_off;
307
308                 pm_genpd_init(&pmd->genpd, NULL, !is_on);
309                 pmu->genpd[entry->bit] = &pmd->genpd;
310
311                 i++;
312         }
313
314         spin_lock_init(&pmu->lock);
315         starfive_pmu_int_enable(pmu, PMU_INT_ALL_MASK & ~PMU_INT_PCH_FAIL, true);
316
317         ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data);
318         if (ret) {
319                 dev_err(dev, "failed to register genpd driver: %d\n", ret);
320                 return ret;
321         }
322
323         dev_info(dev, "registered %u power domains\n", i);
324
325         return 0;
326 }
327
328 static const struct jh7110_pmu_data jh7110_power_domains[] = {
329         {
330                 .name = "SYSTOP",
331                 .bit = JH7110_PD_SYSTOP,
332                 .flags = GENPD_FLAG_ALWAYS_ON,
333         }, {
334                 .name = "CPU",
335                 .bit = JH7110_PD_CPU,
336                 .flags = GENPD_FLAG_ALWAYS_ON,
337         }, {
338                 .name = "GPUA",
339                 .bit = JH7110_PD_GPUA,
340         }, {
341                 .name = "VDEC",
342                 .bit = JH7110_PD_VDEC,
343         }, {
344                 .name = "VOUT",
345                 .bit = JH7110_PD_VOUT,
346         }, {
347                 .name = "ISP",
348                 .bit = JH7110_PD_ISP,
349         }, {
350                 .name = "VENC",
351                 .bit = JH7110_PD_VENC,
352         }, {
353                 .name = "GPUB",
354                 .bit = JH7110_PD_GPUB,
355         }, {
356                 /* sentinel */
357         },
358 };
359
360 static const struct of_device_id jh7110_pmu_of_match[] = {
361         {
362                 .compatible = "starfive,jh7110-pmu",
363                 .data = &jh7110_power_domains,
364         }, {
365                 /* sentinel */
366         }
367 };
368
369 static struct platform_driver jh7110_pmu_driver = {
370         .driver = {
371                 .name = "jh7110-pmu",
372                 .of_match_table = jh7110_pmu_of_match,
373         },
374         .probe  = jh7110_pmu_probe,
375 };
376 builtin_platform_driver(jh7110_pmu_driver);
377
378 MODULE_AUTHOR("Walker Chen <walker.chen@linux.starfivetech.com>");
379 MODULE_DESCRIPTION("Starfive JH7110 Power Domain Driver");
380 MODULE_LICENSE("GPL");