1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2015 Linaro Ltd.
4 *
5 * Author: Jun Nie <jun.nie@linaro.org>
6 */
7#include <linux/delay.h>
8#include <linux/err.h>
9#include <linux/io.h>
10#include <linux/of.h>
11#include <linux/platform_device.h>
12#include <linux/pm_domain.h>
13#include <linux/slab.h>
14
15#define PCU_DM_CLKEN        0x18
16#define PCU_DM_RSTEN        0x1C
17#define PCU_DM_ISOEN        0x20
18#define PCU_DM_PWRDN        0x24
19#define PCU_DM_ACK_SYNC     0x28
20
21enum {
22	PCU_DM_NEON0 = 0,
23	PCU_DM_NEON1,
24	PCU_DM_GPU,
25	PCU_DM_DECPPU,
26	PCU_DM_VOU,
27	PCU_DM_R2D,
28	PCU_DM_TOP,
29};
30
31static void __iomem *pcubase;
32
33struct zx_pm_domain {
34	struct generic_pm_domain dm;
35	unsigned int bit;
36};
37
38static int normal_power_off(struct generic_pm_domain *domain)
39{
40	struct zx_pm_domain *zpd = (struct zx_pm_domain *)domain;
41	unsigned long loop = 1000;
42	u32 tmp;
43
44	tmp = readl_relaxed(pcubase + PCU_DM_CLKEN);
45	tmp &= ~BIT(zpd->bit);
46	writel_relaxed(tmp, pcubase + PCU_DM_CLKEN);
47	udelay(5);
48
49	tmp = readl_relaxed(pcubase + PCU_DM_ISOEN);
50	tmp &= ~BIT(zpd->bit);
51	writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_ISOEN);
52	udelay(5);
53
54	tmp = readl_relaxed(pcubase + PCU_DM_RSTEN);
55	tmp &= ~BIT(zpd->bit);
56	writel_relaxed(tmp, pcubase + PCU_DM_RSTEN);
57	udelay(5);
58
59	tmp = readl_relaxed(pcubase + PCU_DM_PWRDN);
60	tmp &= ~BIT(zpd->bit);
61	writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_PWRDN);
62	do {
63		tmp = readl_relaxed(pcubase + PCU_DM_ACK_SYNC) & BIT(zpd->bit);
64	} while (--loop && !tmp);
65
66	if (!loop) {
67		pr_err("Error: %s %s fail\n", __func__, domain->name);
68		return -EIO;
69	}
70
71	return 0;
72}
73
74static int normal_power_on(struct generic_pm_domain *domain)
75{
76	struct zx_pm_domain *zpd = (struct zx_pm_domain *)domain;
77	unsigned long loop = 10000;
78	u32 tmp;
79
80	tmp = readl_relaxed(pcubase + PCU_DM_PWRDN);
81	tmp &= ~BIT(zpd->bit);
82	writel_relaxed(tmp, pcubase + PCU_DM_PWRDN);
83	do {
84		tmp = readl_relaxed(pcubase + PCU_DM_ACK_SYNC) & BIT(zpd->bit);
85	} while (--loop && tmp);
86
87	if (!loop) {
88		pr_err("Error: %s %s fail\n", __func__, domain->name);
89		return -EIO;
90	}
91
92	tmp = readl_relaxed(pcubase + PCU_DM_RSTEN);
93	tmp &= ~BIT(zpd->bit);
94	writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_RSTEN);
95	udelay(5);
96
97	tmp = readl_relaxed(pcubase + PCU_DM_ISOEN);
98	tmp &= ~BIT(zpd->bit);
99	writel_relaxed(tmp, pcubase + PCU_DM_ISOEN);
100	udelay(5);
101
102	tmp = readl_relaxed(pcubase + PCU_DM_CLKEN);
103	tmp &= ~BIT(zpd->bit);
104	writel_relaxed(tmp | BIT(zpd->bit), pcubase + PCU_DM_CLKEN);
105	udelay(5);
106	return 0;
107}
108
109static struct zx_pm_domain gpu_domain = {
110	.dm = {
111		.name		= "gpu_domain",
112		.power_off	= normal_power_off,
113		.power_on	= normal_power_on,
114	},
115	.bit = PCU_DM_GPU,
116};
117
118static struct zx_pm_domain decppu_domain = {
119	.dm = {
120		.name		= "decppu_domain",
121		.power_off	= normal_power_off,
122		.power_on	= normal_power_on,
123	},
124	.bit = PCU_DM_DECPPU,
125};
126
127static struct zx_pm_domain vou_domain = {
128	.dm = {
129		.name		= "vou_domain",
130		.power_off	= normal_power_off,
131		.power_on	= normal_power_on,
132	},
133	.bit = PCU_DM_VOU,
134};
135
136static struct zx_pm_domain r2d_domain = {
137	.dm = {
138		.name		= "r2d_domain",
139		.power_off	= normal_power_off,
140		.power_on	= normal_power_on,
141	},
142	.bit = PCU_DM_R2D,
143};
144
145static struct generic_pm_domain *zx296702_pm_domains[] = {
146	&vou_domain.dm,
147	&gpu_domain.dm,
148	&decppu_domain.dm,
149	&r2d_domain.dm,
150};
151
152static int zx296702_pd_probe(struct platform_device *pdev)
153{
154	struct genpd_onecell_data *genpd_data;
155	struct resource *res;
156	int i;
157
158	genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL);
159	if (!genpd_data)
160		return -ENOMEM;
161
162	genpd_data->domains = zx296702_pm_domains;
163	genpd_data->num_domains = ARRAY_SIZE(zx296702_pm_domains);
164
165	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
166	if (!res) {
167		dev_err(&pdev->dev, "no memory resource defined\n");
168		return -ENODEV;
169	}
170
171	pcubase = devm_ioremap_resource(&pdev->dev, res);
172	if (IS_ERR(pcubase)) {
173		dev_err(&pdev->dev, "ioremap fail.\n");
174		return -EIO;
175	}
176
177	for (i = 0; i < ARRAY_SIZE(zx296702_pm_domains); ++i)
178		pm_genpd_init(zx296702_pm_domains[i], NULL, false);
179
180	of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data);
181	return 0;
182}
183
184static const struct of_device_id zx296702_pm_domain_matches[] __initconst = {
185	{ .compatible = "zte,zx296702-pcu", },
186	{ },
187};
188
189static struct platform_driver zx296702_pd_driver __initdata = {
190	.driver = {
191		.name = "zx-powerdomain",
192		.owner = THIS_MODULE,
193		.of_match_table = zx296702_pm_domain_matches,
194	},
195	.probe = zx296702_pd_probe,
196};
197
198static int __init zx296702_pd_init(void)
199{
200	return platform_driver_register(&zx296702_pd_driver);
201}
202subsys_initcall(zx296702_pd_init);
203