162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CPU idle for DaVinci SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments Incorporated. https://www.ti.com/
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Derived from Marvell Kirkwood CPU idle code
862306a36Sopenharmony_ci * (arch/arm/mach-kirkwood/cpuidle.c)
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/cpuidle.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/export.h>
1762306a36Sopenharmony_ci#include <asm/cpuidle.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "cpuidle.h"
2062306a36Sopenharmony_ci#include "ddr2.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define DAVINCI_CPUIDLE_MAX_STATES	2
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic void __iomem *ddr2_reg_base;
2562306a36Sopenharmony_cistatic bool ddr2_pdown;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void davinci_save_ddr_power(int enter, bool pdown)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	u32 val;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	val = __raw_readl(ddr2_reg_base + DDR2_SDRCR_OFFSET);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (enter) {
3462306a36Sopenharmony_ci		if (pdown)
3562306a36Sopenharmony_ci			val |= DDR2_SRPD_BIT;
3662306a36Sopenharmony_ci		else
3762306a36Sopenharmony_ci			val &= ~DDR2_SRPD_BIT;
3862306a36Sopenharmony_ci		val |= DDR2_LPMODEN_BIT;
3962306a36Sopenharmony_ci	} else {
4062306a36Sopenharmony_ci		val &= ~(DDR2_SRPD_BIT | DDR2_LPMODEN_BIT);
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	__raw_writel(val, ddr2_reg_base + DDR2_SDRCR_OFFSET);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Actual code that puts the SoC in different idle states */
4762306a36Sopenharmony_cistatic __cpuidle int davinci_enter_idle(struct cpuidle_device *dev,
4862306a36Sopenharmony_ci					struct cpuidle_driver *drv, int index)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	davinci_save_ddr_power(1, ddr2_pdown);
5162306a36Sopenharmony_ci	cpu_do_idle();
5262306a36Sopenharmony_ci	davinci_save_ddr_power(0, ddr2_pdown);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return index;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic struct cpuidle_driver davinci_idle_driver = {
5862306a36Sopenharmony_ci	.name			= "cpuidle-davinci",
5962306a36Sopenharmony_ci	.owner			= THIS_MODULE,
6062306a36Sopenharmony_ci	.states[0]		= ARM_CPUIDLE_WFI_STATE,
6162306a36Sopenharmony_ci	.states[1]		= {
6262306a36Sopenharmony_ci		.enter			= davinci_enter_idle,
6362306a36Sopenharmony_ci		.exit_latency		= 10,
6462306a36Sopenharmony_ci		.target_residency	= 10000,
6562306a36Sopenharmony_ci		.name			= "DDR SR",
6662306a36Sopenharmony_ci		.desc			= "WFI and DDR Self Refresh",
6762306a36Sopenharmony_ci	},
6862306a36Sopenharmony_ci	.state_count = DAVINCI_CPUIDLE_MAX_STATES,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int __init davinci_cpuidle_probe(struct platform_device *pdev)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct davinci_cpuidle_config *pdata = pdev->dev.platform_data;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!pdata) {
7662306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot get platform data\n");
7762306a36Sopenharmony_ci		return -ENOENT;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	ddr2_reg_base = pdata->ddr2_ctlr_base;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ddr2_pdown = pdata->ddr2_pdown;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return cpuidle_register(&davinci_idle_driver, NULL);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic struct platform_driver davinci_cpuidle_driver = {
8862306a36Sopenharmony_ci	.driver = {
8962306a36Sopenharmony_ci		.name	= "cpuidle-davinci",
9062306a36Sopenharmony_ci	},
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int __init davinci_cpuidle_init(void)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	return platform_driver_probe(&davinci_cpuidle_driver,
9662306a36Sopenharmony_ci						davinci_cpuidle_probe);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_cidevice_initcall(davinci_cpuidle_init);
9962306a36Sopenharmony_ci
100