18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 ZTE Ltd.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Baoyou Xie <baoyou.xie@linaro.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "zx2967_pm_domains.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define PCU_DM_CLKEN(zpd)	((zpd)->reg_offset[REG_CLKEN])
168c2ecf20Sopenharmony_ci#define PCU_DM_ISOEN(zpd)	((zpd)->reg_offset[REG_ISOEN])
178c2ecf20Sopenharmony_ci#define PCU_DM_RSTEN(zpd)	((zpd)->reg_offset[REG_RSTEN])
188c2ecf20Sopenharmony_ci#define PCU_DM_PWREN(zpd)	((zpd)->reg_offset[REG_PWREN])
198c2ecf20Sopenharmony_ci#define PCU_DM_ACK_SYNC(zpd)	((zpd)->reg_offset[REG_ACK_SYNC])
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void __iomem *pcubase;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int zx2967_power_on(struct generic_pm_domain *domain)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
268c2ecf20Sopenharmony_ci	unsigned long loop = 1000;
278c2ecf20Sopenharmony_ci	u32 val;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
308c2ecf20Sopenharmony_ci	if (zpd->polarity == PWREN)
318c2ecf20Sopenharmony_ci		val |= BIT(zpd->bit);
328c2ecf20Sopenharmony_ci	else
338c2ecf20Sopenharmony_ci		val &= ~BIT(zpd->bit);
348c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	do {
378c2ecf20Sopenharmony_ci		udelay(1);
388c2ecf20Sopenharmony_ci		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
398c2ecf20Sopenharmony_ci				   & BIT(zpd->bit);
408c2ecf20Sopenharmony_ci	} while (--loop && !val);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (!loop) {
438c2ecf20Sopenharmony_ci		pr_err("Error: %s %s fail\n", __func__, domain->name);
448c2ecf20Sopenharmony_ci		return -EIO;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
488c2ecf20Sopenharmony_ci	val |= BIT(zpd->bit);
498c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
508c2ecf20Sopenharmony_ci	udelay(5);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
538c2ecf20Sopenharmony_ci	val &= ~BIT(zpd->bit);
548c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
558c2ecf20Sopenharmony_ci	udelay(5);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
588c2ecf20Sopenharmony_ci	val |= BIT(zpd->bit);
598c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
608c2ecf20Sopenharmony_ci	udelay(5);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	pr_debug("poweron %s\n", domain->name);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int zx2967_power_off(struct generic_pm_domain *domain)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
708c2ecf20Sopenharmony_ci	unsigned long loop = 1000;
718c2ecf20Sopenharmony_ci	u32 val;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
748c2ecf20Sopenharmony_ci	val &= ~BIT(zpd->bit);
758c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
768c2ecf20Sopenharmony_ci	udelay(5);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
798c2ecf20Sopenharmony_ci	val |= BIT(zpd->bit);
808c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
818c2ecf20Sopenharmony_ci	udelay(5);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
848c2ecf20Sopenharmony_ci	val &= ~BIT(zpd->bit);
858c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
868c2ecf20Sopenharmony_ci	udelay(5);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
898c2ecf20Sopenharmony_ci	if (zpd->polarity == PWREN)
908c2ecf20Sopenharmony_ci		val &= ~BIT(zpd->bit);
918c2ecf20Sopenharmony_ci	else
928c2ecf20Sopenharmony_ci		val |= BIT(zpd->bit);
938c2ecf20Sopenharmony_ci	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	do {
968c2ecf20Sopenharmony_ci		udelay(1);
978c2ecf20Sopenharmony_ci		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
988c2ecf20Sopenharmony_ci				   & BIT(zpd->bit);
998c2ecf20Sopenharmony_ci	} while (--loop && val);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (!loop) {
1028c2ecf20Sopenharmony_ci		pr_err("Error: %s %s fail\n", __func__, domain->name);
1038c2ecf20Sopenharmony_ci		return -EIO;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	pr_debug("poweroff %s\n", domain->name);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciint zx2967_pd_probe(struct platform_device *pdev,
1128c2ecf20Sopenharmony_ci		    struct generic_pm_domain **zx_pm_domains,
1138c2ecf20Sopenharmony_ci		    int domain_num)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct genpd_onecell_data *genpd_data;
1168c2ecf20Sopenharmony_ci	struct resource *res;
1178c2ecf20Sopenharmony_ci	int i;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL);
1208c2ecf20Sopenharmony_ci	if (!genpd_data)
1218c2ecf20Sopenharmony_ci		return -ENOMEM;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	genpd_data->domains = zx_pm_domains;
1248c2ecf20Sopenharmony_ci	genpd_data->num_domains = domain_num;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1278c2ecf20Sopenharmony_ci	pcubase = devm_ioremap_resource(&pdev->dev, res);
1288c2ecf20Sopenharmony_ci	if (IS_ERR(pcubase))
1298c2ecf20Sopenharmony_ci		return PTR_ERR(pcubase);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	for (i = 0; i < domain_num; ++i) {
1328c2ecf20Sopenharmony_ci		zx_pm_domains[i]->power_on = zx2967_power_on;
1338c2ecf20Sopenharmony_ci		zx_pm_domains[i]->power_off = zx2967_power_off;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		pm_genpd_init(zx_pm_domains[i], NULL, false);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data);
1398c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "powerdomain init ok\n");
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
142