18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * CPU idle for DaVinci SoCs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Texas Instruments Incorporated. https://www.ti.com/
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Derived from Marvell Kirkwood CPU idle code
88c2ecf20Sopenharmony_ci * (arch/arm/mach-kirkwood/cpuidle.c)
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/cpuidle.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/export.h>
178c2ecf20Sopenharmony_ci#include <asm/cpuidle.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "cpuidle.h"
208c2ecf20Sopenharmony_ci#include "ddr2.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define DAVINCI_CPUIDLE_MAX_STATES	2
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic void __iomem *ddr2_reg_base;
258c2ecf20Sopenharmony_cistatic bool ddr2_pdown;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void davinci_save_ddr_power(int enter, bool pdown)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	u32 val;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	val = __raw_readl(ddr2_reg_base + DDR2_SDRCR_OFFSET);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (enter) {
348c2ecf20Sopenharmony_ci		if (pdown)
358c2ecf20Sopenharmony_ci			val |= DDR2_SRPD_BIT;
368c2ecf20Sopenharmony_ci		else
378c2ecf20Sopenharmony_ci			val &= ~DDR2_SRPD_BIT;
388c2ecf20Sopenharmony_ci		val |= DDR2_LPMODEN_BIT;
398c2ecf20Sopenharmony_ci	} else {
408c2ecf20Sopenharmony_ci		val &= ~(DDR2_SRPD_BIT | DDR2_LPMODEN_BIT);
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	__raw_writel(val, ddr2_reg_base + DDR2_SDRCR_OFFSET);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* Actual code that puts the SoC in different idle states */
478c2ecf20Sopenharmony_cistatic int davinci_enter_idle(struct cpuidle_device *dev,
488c2ecf20Sopenharmony_ci			      struct cpuidle_driver *drv, int index)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	davinci_save_ddr_power(1, ddr2_pdown);
518c2ecf20Sopenharmony_ci	cpu_do_idle();
528c2ecf20Sopenharmony_ci	davinci_save_ddr_power(0, ddr2_pdown);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return index;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic struct cpuidle_driver davinci_idle_driver = {
588c2ecf20Sopenharmony_ci	.name			= "cpuidle-davinci",
598c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
608c2ecf20Sopenharmony_ci	.states[0]		= ARM_CPUIDLE_WFI_STATE,
618c2ecf20Sopenharmony_ci	.states[1]		= {
628c2ecf20Sopenharmony_ci		.enter			= davinci_enter_idle,
638c2ecf20Sopenharmony_ci		.exit_latency		= 10,
648c2ecf20Sopenharmony_ci		.target_residency	= 10000,
658c2ecf20Sopenharmony_ci		.name			= "DDR SR",
668c2ecf20Sopenharmony_ci		.desc			= "WFI and DDR Self Refresh",
678c2ecf20Sopenharmony_ci	},
688c2ecf20Sopenharmony_ci	.state_count = DAVINCI_CPUIDLE_MAX_STATES,
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int __init davinci_cpuidle_probe(struct platform_device *pdev)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct davinci_cpuidle_config *pdata = pdev->dev.platform_data;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!pdata) {
768c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot get platform data\n");
778c2ecf20Sopenharmony_ci		return -ENOENT;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	ddr2_reg_base = pdata->ddr2_ctlr_base;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ddr2_pdown = pdata->ddr2_pdown;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return cpuidle_register(&davinci_idle_driver, NULL);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct platform_driver davinci_cpuidle_driver = {
888c2ecf20Sopenharmony_ci	.driver = {
898c2ecf20Sopenharmony_ci		.name	= "cpuidle-davinci",
908c2ecf20Sopenharmony_ci	},
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int __init davinci_cpuidle_init(void)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	return platform_driver_probe(&davinci_cpuidle_driver,
968c2ecf20Sopenharmony_ci						davinci_cpuidle_probe);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_cidevice_initcall(davinci_cpuidle_init);
998c2ecf20Sopenharmony_ci
100