root/drivers/cpuidle/cpuidle-cps.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cps_nc_enter
  2. cps_cpuidle_unregister
  3. cps_cpuidle_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright (C) 2014 Imagination Technologies
   4  * Author: Paul Burton <paul.burton@mips.com>
   5  */
   6 
   7 #include <linux/cpu_pm.h>
   8 #include <linux/cpuidle.h>
   9 #include <linux/init.h>
  10 
  11 #include <asm/idle.h>
  12 #include <asm/pm-cps.h>
  13 
  14 /* Enumeration of the various idle states this driver may enter */
  15 enum cps_idle_state {
  16         STATE_WAIT = 0,         /* MIPS wait instruction, coherent */
  17         STATE_NC_WAIT,          /* MIPS wait instruction, non-coherent */
  18         STATE_CLOCK_GATED,      /* Core clock gated */
  19         STATE_POWER_GATED,      /* Core power gated */
  20         STATE_COUNT
  21 };
  22 
  23 static int cps_nc_enter(struct cpuidle_device *dev,
  24                         struct cpuidle_driver *drv, int index)
  25 {
  26         enum cps_pm_state pm_state;
  27         int err;
  28 
  29         /*
  30          * At least one core must remain powered up & clocked in order for the
  31          * system to have any hope of functioning.
  32          *
  33          * TODO: don't treat core 0 specially, just prevent the final core
  34          * TODO: remap interrupt affinity temporarily
  35          */
  36         if (cpus_are_siblings(0, dev->cpu) && (index > STATE_NC_WAIT))
  37                 index = STATE_NC_WAIT;
  38 
  39         /* Select the appropriate cps_pm_state */
  40         switch (index) {
  41         case STATE_NC_WAIT:
  42                 pm_state = CPS_PM_NC_WAIT;
  43                 break;
  44         case STATE_CLOCK_GATED:
  45                 pm_state = CPS_PM_CLOCK_GATED;
  46                 break;
  47         case STATE_POWER_GATED:
  48                 pm_state = CPS_PM_POWER_GATED;
  49                 break;
  50         default:
  51                 BUG();
  52                 return -EINVAL;
  53         }
  54 
  55         /* Notify listeners the CPU is about to power down */
  56         if ((pm_state == CPS_PM_POWER_GATED) && cpu_pm_enter())
  57                 return -EINTR;
  58 
  59         /* Enter that state */
  60         err = cps_pm_enter_state(pm_state);
  61 
  62         /* Notify listeners the CPU is back up */
  63         if (pm_state == CPS_PM_POWER_GATED)
  64                 cpu_pm_exit();
  65 
  66         return err ?: index;
  67 }
  68 
  69 static struct cpuidle_driver cps_driver = {
  70         .name                   = "cpc_cpuidle",
  71         .owner                  = THIS_MODULE,
  72         .states = {
  73                 [STATE_WAIT] = MIPS_CPUIDLE_WAIT_STATE,
  74                 [STATE_NC_WAIT] = {
  75                         .enter  = cps_nc_enter,
  76                         .exit_latency           = 200,
  77                         .target_residency       = 450,
  78                         .name   = "nc-wait",
  79                         .desc   = "non-coherent MIPS wait",
  80                 },
  81                 [STATE_CLOCK_GATED] = {
  82                         .enter  = cps_nc_enter,
  83                         .exit_latency           = 300,
  84                         .target_residency       = 700,
  85                         .flags  = CPUIDLE_FLAG_TIMER_STOP,
  86                         .name   = "clock-gated",
  87                         .desc   = "core clock gated",
  88                 },
  89                 [STATE_POWER_GATED] = {
  90                         .enter  = cps_nc_enter,
  91                         .exit_latency           = 600,
  92                         .target_residency       = 1000,
  93                         .flags  = CPUIDLE_FLAG_TIMER_STOP,
  94                         .name   = "power-gated",
  95                         .desc   = "core power gated",
  96                 },
  97         },
  98         .state_count            = STATE_COUNT,
  99         .safe_state_index       = 0,
 100 };
 101 
 102 static void __init cps_cpuidle_unregister(void)
 103 {
 104         int cpu;
 105         struct cpuidle_device *device;
 106 
 107         for_each_possible_cpu(cpu) {
 108                 device = &per_cpu(cpuidle_dev, cpu);
 109                 cpuidle_unregister_device(device);
 110         }
 111 
 112         cpuidle_unregister_driver(&cps_driver);
 113 }
 114 
 115 static int __init cps_cpuidle_init(void)
 116 {
 117         int err, cpu, i;
 118         struct cpuidle_device *device;
 119 
 120         /* Detect supported states */
 121         if (!cps_pm_support_state(CPS_PM_POWER_GATED))
 122                 cps_driver.state_count = STATE_CLOCK_GATED + 1;
 123         if (!cps_pm_support_state(CPS_PM_CLOCK_GATED))
 124                 cps_driver.state_count = STATE_NC_WAIT + 1;
 125         if (!cps_pm_support_state(CPS_PM_NC_WAIT))
 126                 cps_driver.state_count = STATE_WAIT + 1;
 127 
 128         /* Inform the user if some states are unavailable */
 129         if (cps_driver.state_count < STATE_COUNT) {
 130                 pr_info("cpuidle-cps: limited to ");
 131                 switch (cps_driver.state_count - 1) {
 132                 case STATE_WAIT:
 133                         pr_cont("coherent wait\n");
 134                         break;
 135                 case STATE_NC_WAIT:
 136                         pr_cont("non-coherent wait\n");
 137                         break;
 138                 case STATE_CLOCK_GATED:
 139                         pr_cont("clock gating\n");
 140                         break;
 141                 }
 142         }
 143 
 144         /*
 145          * Set the coupled flag on the appropriate states if this system
 146          * requires it.
 147          */
 148         if (coupled_coherence)
 149                 for (i = STATE_NC_WAIT; i < cps_driver.state_count; i++)
 150                         cps_driver.states[i].flags |= CPUIDLE_FLAG_COUPLED;
 151 
 152         err = cpuidle_register_driver(&cps_driver);
 153         if (err) {
 154                 pr_err("Failed to register CPS cpuidle driver\n");
 155                 return err;
 156         }
 157 
 158         for_each_possible_cpu(cpu) {
 159                 device = &per_cpu(cpuidle_dev, cpu);
 160                 device->cpu = cpu;
 161 #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
 162                 cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);
 163 #endif
 164 
 165                 err = cpuidle_register_device(device);
 166                 if (err) {
 167                         pr_err("Failed to register CPU%d cpuidle device\n",
 168                                cpu);
 169                         goto err_out;
 170                 }
 171         }
 172 
 173         return 0;
 174 err_out:
 175         cps_cpuidle_unregister();
 176         return err;
 177 }
 178 device_initcall(cps_cpuidle_init);

/* [<][>][^][v][top][bottom][index][help] */