1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2712eddf7SBartlomiej Zolnierkiewicz /* 3712eddf7SBartlomiej Zolnierkiewicz * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. 43d739985SJaecheol Lee * http://www.samsung.com 53d739985SJaecheol Lee * 6712eddf7SBartlomiej Zolnierkiewicz * Coupled cpuidle support based on the work of: 7712eddf7SBartlomiej Zolnierkiewicz * Colin Cross <ccross@android.com> 8712eddf7SBartlomiej Zolnierkiewicz * Daniel Lezcano <daniel.lezcano@linaro.org> 93d739985SJaecheol Lee */ 103d739985SJaecheol Lee 113d739985SJaecheol Lee #include <linux/cpuidle.h> 1267173ca4SAmit Daniel Kachhap #include <linux/cpu_pm.h> 1376ee4557SKyungmin Park #include <linux/export.h> 1484599238SPaul Gortmaker #include <linux/init.h> 1535baa336SBartlomiej Zolnierkiewicz #include <linux/platform_device.h> 16712eddf7SBartlomiej Zolnierkiewicz #include <linux/of.h> 17712eddf7SBartlomiej Zolnierkiewicz #include <linux/platform_data/cpuidle-exynos.h> 183d739985SJaecheol Lee 1967173ca4SAmit Daniel Kachhap #include <asm/suspend.h> 2006c77b3cSAmit Daniel Kachhap #include <asm/cpuidle.h> 2167173ca4SAmit Daniel Kachhap 22712eddf7SBartlomiej Zolnierkiewicz static atomic_t exynos_idle_barrier; 23712eddf7SBartlomiej Zolnierkiewicz 24712eddf7SBartlomiej Zolnierkiewicz static struct cpuidle_exynos_data *exynos_cpuidle_pdata; 25277f5046SDaniel Lezcano static void (*exynos_enter_aftr)(void); 26ccd458c1SKukjin Kim 27712eddf7SBartlomiej Zolnierkiewicz static int exynos_enter_coupled_lowpower(struct cpuidle_device *dev, 28712eddf7SBartlomiej Zolnierkiewicz struct cpuidle_driver *drv, 29712eddf7SBartlomiej Zolnierkiewicz int index) 30712eddf7SBartlomiej Zolnierkiewicz { 31712eddf7SBartlomiej Zolnierkiewicz int ret; 32712eddf7SBartlomiej Zolnierkiewicz 33712eddf7SBartlomiej Zolnierkiewicz exynos_cpuidle_pdata->pre_enter_aftr(); 34712eddf7SBartlomiej Zolnierkiewicz 35712eddf7SBartlomiej Zolnierkiewicz /* 36712eddf7SBartlomiej Zolnierkiewicz * Waiting all cpus to reach this point at the same moment 37712eddf7SBartlomiej Zolnierkiewicz */ 38712eddf7SBartlomiej Zolnierkiewicz cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier); 39712eddf7SBartlomiej Zolnierkiewicz 40712eddf7SBartlomiej Zolnierkiewicz /* 41712eddf7SBartlomiej Zolnierkiewicz * Both cpus will reach this point at the same time 42712eddf7SBartlomiej Zolnierkiewicz */ 43712eddf7SBartlomiej Zolnierkiewicz ret = dev->cpu ? exynos_cpuidle_pdata->cpu1_powerdown() 44712eddf7SBartlomiej Zolnierkiewicz : exynos_cpuidle_pdata->cpu0_enter_aftr(); 45712eddf7SBartlomiej Zolnierkiewicz if (ret) 46712eddf7SBartlomiej Zolnierkiewicz index = ret; 47712eddf7SBartlomiej Zolnierkiewicz 48712eddf7SBartlomiej Zolnierkiewicz /* 49712eddf7SBartlomiej Zolnierkiewicz * Waiting all cpus to finish the power sequence before going further 50712eddf7SBartlomiej Zolnierkiewicz */ 51712eddf7SBartlomiej Zolnierkiewicz cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier); 52712eddf7SBartlomiej Zolnierkiewicz 53712eddf7SBartlomiej Zolnierkiewicz exynos_cpuidle_pdata->post_enter_aftr(); 54712eddf7SBartlomiej Zolnierkiewicz 55712eddf7SBartlomiej Zolnierkiewicz return index; 56712eddf7SBartlomiej Zolnierkiewicz } 57712eddf7SBartlomiej Zolnierkiewicz 587880e45eSDaniel Lezcano static int exynos_enter_lowpower(struct cpuidle_device *dev, 5967173ca4SAmit Daniel Kachhap struct cpuidle_driver *drv, 6067173ca4SAmit Daniel Kachhap int index) 6167173ca4SAmit Daniel Kachhap { 6267173ca4SAmit Daniel Kachhap int new_index = index; 6367173ca4SAmit Daniel Kachhap 64118f5c1dSBartlomiej Zolnierkiewicz /* AFTR can only be entered when cores other than CPU0 are offline */ 65118f5c1dSBartlomiej Zolnierkiewicz if (num_online_cpus() > 1 || dev->cpu != 0) 6667173ca4SAmit Daniel Kachhap new_index = drv->safe_state_index; 6767173ca4SAmit Daniel Kachhap 6867173ca4SAmit Daniel Kachhap if (new_index == 0) 6906c77b3cSAmit Daniel Kachhap return arm_cpuidle_simple_enter(dev, drv, new_index); 7001601b34STomasz Figa 7101601b34STomasz Figa exynos_enter_aftr(); 7201601b34STomasz Figa 7301601b34STomasz Figa return new_index; 7467173ca4SAmit Daniel Kachhap } 7567173ca4SAmit Daniel Kachhap 767880e45eSDaniel Lezcano static struct cpuidle_driver exynos_idle_driver = { 777880e45eSDaniel Lezcano .name = "exynos_idle", 7853af16a1SDaniel Lezcano .owner = THIS_MODULE, 7953af16a1SDaniel Lezcano .states = { 8053af16a1SDaniel Lezcano [0] = ARM_CPUIDLE_WFI_STATE, 8153af16a1SDaniel Lezcano [1] = { 827880e45eSDaniel Lezcano .enter = exynos_enter_lowpower, 8353af16a1SDaniel Lezcano .exit_latency = 300, 84c324f43aSMarek Szyprowski .target_residency = 10000, 8553af16a1SDaniel Lezcano .name = "C1", 8653af16a1SDaniel Lezcano .desc = "ARM power down", 8753af16a1SDaniel Lezcano }, 8853af16a1SDaniel Lezcano }, 8953af16a1SDaniel Lezcano .state_count = 2, 9053af16a1SDaniel Lezcano .safe_state_index = 0, 9153af16a1SDaniel Lezcano }; 9253af16a1SDaniel Lezcano 93712eddf7SBartlomiej Zolnierkiewicz static struct cpuidle_driver exynos_coupled_idle_driver = { 94712eddf7SBartlomiej Zolnierkiewicz .name = "exynos_coupled_idle", 95712eddf7SBartlomiej Zolnierkiewicz .owner = THIS_MODULE, 96712eddf7SBartlomiej Zolnierkiewicz .states = { 97712eddf7SBartlomiej Zolnierkiewicz [0] = ARM_CPUIDLE_WFI_STATE, 98712eddf7SBartlomiej Zolnierkiewicz [1] = { 99712eddf7SBartlomiej Zolnierkiewicz .enter = exynos_enter_coupled_lowpower, 100712eddf7SBartlomiej Zolnierkiewicz .exit_latency = 5000, 101712eddf7SBartlomiej Zolnierkiewicz .target_residency = 10000, 102712eddf7SBartlomiej Zolnierkiewicz .flags = CPUIDLE_FLAG_COUPLED | 103712eddf7SBartlomiej Zolnierkiewicz CPUIDLE_FLAG_TIMER_STOP, 104712eddf7SBartlomiej Zolnierkiewicz .name = "C1", 105712eddf7SBartlomiej Zolnierkiewicz .desc = "ARM power down", 106712eddf7SBartlomiej Zolnierkiewicz }, 107712eddf7SBartlomiej Zolnierkiewicz }, 108712eddf7SBartlomiej Zolnierkiewicz .state_count = 2, 109712eddf7SBartlomiej Zolnierkiewicz .safe_state_index = 0, 110712eddf7SBartlomiej Zolnierkiewicz }; 111712eddf7SBartlomiej Zolnierkiewicz 112f612a4fbSJingoo Han static int exynos_cpuidle_probe(struct platform_device *pdev) 1133d739985SJaecheol Lee { 114043c86b6SDaniel Lezcano int ret; 1153d739985SJaecheol Lee 116cfdda353SBartlomiej Zolnierkiewicz if (IS_ENABLED(CONFIG_SMP) && 1178919d779SMarek Szyprowski (of_machine_is_compatible("samsung,exynos4210") || 1188919d779SMarek Szyprowski of_machine_is_compatible("samsung,exynos3250"))) { 119712eddf7SBartlomiej Zolnierkiewicz exynos_cpuidle_pdata = pdev->dev.platform_data; 120712eddf7SBartlomiej Zolnierkiewicz 121712eddf7SBartlomiej Zolnierkiewicz ret = cpuidle_register(&exynos_coupled_idle_driver, 122712eddf7SBartlomiej Zolnierkiewicz cpu_possible_mask); 123712eddf7SBartlomiej Zolnierkiewicz } else { 124277f5046SDaniel Lezcano exynos_enter_aftr = (void *)(pdev->dev.platform_data); 125277f5046SDaniel Lezcano 1267880e45eSDaniel Lezcano ret = cpuidle_register(&exynos_idle_driver, NULL); 127712eddf7SBartlomiej Zolnierkiewicz } 128712eddf7SBartlomiej Zolnierkiewicz 1295db9f436SDaniel Lezcano if (ret) { 130ae7c4c87SJingoo Han dev_err(&pdev->dev, "failed to register cpuidle driver\n"); 1315db9f436SDaniel Lezcano return ret; 13246bcfad7SDeepthi Dharwar } 1333d739985SJaecheol Lee 1343d739985SJaecheol Lee return 0; 1353d739985SJaecheol Lee } 13635baa336SBartlomiej Zolnierkiewicz 13735baa336SBartlomiej Zolnierkiewicz static struct platform_driver exynos_cpuidle_driver = { 13835baa336SBartlomiej Zolnierkiewicz .probe = exynos_cpuidle_probe, 13935baa336SBartlomiej Zolnierkiewicz .driver = { 14035baa336SBartlomiej Zolnierkiewicz .name = "exynos_cpuidle", 14135baa336SBartlomiej Zolnierkiewicz }, 14235baa336SBartlomiej Zolnierkiewicz }; 14384599238SPaul Gortmaker builtin_platform_driver(exynos_cpuidle_driver); 144