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