summaryrefslogtreecommitdiffstats
path: root/drivers/soc/zte/zx2967_pm_domains.c
blob: a4503e31b616c70ff9e05338069dc738e41e417d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2017 ZTE Ltd.
 *
 * Author: Baoyou Xie <baoyou.xie@linaro.org>
 */

#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>

#include "zx2967_pm_domains.h"

#define PCU_DM_CLKEN(zpd)	((zpd)->reg_offset[REG_CLKEN])
#define PCU_DM_ISOEN(zpd)	((zpd)->reg_offset[REG_ISOEN])
#define PCU_DM_RSTEN(zpd)	((zpd)->reg_offset[REG_RSTEN])
#define PCU_DM_PWREN(zpd)	((zpd)->reg_offset[REG_PWREN])
#define PCU_DM_ACK_SYNC(zpd)	((zpd)->reg_offset[REG_ACK_SYNC])

static void __iomem *pcubase;

static int zx2967_power_on(struct generic_pm_domain *domain)
{
	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
	unsigned long loop = 1000;
	u32 val;

	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
	if (zpd->polarity == PWREN)
		val |= BIT(zpd->bit);
	else
		val &= ~BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));

	do {
		udelay(1);
		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
				   & BIT(zpd->bit);
	} while (--loop && !val);

	if (!loop) {
		pr_err("Error: %s %s fail\n", __func__, domain->name);
		return -EIO;
	}

	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
	val |= BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
	udelay(5);

	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
	val &= ~BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
	udelay(5);

	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
	val |= BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
	udelay(5);

	pr_debug("poweron %s\n", domain->name);

	return 0;
}

static int zx2967_power_off(struct generic_pm_domain *domain)
{
	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
	unsigned long loop = 1000;
	u32 val;

	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
	val &= ~BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
	udelay(5);

	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
	val |= BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
	udelay(5);

	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
	val &= ~BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
	udelay(5);

	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
	if (zpd->polarity == PWREN)
		val &= ~BIT(zpd->bit);
	else
		val |= BIT(zpd->bit);
	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));

	do {
		udelay(1);
		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
				   & BIT(zpd->bit);
	} while (--loop && val);

	if (!loop) {
		pr_err("Error: %s %s fail\n", __func__, domain->name);
		return -EIO;
	}

	pr_debug("poweroff %s\n", domain->name);

	return 0;
}

int zx2967_pd_probe(struct platform_device *pdev,
		    struct generic_pm_domain **zx_pm_domains,
		    int domain_num)
{
	struct genpd_onecell_data *genpd_data;
	struct resource *res;
	int i;

	genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL);
	if (!genpd_data)
		return -ENOMEM;

	genpd_data->domains = zx_pm_domains;
	genpd_data->num_domains = domain_num;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	pcubase = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(pcubase))
		return PTR_ERR(pcubase);

	for (i = 0; i < domain_num; ++i) {
		zx_pm_domains[i]->power_on = zx2967_power_on;
		zx_pm_domains[i]->power_off = zx2967_power_off;

		pm_genpd_init(zx_pm_domains[i], NULL, false);
	}

	of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data);
	dev_info(&pdev->dev, "powerdomain init ok\n");
	return 0;
}