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(addr: pm_domain->reg); |
36 | |
37 | /* Turn on the power island */ |
38 | val |= pm_domain->power_on; |
39 | writel(val, addr: pm_domain->reg); |
40 | |
41 | /* Disable isolation */ |
42 | val |= 0x100; |
43 | writel(val, addr: 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, addr: pm_domain->reg); |
51 | |
52 | val |= pm_domain->clock_enable; |
53 | writel(val, addr: pm_domain->reg); |
54 | |
55 | val |= pm_domain->reset; |
56 | writel(val, addr: pm_domain->reg); |
57 | |
58 | writel(val: after_power_on, addr: pm_domain->reg); |
59 | } |
60 | |
61 | if (pm_domain->lock) |
62 | spin_unlock_irqrestore(lock: 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(addr: pm_domain->reg); |
81 | val &= ~pm_domain->power_on; |
82 | val &= ~0x100; |
83 | writel(val, addr: pm_domain->reg); |
84 | |
85 | if (pm_domain->lock) |
86 | spin_unlock_irqrestore(lock: 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(size: sizeof(*pm_domain), GFP_KERNEL); |
99 | if (!pm_domain) |
100 | return ERR_PTR(error: -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(genpd: &pm_domain->genpd, NULL, is_off: 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 | } |
116 | |