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 
mmp_pm_domain_power_on(struct generic_pm_domain * genpd)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 
mmp_pm_domain_power_off(struct generic_pm_domain * genpd)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 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 
mmp_pm_domain_register(const char * name,void __iomem * reg,u32 power_on,u32 reset,u32 clock_enable,unsigned int flags,spinlock_t * lock)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 }
116