18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
48c2ecf20Sopenharmony_ci//		http://www.samsung.com/
58c2ecf20Sopenharmony_ci//
68c2ecf20Sopenharmony_ci// Exynos - CPU PMU(Power Management Unit) support
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/of.h>
98c2ecf20Sopenharmony_ci#include <linux/of_address.h>
108c2ecf20Sopenharmony_ci#include <linux/of_device.h>
118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/soc/samsung/exynos-regs-pmu.h>
168c2ecf20Sopenharmony_ci#include <linux/soc/samsung/exynos-pmu.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "exynos-pmu.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct exynos_pmu_context {
218c2ecf20Sopenharmony_ci	struct device *dev;
228c2ecf20Sopenharmony_ci	const struct exynos_pmu_data *pmu_data;
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_civoid __iomem *pmu_base_addr;
268c2ecf20Sopenharmony_cistatic struct exynos_pmu_context *pmu_context;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_civoid pmu_raw_writel(u32 val, u32 offset)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	writel_relaxed(val, pmu_base_addr + offset);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciu32 pmu_raw_readl(u32 offset)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	return readl_relaxed(pmu_base_addr + offset);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_civoid exynos_sys_powerdown_conf(enum sys_powerdown mode)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned int i;
418c2ecf20Sopenharmony_ci	const struct exynos_pmu_data *pmu_data;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (!pmu_context || !pmu_context->pmu_data)
448c2ecf20Sopenharmony_ci		return;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	pmu_data = pmu_context->pmu_data;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (pmu_data->powerdown_conf)
498c2ecf20Sopenharmony_ci		pmu_data->powerdown_conf(mode);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (pmu_data->pmu_config) {
528c2ecf20Sopenharmony_ci		for (i = 0; (pmu_data->pmu_config[i].offset != PMU_TABLE_END); i++)
538c2ecf20Sopenharmony_ci			pmu_raw_writel(pmu_data->pmu_config[i].val[mode],
548c2ecf20Sopenharmony_ci					pmu_data->pmu_config[i].offset);
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (pmu_data->powerdown_conf_extra)
588c2ecf20Sopenharmony_ci		pmu_data->powerdown_conf_extra(mode);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * Split the data between ARM architectures because it is relatively big
638c2ecf20Sopenharmony_ci * and useless on other arch.
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ci#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
668c2ecf20Sopenharmony_ci#define exynos_pmu_data_arm_ptr(data)	(&data)
678c2ecf20Sopenharmony_ci#else
688c2ecf20Sopenharmony_ci#define exynos_pmu_data_arm_ptr(data)	NULL
698c2ecf20Sopenharmony_ci#endif
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/*
728c2ecf20Sopenharmony_ci * PMU platform driver and devicetree bindings.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_pmu_of_device_ids[] = {
758c2ecf20Sopenharmony_ci	{
768c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos3250-pmu",
778c2ecf20Sopenharmony_ci		.data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data),
788c2ecf20Sopenharmony_ci	}, {
798c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos4210-pmu",
808c2ecf20Sopenharmony_ci		.data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data),
818c2ecf20Sopenharmony_ci	}, {
828c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos4412-pmu",
838c2ecf20Sopenharmony_ci		.data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data),
848c2ecf20Sopenharmony_ci	}, {
858c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos5250-pmu",
868c2ecf20Sopenharmony_ci		.data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data),
878c2ecf20Sopenharmony_ci	}, {
888c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos5410-pmu",
898c2ecf20Sopenharmony_ci	}, {
908c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos5420-pmu",
918c2ecf20Sopenharmony_ci		.data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data),
928c2ecf20Sopenharmony_ci	}, {
938c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos5433-pmu",
948c2ecf20Sopenharmony_ci	}, {
958c2ecf20Sopenharmony_ci		.compatible = "samsung,exynos7-pmu",
968c2ecf20Sopenharmony_ci	},
978c2ecf20Sopenharmony_ci	{ /*sentinel*/ },
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistruct regmap *exynos_get_pmu_regmap(void)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct device_node *np = of_find_matching_node(NULL,
1038c2ecf20Sopenharmony_ci						      exynos_pmu_of_device_ids);
1048c2ecf20Sopenharmony_ci	if (np)
1058c2ecf20Sopenharmony_ci		return syscon_node_to_regmap(np);
1068c2ecf20Sopenharmony_ci	return ERR_PTR(-ENODEV);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(exynos_get_pmu_regmap);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int exynos_pmu_probe(struct platform_device *pdev)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	pmu_base_addr = devm_platform_ioremap_resource(pdev, 0);
1158c2ecf20Sopenharmony_ci	if (IS_ERR(pmu_base_addr))
1168c2ecf20Sopenharmony_ci		return PTR_ERR(pmu_base_addr);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	pmu_context = devm_kzalloc(&pdev->dev,
1198c2ecf20Sopenharmony_ci			sizeof(struct exynos_pmu_context),
1208c2ecf20Sopenharmony_ci			GFP_KERNEL);
1218c2ecf20Sopenharmony_ci	if (!pmu_context)
1228c2ecf20Sopenharmony_ci		return -ENOMEM;
1238c2ecf20Sopenharmony_ci	pmu_context->dev = dev;
1248c2ecf20Sopenharmony_ci	pmu_context->pmu_data = of_device_get_match_data(dev);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init)
1278c2ecf20Sopenharmony_ci		pmu_context->pmu_data->pmu_init();
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pmu_context);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (devm_of_platform_populate(dev))
1328c2ecf20Sopenharmony_ci		dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n");
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	dev_dbg(dev, "Exynos PMU Driver probe done\n");
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic struct platform_driver exynos_pmu_driver = {
1398c2ecf20Sopenharmony_ci	.driver  = {
1408c2ecf20Sopenharmony_ci		.name   = "exynos-pmu",
1418c2ecf20Sopenharmony_ci		.of_match_table = exynos_pmu_of_device_ids,
1428c2ecf20Sopenharmony_ci	},
1438c2ecf20Sopenharmony_ci	.probe = exynos_pmu_probe,
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int __init exynos_pmu_init(void)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return platform_driver_register(&exynos_pmu_driver);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_cipostcore_initcall(exynos_pmu_init);
152