From 34f81f743741efe363e503002e9fbd29fb073ca8 Mon Sep 17 00:00:00 2001 From: Jang JeongHoon Date: Fri, 4 May 2018 11:22:29 +0900 Subject: [PATCH] [COMMON] soc: samsung: Added initial exynos_pmu codes. Change-Id: I018b1403724225c5da3b9cd6c46824e452b23aa1 Signed-off-by: Jang JeongHoon --- drivers/soc/samsung/Kconfig | 10 +- drivers/soc/samsung/Makefile | 2 - drivers/soc/samsung/exynos-pmu.c | 334 +++++++++++++++++++++---------- include/soc/samsung/exynos-pmu.h | 54 +++++ 4 files changed, 286 insertions(+), 114 deletions(-) create mode 100644 include/soc/samsung/exynos-pmu.h diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index bbb444f43dea..d2b60d3969d8 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -45,9 +45,9 @@ config PMUCAL Support PMUCAL for Exynos SoC. config EXYNOS_PMU - bool "Exynos PMU controller driver" if COMPILE_TEST - depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) - select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS + bool "Exynos Power Management Unit Driver Support" + depends on ARCH_EXYNOS + select MFD_SYSCON config ECT bool "Enable Exynos Characteristic Table File" @@ -60,10 +60,6 @@ config ECT_DUMP depends on ECT # There is no need to enable these drivers for ARMv8 -config EXYNOS_PMU_ARM_DRIVERS - bool "Exynos PMU ARMv7-specific driver extensions" if COMPILE_TEST - depends on EXYNOS_PMU - config EXYNOS_PM_DOMAINS bool "Exynos PM domains" if COMPILE_TEST depends on PM_GENERIC_DOMAINS || COMPILE_TEST diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index 56f2be6c6fb2..a90eab6e1cbe 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -8,8 +8,6 @@ obj-$(CONFIG_ECT) += ect_parser.o obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o -obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \ - exynos5250-pmu.o exynos5420-pmu.o obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index bd4a76f27bc2..731d6ee970ee 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. + * Copyright (c) 2015 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * EXYNOS - CPU PMU(Power Management Unit) support @@ -9,151 +9,275 @@ * published by the Free Software Foundation. */ -#include -#include -#include +#include +#include #include #include -#include +#include +#include -#include -#include +/** + * "pmureg" has the mapped base address of PMU(Power Management Unit) + */ +static struct regmap *pmureg; -#include "exynos-pmu.h" +/** + * No driver refers the "pmureg" directly, through the only exported API. + */ +int exynos_pmu_read(unsigned int offset, unsigned int *val) +{ + return regmap_read(pmureg, offset, val); +} -struct exynos_pmu_context { - struct device *dev; - const struct exynos_pmu_data *pmu_data; -}; +int exynos_pmu_write(unsigned int offset, unsigned int val) +{ + return regmap_write(pmureg, offset, val); +} + +int exynos_pmu_update(unsigned int offset, unsigned int mask, unsigned int val) +{ + return regmap_update_bits(pmureg, offset, mask, val); +} + +struct regmap *exynos_get_pmu_regmap(void) +{ + return pmureg; +} + +EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap); +EXPORT_SYMBOL(exynos_pmu_read); +EXPORT_SYMBOL(exynos_pmu_write); +EXPORT_SYMBOL(exynos_pmu_update); + +/** + * CPU power control registers in PMU are arranged at regular intervals + * (interval = 0x8). pmu_cpu_offset calculates how far cpu is from address + * of first cpu. This expression is based on cpu and cluster id in MPIDR, + * refer below. + + * cpu address offset : ((cluster id << 2) | (cpu id)) * 0x8 + */ + +#ifdef CONFIG_SOC_EXYNOS9810 +#define CPU_PER_OFFSET 0x8 +#define phy_cluster(cpu) !MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1) +#else +#define CPU_PER_OFFSET 0x80 +#define phy_cluster(cpu) MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1) +#endif -void __iomem *pmu_base_addr; -static struct exynos_pmu_context *pmu_context; +#define phy_cpu(cpu) MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0) -void pmu_raw_writel(u32 val, u32 offset) +#define pmu_cpu_offset(cpu) (((phy_cluster(cpu) << 2)| phy_cpu(cpu)) * CPU_PER_OFFSET) + +#define PMU_CPU_CONFIG_BASE 0x2000 +#define PMU_CPU_STATUS_BASE 0x2004 +#define CPU_LOCAL_PWR_CFG 0xF +static void pmu_cpu_ctrl(unsigned int cpu, int enable) { - writel_relaxed(val, pmu_base_addr + offset); + unsigned int offset; + + offset = pmu_cpu_offset(cpu); + regmap_update_bits(pmureg, PMU_CPU_CONFIG_BASE + offset, + CPU_LOCAL_PWR_CFG, + enable ? CPU_LOCAL_PWR_CFG : 0); } -u32 pmu_raw_readl(u32 offset) +static int pmu_cpu_state(unsigned int cpu) { - return readl_relaxed(pmu_base_addr + offset); + unsigned int offset, val = 0; + + offset = pmu_cpu_offset(cpu); + regmap_read(pmureg, PMU_CPU_STATUS_BASE + offset, &val); + + return ((val & CPU_LOCAL_PWR_CFG) == CPU_LOCAL_PWR_CFG); } -void exynos_sys_powerdown_conf(enum sys_powerdown mode) +#define CLUSTER_ADDR_OFFSET 0x8 +#define PMU_NONCPU_CONFIG_BASE 0x2040 +#define PMU_NONCPU_STATUS_BASE 0x2044 +#define PMU_MEMORY_CLUSTER1_NONCPU_STATUS 0x2380 +#define MEMORY_CLUSTER_ADDR_OFFSET 0x21C +#define NONCPU_LOCAL_PWR_CFG 0xF +#define SHARED_CACHE_LOCAL_PWR_CFG 0x1 + +static void pmu_cluster_ctrl(unsigned int cpu, int enable) { - unsigned int i; - const struct exynos_pmu_data *pmu_data; + unsigned int offset; - if (!pmu_context || !pmu_context->pmu_data) - return; + offset = phy_cluster(cpu) * CLUSTER_ADDR_OFFSET; + regmap_update_bits(pmureg, + PMU_NONCPU_CONFIG_BASE + offset, + NONCPU_LOCAL_PWR_CFG, + enable ? NONCPU_LOCAL_PWR_CFG : 0); +} - pmu_data = pmu_context->pmu_data; +static bool pmu_noncpu_state(unsigned int cpu) +{ + unsigned int noncpu_stat = 0; + unsigned int offset; - if (pmu_data->powerdown_conf) - pmu_data->powerdown_conf(mode); + offset = phy_cluster(cpu) * CLUSTER_ADDR_OFFSET; + regmap_read(pmureg, + PMU_NONCPU_STATUS_BASE + offset, &noncpu_stat); - if (pmu_data->pmu_config) { - for (i = 0; (pmu_data->pmu_config[i].offset != PMU_TABLE_END); i++) - pmu_raw_writel(pmu_data->pmu_config[i].val[mode], - pmu_data->pmu_config[i].offset); - } + return !!(noncpu_stat & NONCPU_LOCAL_PWR_CFG); +} - if (pmu_data->powerdown_conf_extra) - pmu_data->powerdown_conf_extra(mode); +static int pmu_shared_cache_state(unsigned int cpu) +{ + unsigned int shared_stat = 0; + unsigned int offset; - if (pmu_data->pmu_config_extra) { - for (i = 0; pmu_data->pmu_config_extra[i].offset != PMU_TABLE_END; i++) - pmu_raw_writel(pmu_data->pmu_config_extra[i].val[mode], - pmu_data->pmu_config_extra[i].offset); - } + offset = phy_cluster(cpu) * MEMORY_CLUSTER_ADDR_OFFSET; + + regmap_read(pmureg, + PMU_MEMORY_CLUSTER1_NONCPU_STATUS + offset, &shared_stat); + + return (shared_stat & SHARED_CACHE_LOCAL_PWR_CFG); } -/* - * Split the data between ARM architectures because it is relatively big - * and useless on other arch. - */ -#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS -#define exynos_pmu_data_arm_ptr(data) (&data) -#else -#define exynos_pmu_data_arm_ptr(data) NULL -#endif +static void exynos_cpu_up(unsigned int cpu) +{ + pmu_cpu_ctrl(cpu, 1); +} -/* - * PMU platform driver and devicetree bindings. - */ -static const struct of_device_id exynos_pmu_of_device_ids[] = { - { - .compatible = "samsung,exynos3250-pmu", - .data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data), - }, { - .compatible = "samsung,exynos4210-pmu", - .data = exynos_pmu_data_arm_ptr(exynos4210_pmu_data), - }, { - .compatible = "samsung,exynos4212-pmu", - .data = exynos_pmu_data_arm_ptr(exynos4212_pmu_data), - }, { - .compatible = "samsung,exynos4412-pmu", - .data = exynos_pmu_data_arm_ptr(exynos4412_pmu_data), - }, { - .compatible = "samsung,exynos5250-pmu", - .data = exynos_pmu_data_arm_ptr(exynos5250_pmu_data), - }, { - .compatible = "samsung,exynos5420-pmu", - .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data), - }, { - .compatible = "samsung,exynos5433-pmu", - }, - { /*sentinel*/ }, +static void exynos_cpu_down(unsigned int cpu) +{ + pmu_cpu_ctrl(cpu, 0); +} + +static int exynos_cpu_state(unsigned int cpu) +{ + return pmu_cpu_state(cpu); +} + +static void exynos_cluster_up(unsigned int cpu) +{ + pmu_cluster_ctrl(cpu, false); +} + +static void exynos_cluster_down(unsigned int cpu) +{ + pmu_cluster_ctrl(cpu, true); +} + +static int exynos_cluster_state(unsigned int cpu) +{ + return pmu_shared_cache_state(cpu) && + pmu_noncpu_state(cpu); +} + +struct exynos_cpu_power_ops exynos_cpu = { + .power_up = exynos_cpu_up, + .power_down = exynos_cpu_down, + .power_state = exynos_cpu_state, + .cluster_up = exynos_cluster_up, + .cluster_down = exynos_cluster_down, + .cluster_state = exynos_cluster_state, }; -struct regmap *exynos_get_pmu_regmap(void) +#define PMU_CP_STAT 0x0038 +int exynos_check_cp_status(void) { - struct device_node *np = of_find_matching_node(NULL, - exynos_pmu_of_device_ids); - if (np) - return syscon_node_to_regmap(np); - return ERR_PTR(-ENODEV); + unsigned int val; + + exynos_pmu_read(PMU_CP_STAT, &val); + + return val; } -EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap); -static int exynos_pmu_probe(struct platform_device *pdev) +static struct bus_type exynos_info_subsys = { + .name = "exynos_info", + .dev_name = "exynos_info", +}; + +#define NR_CPUS_PER_CLUSTER 4 +static ssize_t core_status_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) { - struct device *dev = &pdev->dev; - struct resource *res; + ssize_t n = 0; + int cpu; + + for_each_possible_cpu(cpu) { + /* + * Each cluster has four cores. + * "cpu % NR_CPUS_PER_CLUSTER == 0" means that + * the cpu is a first one of each cluster. + */ + if (!(cpu % NR_CPUS_PER_CLUSTER)) { + n += scnprintf(buf + n, 26, "%s shared_cache : %d\n", + (!cpu) ? "boot" : "nonboot", + pmu_shared_cache_state(cpu)); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pmu_base_addr = devm_ioremap_resource(dev, res); - if (IS_ERR(pmu_base_addr)) - return PTR_ERR(pmu_base_addr); + n += scnprintf(buf + n, 24, "%s Noncpu : %d\n", + (!cpu) ? "boot" : "nonboot", + pmu_noncpu_state(cpu)); + } + n += scnprintf(buf + n, 24, "CPU%d : %d\n", + cpu, pmu_cpu_state(cpu)); + } - pmu_context = devm_kzalloc(&pdev->dev, - sizeof(struct exynos_pmu_context), - GFP_KERNEL); - if (!pmu_context) - return -ENOMEM; - pmu_context->dev = dev; - pmu_context->pmu_data = of_device_get_match_data(dev); + return n; +} - if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init) - pmu_context->pmu_data->pmu_init(); +static struct kobj_attribute cs_attr = + __ATTR(core_status, 0644, core_status_show, NULL); - platform_set_drvdata(pdev, pmu_context); +static struct attribute *cs_sysfs_attrs[] = { + &cs_attr.attr, + NULL, +}; + +static struct attribute_group cs_sysfs_group = { + .attrs = cs_sysfs_attrs, +}; + +static const struct attribute_group *cs_sysfs_groups[] = { + &cs_sysfs_group, + NULL, +}; + +static int exynos_pmu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,syscon-phandle"); + if (IS_ERR(pmureg)) { + pr_err("Fail to get regmap of PMU\n"); + return PTR_ERR(pmureg); + } + + if (subsys_system_register(&exynos_info_subsys, + cs_sysfs_groups)) + pr_err("Fail to register exynos_info subsys\n"); - dev_dbg(dev, "Exynos PMU Driver probe done\n"); return 0; } +static const struct of_device_id of_exynos_pmu_match[] = { + { .compatible = "samsung,exynos-pmu", }, + { }, +}; + +static const struct platform_device_id exynos_pmu_ids[] = { + { "exynos-pmu", }, + { } +}; + static struct platform_driver exynos_pmu_driver = { - .driver = { - .name = "exynos-pmu", - .of_match_table = exynos_pmu_of_device_ids, + .driver = { + .name = "exynos-pmu", + .owner = THIS_MODULE, + .of_match_table = of_exynos_pmu_match, }, - .probe = exynos_pmu_probe, + .probe = exynos_pmu_probe, + .id_table = exynos_pmu_ids, }; -static int __init exynos_pmu_init(void) +int __init exynos_pmu_init(void) { return platform_driver_register(&exynos_pmu_driver); - } -postcore_initcall(exynos_pmu_init); +subsys_initcall(exynos_pmu_init); diff --git a/include/soc/samsung/exynos-pmu.h b/include/soc/samsung/exynos-pmu.h new file mode 100644 index 000000000000..50300ad42396 --- /dev/null +++ b/include/soc/samsung/exynos-pmu.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS - PMU(Power Management Unit) support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __EXYNOS_PMU_H +#define __EXYNOS_PMU_H __FILE__ + +/** + * struct exynos_cpu_power_ops + * + * CPU power control operations + * + * @power_up : Set cpu configuration register + * @power_down : Clear cpu configuration register + * @power_state : Show cpu status. Return true if cpu power on, otherwise return false. + */ +struct exynos_cpu_power_ops { + void (*power_up)(unsigned int cpu); + void (*power_down)(unsigned int cpu); + int (*power_state)(unsigned int cpu); + void (*cluster_up)(unsigned int cpu); + void (*cluster_down)(unsigned int cpu); + int (*cluster_state)(unsigned int cpu); +}; +extern struct exynos_cpu_power_ops exynos_cpu; + +/** + * The APIs to control the PMU + */ +#ifdef CONFIG_EXYNOS_PMU +extern int exynos_pmu_read(unsigned int offset, unsigned int *val); +extern int exynos_pmu_write(unsigned int offset, unsigned int val); +extern int exynos_pmu_update(unsigned int offset, unsigned int mask, unsigned int val); + +extern void exynos_cpu_reset_enable(unsigned int cpu); +extern void exynos_cpu_reset_disable(unsigned int cpu); +extern int exynos_check_cp_status(void); +#else +static inline int exynos_pmu_read(unsigned int offset, unsigned int *val) {return 0;} +static inline int exynos_pmu_write(unsigned int offset, unsigned int val) {return 0;} +static inline int exynos_pmu_update(unsigned int offset, unsigned int mask, unsigned int val) {return 0;} + +static inline void exynos_cpu_reset_enable(unsigned int cpu) {return ;} +static inline void exynos_cpu_reset_disable(unsigned int cpu) {return ;} +static inline int exynos_check_cp_status(void) {return 0;} +#endif +#endif /* __EXYNOS_PMU_H */ -- 2.20.1