162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci// http://www.samsung.com/ 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Exynos - CPU PMU(Power Management Unit) support 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/of_address.h> 1062306a36Sopenharmony_ci#include <linux/mfd/core.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/of_platform.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/soc/samsung/exynos-regs-pmu.h> 1762306a36Sopenharmony_ci#include <linux/soc/samsung/exynos-pmu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "exynos-pmu.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct exynos_pmu_context { 2262306a36Sopenharmony_ci struct device *dev; 2362306a36Sopenharmony_ci const struct exynos_pmu_data *pmu_data; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_civoid __iomem *pmu_base_addr; 2762306a36Sopenharmony_cistatic struct exynos_pmu_context *pmu_context; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civoid pmu_raw_writel(u32 val, u32 offset) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci writel_relaxed(val, pmu_base_addr + offset); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciu32 pmu_raw_readl(u32 offset) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return readl_relaxed(pmu_base_addr + offset); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_civoid exynos_sys_powerdown_conf(enum sys_powerdown mode) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci unsigned int i; 4262306a36Sopenharmony_ci const struct exynos_pmu_data *pmu_data; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (!pmu_context || !pmu_context->pmu_data) 4562306a36Sopenharmony_ci return; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci pmu_data = pmu_context->pmu_data; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (pmu_data->powerdown_conf) 5062306a36Sopenharmony_ci pmu_data->powerdown_conf(mode); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (pmu_data->pmu_config) { 5362306a36Sopenharmony_ci for (i = 0; (pmu_data->pmu_config[i].offset != PMU_TABLE_END); i++) 5462306a36Sopenharmony_ci pmu_raw_writel(pmu_data->pmu_config[i].val[mode], 5562306a36Sopenharmony_ci pmu_data->pmu_config[i].offset); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (pmu_data->powerdown_conf_extra) 5962306a36Sopenharmony_ci pmu_data->powerdown_conf_extra(mode); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (pmu_data->pmu_config_extra) { 6262306a36Sopenharmony_ci for (i = 0; pmu_data->pmu_config_extra[i].offset != PMU_TABLE_END; i++) 6362306a36Sopenharmony_ci pmu_raw_writel(pmu_data->pmu_config_extra[i].val[mode], 6462306a36Sopenharmony_ci pmu_data->pmu_config_extra[i].offset); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * Split the data between ARM architectures because it is relatively big 7062306a36Sopenharmony_ci * and useless on other arch. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS 7362306a36Sopenharmony_ci#define exynos_pmu_data_arm_ptr(data) (&data) 7462306a36Sopenharmony_ci#else 7562306a36Sopenharmony_ci#define exynos_pmu_data_arm_ptr(data) NULL 7662306a36Sopenharmony_ci#endif 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * PMU platform driver and devicetree bindings. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic const struct of_device_id exynos_pmu_of_device_ids[] = { 8262306a36Sopenharmony_ci { 8362306a36Sopenharmony_ci .compatible = "samsung,exynos3250-pmu", 8462306a36Sopenharmony_ci .data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data), 8562306a36Sopenharmony_ci }, { 8662306a36Sopenharmony_ci .compatible = "samsung,exynos4210-pmu", 8762306a36Sopenharmony_ci .data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data), 8862306a36Sopenharmony_ci }, { 8962306a36Sopenharmony_ci .compatible = "samsung,exynos4212-pmu", 9062306a36Sopenharmony_ci .data = exynos_pmu_data_arm_ptr(exynos4212_pmu_data), 9162306a36Sopenharmony_ci }, { 9262306a36Sopenharmony_ci .compatible = "samsung,exynos4412-pmu", 9362306a36Sopenharmony_ci .data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data), 9462306a36Sopenharmony_ci }, { 9562306a36Sopenharmony_ci .compatible = "samsung,exynos5250-pmu", 9662306a36Sopenharmony_ci .data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data), 9762306a36Sopenharmony_ci }, { 9862306a36Sopenharmony_ci .compatible = "samsung,exynos5410-pmu", 9962306a36Sopenharmony_ci }, { 10062306a36Sopenharmony_ci .compatible = "samsung,exynos5420-pmu", 10162306a36Sopenharmony_ci .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data), 10262306a36Sopenharmony_ci }, { 10362306a36Sopenharmony_ci .compatible = "samsung,exynos5433-pmu", 10462306a36Sopenharmony_ci }, { 10562306a36Sopenharmony_ci .compatible = "samsung,exynos7-pmu", 10662306a36Sopenharmony_ci }, { 10762306a36Sopenharmony_ci .compatible = "samsung,exynos850-pmu", 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci { /*sentinel*/ }, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic const struct mfd_cell exynos_pmu_devs[] = { 11362306a36Sopenharmony_ci { .name = "exynos-clkout", }, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct regmap *exynos_get_pmu_regmap(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct device_node *np = of_find_matching_node(NULL, 11962306a36Sopenharmony_ci exynos_pmu_of_device_ids); 12062306a36Sopenharmony_ci if (np) 12162306a36Sopenharmony_ci return syscon_node_to_regmap(np); 12262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(exynos_get_pmu_regmap); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int exynos_pmu_probe(struct platform_device *pdev) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 12962306a36Sopenharmony_ci int ret; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci pmu_base_addr = devm_platform_ioremap_resource(pdev, 0); 13262306a36Sopenharmony_ci if (IS_ERR(pmu_base_addr)) 13362306a36Sopenharmony_ci return PTR_ERR(pmu_base_addr); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci pmu_context = devm_kzalloc(&pdev->dev, 13662306a36Sopenharmony_ci sizeof(struct exynos_pmu_context), 13762306a36Sopenharmony_ci GFP_KERNEL); 13862306a36Sopenharmony_ci if (!pmu_context) 13962306a36Sopenharmony_ci return -ENOMEM; 14062306a36Sopenharmony_ci pmu_context->dev = dev; 14162306a36Sopenharmony_ci pmu_context->pmu_data = of_device_get_match_data(dev); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init) 14462306a36Sopenharmony_ci pmu_context->pmu_data->pmu_init(); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci platform_set_drvdata(pdev, pmu_context); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, exynos_pmu_devs, 14962306a36Sopenharmony_ci ARRAY_SIZE(exynos_pmu_devs), NULL, 0, NULL); 15062306a36Sopenharmony_ci if (ret) 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (devm_of_platform_populate(dev)) 15462306a36Sopenharmony_ci dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dev_dbg(dev, "Exynos PMU Driver probe done\n"); 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic struct platform_driver exynos_pmu_driver = { 16162306a36Sopenharmony_ci .driver = { 16262306a36Sopenharmony_ci .name = "exynos-pmu", 16362306a36Sopenharmony_ci .of_match_table = exynos_pmu_of_device_ids, 16462306a36Sopenharmony_ci }, 16562306a36Sopenharmony_ci .probe = exynos_pmu_probe, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int __init exynos_pmu_init(void) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return platform_driver_register(&exynos_pmu_driver); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_cipostcore_initcall(exynos_pmu_init); 174