Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
0004 //      http://www.samsung.com/
0005 //
0006 // Exynos - CPU PMU(Power Management Unit) support
0007 
0008 #include <linux/of.h>
0009 #include <linux/of_address.h>
0010 #include <linux/of_device.h>
0011 #include <linux/mfd/core.h>
0012 #include <linux/mfd/syscon.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/delay.h>
0015 
0016 #include <linux/soc/samsung/exynos-regs-pmu.h>
0017 #include <linux/soc/samsung/exynos-pmu.h>
0018 
0019 #include "exynos-pmu.h"
0020 
0021 struct exynos_pmu_context {
0022     struct device *dev;
0023     const struct exynos_pmu_data *pmu_data;
0024 };
0025 
0026 void __iomem *pmu_base_addr;
0027 static struct exynos_pmu_context *pmu_context;
0028 
0029 void pmu_raw_writel(u32 val, u32 offset)
0030 {
0031     writel_relaxed(val, pmu_base_addr + offset);
0032 }
0033 
0034 u32 pmu_raw_readl(u32 offset)
0035 {
0036     return readl_relaxed(pmu_base_addr + offset);
0037 }
0038 
0039 void exynos_sys_powerdown_conf(enum sys_powerdown mode)
0040 {
0041     unsigned int i;
0042     const struct exynos_pmu_data *pmu_data;
0043 
0044     if (!pmu_context || !pmu_context->pmu_data)
0045         return;
0046 
0047     pmu_data = pmu_context->pmu_data;
0048 
0049     if (pmu_data->powerdown_conf)
0050         pmu_data->powerdown_conf(mode);
0051 
0052     if (pmu_data->pmu_config) {
0053         for (i = 0; (pmu_data->pmu_config[i].offset != PMU_TABLE_END); i++)
0054             pmu_raw_writel(pmu_data->pmu_config[i].val[mode],
0055                     pmu_data->pmu_config[i].offset);
0056     }
0057 
0058     if (pmu_data->powerdown_conf_extra)
0059         pmu_data->powerdown_conf_extra(mode);
0060 }
0061 
0062 /*
0063  * Split the data between ARM architectures because it is relatively big
0064  * and useless on other arch.
0065  */
0066 #ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
0067 #define exynos_pmu_data_arm_ptr(data)   (&data)
0068 #else
0069 #define exynos_pmu_data_arm_ptr(data)   NULL
0070 #endif
0071 
0072 /*
0073  * PMU platform driver and devicetree bindings.
0074  */
0075 static const struct of_device_id exynos_pmu_of_device_ids[] = {
0076     {
0077         .compatible = "samsung,exynos3250-pmu",
0078         .data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data),
0079     }, {
0080         .compatible = "samsung,exynos4210-pmu",
0081         .data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data),
0082     }, {
0083         .compatible = "samsung,exynos4412-pmu",
0084         .data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data),
0085     }, {
0086         .compatible = "samsung,exynos5250-pmu",
0087         .data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data),
0088     }, {
0089         .compatible = "samsung,exynos5410-pmu",
0090     }, {
0091         .compatible = "samsung,exynos5420-pmu",
0092         .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data),
0093     }, {
0094         .compatible = "samsung,exynos5433-pmu",
0095     }, {
0096         .compatible = "samsung,exynos7-pmu",
0097     }, {
0098         .compatible = "samsung,exynos850-pmu",
0099     },
0100     { /*sentinel*/ },
0101 };
0102 
0103 static const struct mfd_cell exynos_pmu_devs[] = {
0104     { .name = "exynos-clkout", },
0105 };
0106 
0107 struct regmap *exynos_get_pmu_regmap(void)
0108 {
0109     struct device_node *np = of_find_matching_node(NULL,
0110                               exynos_pmu_of_device_ids);
0111     if (np)
0112         return syscon_node_to_regmap(np);
0113     return ERR_PTR(-ENODEV);
0114 }
0115 EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap);
0116 
0117 static int exynos_pmu_probe(struct platform_device *pdev)
0118 {
0119     struct device *dev = &pdev->dev;
0120     int ret;
0121 
0122     pmu_base_addr = devm_platform_ioremap_resource(pdev, 0);
0123     if (IS_ERR(pmu_base_addr))
0124         return PTR_ERR(pmu_base_addr);
0125 
0126     pmu_context = devm_kzalloc(&pdev->dev,
0127             sizeof(struct exynos_pmu_context),
0128             GFP_KERNEL);
0129     if (!pmu_context)
0130         return -ENOMEM;
0131     pmu_context->dev = dev;
0132     pmu_context->pmu_data = of_device_get_match_data(dev);
0133 
0134     if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init)
0135         pmu_context->pmu_data->pmu_init();
0136 
0137     platform_set_drvdata(pdev, pmu_context);
0138 
0139     ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, exynos_pmu_devs,
0140                    ARRAY_SIZE(exynos_pmu_devs), NULL, 0, NULL);
0141     if (ret)
0142         return ret;
0143 
0144     if (devm_of_platform_populate(dev))
0145         dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n");
0146 
0147     dev_dbg(dev, "Exynos PMU Driver probe done\n");
0148     return 0;
0149 }
0150 
0151 static struct platform_driver exynos_pmu_driver = {
0152     .driver  = {
0153         .name   = "exynos-pmu",
0154         .of_match_table = exynos_pmu_of_device_ids,
0155     },
0156     .probe = exynos_pmu_probe,
0157 };
0158 
0159 static int __init exynos_pmu_init(void)
0160 {
0161     return platform_driver_register(&exynos_pmu_driver);
0162 
0163 }
0164 postcore_initcall(exynos_pmu_init);