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
16struct 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
26static 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
67static 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
91struct 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