Merge branches 'clk-range', 'clk-uniphier', 'clk-apple' and 'clk-qcom' into clk-next
[linux-2.6-microblaze.git] / drivers / clk / mmp / pwr-island.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * MMP PMU power island support
4  *
5  * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
6  */
7
8 #include <linux/pm_domain.h>
9 #include <linux/slab.h>
10 #include <linux/io.h>
11
12 #include "clk.h"
13
14 #define to_mmp_pm_domain(genpd) container_of(genpd, struct mmp_pm_domain, genpd)
15
16 struct mmp_pm_domain {
17         struct generic_pm_domain genpd;
18         void __iomem *reg;
19         spinlock_t *lock;
20         u32 power_on;
21         u32 reset;
22         u32 clock_enable;
23         unsigned int flags;
24 };
25
26 static int mmp_pm_domain_power_on(struct generic_pm_domain *genpd)
27 {
28         struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd);
29         unsigned long flags = 0;
30         u32 val;
31
32         if (pm_domain->lock)
33                 spin_lock_irqsave(pm_domain->lock, flags);
34
35         val = readl(pm_domain->reg);
36
37         /* Turn on the power island */
38         val |= pm_domain->power_on;
39         writel(val, pm_domain->reg);
40
41         /* Disable isolation */
42         val |= 0x100;
43         writel(val, pm_domain->reg);
44
45         /* Some blocks need to be reset after a power up */
46         if (pm_domain->reset || pm_domain->clock_enable) {
47                 u32 after_power_on = val;
48
49                 val &= ~pm_domain->reset;
50                 writel(val, pm_domain->reg);
51
52                 val |= pm_domain->clock_enable;
53                 writel(val, pm_domain->reg);
54
55                 val |= pm_domain->reset;
56                 writel(val, pm_domain->reg);
57
58                 writel(after_power_on, pm_domain->reg);
59         }
60
61         if (pm_domain->lock)
62                 spin_unlock_irqrestore(pm_domain->lock, flags);
63
64         return 0;
65 }
66
67 static int mmp_pm_domain_power_off(struct generic_pm_domain *genpd)
68 {
69         struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd);
70         unsigned long flags = 0;
71         u32 val;
72
73         if (pm_domain->flags & MMP_PM_DOMAIN_NO_DISABLE)
74                 return 0;
75
76         if (pm_domain->lock)
77                 spin_lock_irqsave(pm_domain->lock, flags);
78
79         /* Turn off and isolate the power island. */
80         val = readl(pm_domain->reg);
81         val &= ~pm_domain->power_on;
82         val &= ~0x100;
83         writel(val, pm_domain->reg);
84
85         if (pm_domain->lock)
86                 spin_unlock_irqrestore(pm_domain->lock, flags);
87
88         return 0;
89 }
90
91 struct generic_pm_domain *mmp_pm_domain_register(const char *name,
92                 void __iomem *reg,
93                 u32 power_on, u32 reset, u32 clock_enable,
94                 unsigned int flags, spinlock_t *lock)
95 {
96         struct mmp_pm_domain *pm_domain;
97
98         pm_domain = kzalloc(sizeof(*pm_domain), GFP_KERNEL);
99         if (!pm_domain)
100                 return ERR_PTR(-ENOMEM);
101
102         pm_domain->reg = reg;
103         pm_domain->power_on = power_on;
104         pm_domain->reset = reset;
105         pm_domain->clock_enable = clock_enable;
106         pm_domain->flags = flags;
107         pm_domain->lock = lock;
108
109         pm_genpd_init(&pm_domain->genpd, NULL, true);
110         pm_domain->genpd.name = name;
111         pm_domain->genpd.power_on = mmp_pm_domain_power_on;
112         pm_domain->genpd.power_off = mmp_pm_domain_power_off;
113
114         return &pm_domain->genpd;
115 }