brintos

brintos / linux-shallow public Read only

0
0
Text · 3.3 KiB · b2b5666 Raw
144 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.4 *		http://www.samsung.com5 *6 * Coupled cpuidle support based on the work of:7 *	Colin Cross <ccross@android.com>8 *	Daniel Lezcano <daniel.lezcano@linaro.org>9*/10 11#include <linux/cpuidle.h>12#include <linux/cpu_pm.h>13#include <linux/export.h>14#include <linux/init.h>15#include <linux/platform_device.h>16#include <linux/of.h>17#include <linux/platform_data/cpuidle-exynos.h>18 19#include <asm/suspend.h>20#include <asm/cpuidle.h>21 22static atomic_t exynos_idle_barrier;23 24static struct cpuidle_exynos_data *exynos_cpuidle_pdata;25static void (*exynos_enter_aftr)(void);26 27static int exynos_enter_coupled_lowpower(struct cpuidle_device *dev,28					 struct cpuidle_driver *drv,29					 int index)30{31	int ret;32 33	exynos_cpuidle_pdata->pre_enter_aftr();34 35	/*36	 * Waiting all cpus to reach this point at the same moment37	 */38	cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier);39 40	/*41	 * Both cpus will reach this point at the same time42	 */43	ret = dev->cpu ? exynos_cpuidle_pdata->cpu1_powerdown()44		       : exynos_cpuidle_pdata->cpu0_enter_aftr();45	if (ret)46		index = ret;47 48	/*49	 * Waiting all cpus to finish the power sequence before going further50	 */51	cpuidle_coupled_parallel_barrier(dev, &exynos_idle_barrier);52 53	exynos_cpuidle_pdata->post_enter_aftr();54 55	return index;56}57 58static int exynos_enter_lowpower(struct cpuidle_device *dev,59				struct cpuidle_driver *drv,60				int index)61{62	int new_index = index;63 64	/* AFTR can only be entered when cores other than CPU0 are offline */65	if (num_online_cpus() > 1 || dev->cpu != 0)66		new_index = drv->safe_state_index;67 68	if (new_index == 0)69		return arm_cpuidle_simple_enter(dev, drv, new_index);70 71	exynos_enter_aftr();72 73	return new_index;74}75 76static struct cpuidle_driver exynos_idle_driver = {77	.name			= "exynos_idle",78	.owner			= THIS_MODULE,79	.states = {80		[0] = ARM_CPUIDLE_WFI_STATE,81		[1] = {82			.enter			= exynos_enter_lowpower,83			.exit_latency		= 300,84			.target_residency	= 10000,85			.name			= "C1",86			.desc			= "ARM power down",87		},88	},89	.state_count = 2,90	.safe_state_index = 0,91};92 93static struct cpuidle_driver exynos_coupled_idle_driver = {94	.name			= "exynos_coupled_idle",95	.owner			= THIS_MODULE,96	.states = {97		[0] = ARM_CPUIDLE_WFI_STATE,98		[1] = {99			.enter			= exynos_enter_coupled_lowpower,100			.exit_latency		= 5000,101			.target_residency	= 10000,102			.flags			= CPUIDLE_FLAG_COUPLED |103						  CPUIDLE_FLAG_TIMER_STOP,104			.name			= "C1",105			.desc			= "ARM power down",106		},107	},108	.state_count = 2,109	.safe_state_index = 0,110};111 112static int exynos_cpuidle_probe(struct platform_device *pdev)113{114	int ret;115 116	if (IS_ENABLED(CONFIG_SMP) &&117	    (of_machine_is_compatible("samsung,exynos4210") ||118	     of_machine_is_compatible("samsung,exynos3250"))) {119		exynos_cpuidle_pdata = pdev->dev.platform_data;120 121		ret = cpuidle_register(&exynos_coupled_idle_driver,122				       cpu_possible_mask);123	} else {124		exynos_enter_aftr = (void *)(pdev->dev.platform_data);125 126		ret = cpuidle_register(&exynos_idle_driver, NULL);127	}128 129	if (ret) {130		dev_err(&pdev->dev, "failed to register cpuidle driver\n");131		return ret;132	}133 134	return 0;135}136 137static struct platform_driver exynos_cpuidle_driver = {138	.probe	= exynos_cpuidle_probe,139	.driver = {140		.name = "exynos_cpuidle",141	},142};143builtin_platform_driver(exynos_cpuidle_driver);144