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