Commit 90c0ebd772713d8f80a0d27cb1b572f0f09a301c

Authored by Vaibhav Bedia
1 parent 276a52f8b7
Exists in master

arm:omap:am33xx: Basic cpuidle support

Add basic cpuidle support for AM33XX family of SoC.
Right now only two idle states (WFI and WFI+SR) are
supported. The latency/residency numbers chosen will
be fine-tuned based on power measurements on actual
hardware.

Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>

Showing 5 changed files with 241 additions and 1 deletions Side-by-side Diff

arch/arm/mach-omap2/Makefile
... ... @@ -81,7 +81,7 @@
81 81 obj-$(CONFIG_ARCH_OMAP2) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o
82 82 obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \
83 83 vc3xxx_data.o vp3xxx_data.o dvfs.o
84   -obj-$(CONFIG_SOC_OMAPAM33XX) += cminst33xx.o prminst33xx.o cm33xx.o
  84 +obj-$(CONFIG_SOC_OMAPAM33XX) += cminst33xx.o prminst33xx.o cm33xx.o cpuidle33xx.o
85 85  
86 86 # XXX The presence of cm2xxx_3xxx.o on the line below is temporary and
87 87 # will be removed once the OMAP4 part of the codebase is converted to
arch/arm/mach-omap2/cpuidle33xx.c
  1 +/*
  2 + * CPU idle for AM33XX SoCs
  3 + *
  4 + * Copyright (C) 2011 Texas Instruments Incorporated. http://www.ti.com/
  5 + *
  6 + * Derived from Davinci CPU idle code
  7 + * (arch/arm/mach-davinci/cpuidle.c)
  8 + *
  9 + * This program is free software; you can redistribute it and/or
  10 + * modify it under the terms of the GNU General Public License as
  11 + * published by the Free Software Foundation version 2.
  12 + *
  13 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  14 + * kind, whether express or implied; without even the implied warranty
  15 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 + * GNU General Public License for more details.
  17 + */
  18 +
  19 +#include <linux/kernel.h>
  20 +#include <linux/init.h>
  21 +#include <linux/io.h>
  22 +#include <linux/platform_device.h>
  23 +#include <linux/cpuidle.h>
  24 +#include <linux/sched.h>
  25 +#include <asm/proc-fns.h>
  26 +
  27 +#include <plat/emif.h>
  28 +
  29 +#include "cpuidle33xx.h"
  30 +
  31 +#define AM33XX_CPUIDLE_MAX_STATES 2
  32 +
  33 +struct am33xx_ops {
  34 + void (*enter) (u32 flags);
  35 + void (*exit) (u32 flags);
  36 + u32 flags;
  37 +};
  38 +
  39 +/* fields in am33xx_ops.flags */
  40 +#define AM33XX_CPUIDLE_FLAGS_DDR2_PWDN BIT(0)
  41 +
  42 +static struct cpuidle_driver am33xx_idle_driver = {
  43 + .name = "cpuidle-am33xx",
  44 + .owner = THIS_MODULE,
  45 +};
  46 +
  47 +static DEFINE_PER_CPU(struct cpuidle_device, am33xx_cpuidle_device);
  48 +static void __iomem *emif_base;
  49 +
  50 +static void am33xx_save_ddr_power(int enter, bool pdown)
  51 +{
  52 + u32 val;
  53 +
  54 + val = __raw_readl(emif_base + EMIF4_0_SDRAM_MGMT_CTRL);
  55 +
  56 + /* TODO: Choose the mode based on memory type */
  57 + if (enter)
  58 + val = SELF_REFRESH_ENABLE(64);
  59 + else
  60 + val = SELF_REFRESH_DISABLE;
  61 +
  62 + __raw_writel(val, emif_base + EMIF4_0_SDRAM_MGMT_CTRL);
  63 +}
  64 +
  65 +static void am33xx_c2state_enter(u32 flags)
  66 +{
  67 + am33xx_save_ddr_power(1, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN));
  68 +}
  69 +
  70 +static void am33xx_c2state_exit(u32 flags)
  71 +{
  72 + am33xx_save_ddr_power(0, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN));
  73 +}
  74 +
  75 +static struct am33xx_ops am33xx_states[AM33XX_CPUIDLE_MAX_STATES] = {
  76 + [1] = {
  77 + .enter = am33xx_c2state_enter,
  78 + .exit = am33xx_c2state_exit,
  79 + },
  80 +};
  81 +
  82 +/* Actual code that puts the SoC in different idle states */
  83 +static int am33xx_enter_idle(struct cpuidle_device *dev,
  84 + struct cpuidle_state *state)
  85 +{
  86 + struct am33xx_ops *ops = cpuidle_get_statedata(state);
  87 + struct timeval before, after;
  88 + int idle_time;
  89 +
  90 + local_irq_disable();
  91 + do_gettimeofday(&before);
  92 +
  93 + if (ops && ops->enter)
  94 + ops->enter(ops->flags);
  95 +
  96 + /* Wait for interrupt state */
  97 + cpu_do_idle();
  98 + if (ops && ops->exit)
  99 + ops->exit(ops->flags);
  100 +
  101 + do_gettimeofday(&after);
  102 + local_irq_enable();
  103 + idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
  104 + (after.tv_usec - before.tv_usec);
  105 + return idle_time;
  106 +}
  107 +
  108 +static int __init am33xx_cpuidle_probe(struct platform_device *pdev)
  109 +{
  110 + int ret;
  111 + struct cpuidle_device *device;
  112 + struct am33xx_cpuidle_config *pdata = pdev->dev.platform_data;
  113 +
  114 + device = &per_cpu(am33xx_cpuidle_device, smp_processor_id());
  115 +
  116 + if (!pdata) {
  117 + dev_err(&pdev->dev, "cannot get platform data\n");
  118 + return -ENOENT;
  119 + }
  120 +
  121 + emif_base = pdata->emif_base;
  122 +
  123 + ret = cpuidle_register_driver(&am33xx_idle_driver);
  124 + if (ret) {
  125 + dev_err(&pdev->dev, "failed to register driver\n");
  126 + return ret;
  127 + }
  128 +
  129 + /* Wait for interrupt state */
  130 + device->states[0].enter = am33xx_enter_idle;
  131 + device->states[0].exit_latency = 1;
  132 + device->states[0].target_residency = 10000;
  133 + device->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
  134 + strcpy(device->states[0].name, "WFI");
  135 + strcpy(device->states[0].desc, "Wait for interrupt");
  136 +
  137 + /* Wait for interrupt and DDR self refresh state */
  138 + device->states[1].enter = am33xx_enter_idle;
  139 + device->states[1].exit_latency = 100;
  140 + device->states[1].target_residency = 10000;
  141 + device->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
  142 + strcpy(device->states[1].name, "DDR SR");
  143 + strcpy(device->states[1].desc, "WFI and DDR Self Refresh");
  144 + if (pdata->ddr2_pdown)
  145 + am33xx_states[1].flags |= AM33XX_CPUIDLE_FLAGS_DDR2_PWDN;
  146 + cpuidle_set_statedata(&device->states[1], &am33xx_states[1]);
  147 +
  148 + device->state_count = AM33XX_CPUIDLE_MAX_STATES;
  149 +
  150 + ret = cpuidle_register_device(device);
  151 + if (ret) {
  152 + dev_err(&pdev->dev, "failed to register device\n");
  153 + cpuidle_unregister_driver(&am33xx_idle_driver);
  154 + return ret;
  155 + }
  156 +
  157 + return 0;
  158 +}
  159 +
  160 +static struct platform_driver am33xx_cpuidle_driver = {
  161 + .driver = {
  162 + .name = "cpuidle-am33xx",
  163 + .owner = THIS_MODULE,
  164 + },
  165 +};
  166 +
  167 +static int __init am33xx_cpuidle_init(void)
  168 +{
  169 + return platform_driver_probe(&am33xx_cpuidle_driver,
  170 + am33xx_cpuidle_probe);
  171 +}
  172 +device_initcall(am33xx_cpuidle_init);
arch/arm/mach-omap2/cpuidle33xx.h
  1 +/*
  2 + * TI AM33XX cpuidle platform support
  3 + *
  4 + * Copyright (C) 2011 Texas Instruments, Inc. - http://www.ti.com/
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License as
  8 + * published by the Free Software Foundation version 2.
  9 + *
  10 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  11 + * kind, whether express or implied; without even the implied warranty
  12 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + */
  15 +
  16 +#ifndef _AM33XX_CPUIDLE_H
  17 +#define _AM33XX_CPUIDLE_H
  18 +
  19 +struct am33xx_cpuidle_config {
  20 + u32 ddr2_pdown;
  21 + void __iomem *emif_base;
  22 +};
  23 +
  24 +#endif
arch/arm/plat-omap/include/plat/am33xx.h
... ... @@ -22,6 +22,8 @@
22 22 #define AM33XX_CTRL_BASE AM33XX_SCM_BASE
23 23 #define AM33XX_PRCM_BASE 0x44E00000
24 24  
  25 +#define AM33XX_EMIF0_BASE 0x4C000000
  26 +
25 27 #define AM33XX_GPIO0_BASE 0x44E07000
26 28 #define AM33XX_GPIO1_BASE 0x4804C000
27 29 #define AM33XX_GPIO2_BASE 0x481AC000
arch/arm/plat-omap/include/plat/emif.h
  1 +/*
  2 + * EMIF register definitions for TI81xx and AM33xx
  3 + *
  4 + * Copyright (C) 2011 Texas Instruments, Inc. - http://www.ti.com/
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License as
  8 + * published by the Free Software Foundation version 2.
  9 + *
  10 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  11 + * kind, whether express or implied; without even the implied warranty
  12 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + */
  15 +
  16 +#ifndef __EMIF_H
  17 +#define __EMIF_H
  18 +
  19 +#define EMIF_MOD_ID_REV (0x0)
  20 +#define EMIF4_0_SDRAM_STATUS (0x04)
  21 +#define EMIF4_0_SDRAM_CONFIG (0x08)
  22 +#define EMIF4_0_SDRAM_CONFIG2 (0x0C)
  23 +#define EMIF4_0_SDRAM_REF_CTRL (0x10)
  24 +#define EMIF4_0_SDRAM_REF_CTRL_SHADOW (0x14)
  25 +#define EMIF4_0_SDRAM_TIM_1 (0x18)
  26 +#define EMIF4_0_SDRAM_TIM_1_SHADOW (0x1C)
  27 +#define EMIF4_0_SDRAM_TIM_2 (0x20)
  28 +#define EMIF4_0_SDRAM_TIM_2_SHADOW (0x24)
  29 +#define EMIF4_0_SDRAM_TIM_3 (0x28)
  30 +#define EMIF4_0_SDRAM_TIM_3_SHADOW (0x2C)
  31 +#define EMIF4_0_SDRAM_MGMT_CTRL (0x38)
  32 +#define EMIF4_0_SDRAM_MGMT_CTRL_SHD (0x3C)
  33 +#define EMIF4_0_DDR_PHY_CTRL_1 (0xE4)
  34 +#define EMIF4_0_DDR_PHY_CTRL_1_SHADOW (0xE8)
  35 +#define EMIF4_0_DDR_PHY_CTRL_2 (0xEC)
  36 +#define EMIF4_0_IODFT_TLGC (0x60)
  37 +
  38 +#define SELF_REFRESH_ENABLE(m) (0x2 << 8 | (m << 4))
  39 +#define SELF_REFRESH_DISABLE (0x0 << 8)
  40 +
  41 +#endif /* __EMIF_H */