Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2014 Imagination Technologies
0004  * Author: Paul Burton <paul.burton@mips.com>
0005  */
0006 
0007 #include <linux/cpu_pm.h>
0008 #include <linux/cpuidle.h>
0009 #include <linux/init.h>
0010 
0011 #include <asm/idle.h>
0012 #include <asm/pm-cps.h>
0013 
0014 /* Enumeration of the various idle states this driver may enter */
0015 enum cps_idle_state {
0016     STATE_WAIT = 0,     /* MIPS wait instruction, coherent */
0017     STATE_NC_WAIT,      /* MIPS wait instruction, non-coherent */
0018     STATE_CLOCK_GATED,  /* Core clock gated */
0019     STATE_POWER_GATED,  /* Core power gated */
0020     STATE_COUNT
0021 };
0022 
0023 static int cps_nc_enter(struct cpuidle_device *dev,
0024             struct cpuidle_driver *drv, int index)
0025 {
0026     enum cps_pm_state pm_state;
0027     int err;
0028 
0029     /*
0030      * At least one core must remain powered up & clocked in order for the
0031      * system to have any hope of functioning.
0032      *
0033      * TODO: don't treat core 0 specially, just prevent the final core
0034      * TODO: remap interrupt affinity temporarily
0035      */
0036     if (cpus_are_siblings(0, dev->cpu) && (index > STATE_NC_WAIT))
0037         index = STATE_NC_WAIT;
0038 
0039     /* Select the appropriate cps_pm_state */
0040     switch (index) {
0041     case STATE_NC_WAIT:
0042         pm_state = CPS_PM_NC_WAIT;
0043         break;
0044     case STATE_CLOCK_GATED:
0045         pm_state = CPS_PM_CLOCK_GATED;
0046         break;
0047     case STATE_POWER_GATED:
0048         pm_state = CPS_PM_POWER_GATED;
0049         break;
0050     default:
0051         BUG();
0052         return -EINVAL;
0053     }
0054 
0055     /* Notify listeners the CPU is about to power down */
0056     if ((pm_state == CPS_PM_POWER_GATED) && cpu_pm_enter())
0057         return -EINTR;
0058 
0059     /* Enter that state */
0060     err = cps_pm_enter_state(pm_state);
0061 
0062     /* Notify listeners the CPU is back up */
0063     if (pm_state == CPS_PM_POWER_GATED)
0064         cpu_pm_exit();
0065 
0066     return err ?: index;
0067 }
0068 
0069 static struct cpuidle_driver cps_driver = {
0070     .name           = "cpc_cpuidle",
0071     .owner          = THIS_MODULE,
0072     .states = {
0073         [STATE_WAIT] = MIPS_CPUIDLE_WAIT_STATE,
0074         [STATE_NC_WAIT] = {
0075             .enter  = cps_nc_enter,
0076             .exit_latency       = 200,
0077             .target_residency   = 450,
0078             .name   = "nc-wait",
0079             .desc   = "non-coherent MIPS wait",
0080         },
0081         [STATE_CLOCK_GATED] = {
0082             .enter  = cps_nc_enter,
0083             .exit_latency       = 300,
0084             .target_residency   = 700,
0085             .flags  = CPUIDLE_FLAG_TIMER_STOP,
0086             .name   = "clock-gated",
0087             .desc   = "core clock gated",
0088         },
0089         [STATE_POWER_GATED] = {
0090             .enter  = cps_nc_enter,
0091             .exit_latency       = 600,
0092             .target_residency   = 1000,
0093             .flags  = CPUIDLE_FLAG_TIMER_STOP,
0094             .name   = "power-gated",
0095             .desc   = "core power gated",
0096         },
0097     },
0098     .state_count        = STATE_COUNT,
0099     .safe_state_index   = 0,
0100 };
0101 
0102 static void __init cps_cpuidle_unregister(void)
0103 {
0104     int cpu;
0105     struct cpuidle_device *device;
0106 
0107     for_each_possible_cpu(cpu) {
0108         device = &per_cpu(cpuidle_dev, cpu);
0109         cpuidle_unregister_device(device);
0110     }
0111 
0112     cpuidle_unregister_driver(&cps_driver);
0113 }
0114 
0115 static int __init cps_cpuidle_init(void)
0116 {
0117     int err, cpu, i;
0118     struct cpuidle_device *device;
0119 
0120     /* Detect supported states */
0121     if (!cps_pm_support_state(CPS_PM_POWER_GATED))
0122         cps_driver.state_count = STATE_CLOCK_GATED + 1;
0123     if (!cps_pm_support_state(CPS_PM_CLOCK_GATED))
0124         cps_driver.state_count = STATE_NC_WAIT + 1;
0125     if (!cps_pm_support_state(CPS_PM_NC_WAIT))
0126         cps_driver.state_count = STATE_WAIT + 1;
0127 
0128     /* Inform the user if some states are unavailable */
0129     if (cps_driver.state_count < STATE_COUNT) {
0130         pr_info("cpuidle-cps: limited to ");
0131         switch (cps_driver.state_count - 1) {
0132         case STATE_WAIT:
0133             pr_cont("coherent wait\n");
0134             break;
0135         case STATE_NC_WAIT:
0136             pr_cont("non-coherent wait\n");
0137             break;
0138         case STATE_CLOCK_GATED:
0139             pr_cont("clock gating\n");
0140             break;
0141         }
0142     }
0143 
0144     /*
0145      * Set the coupled flag on the appropriate states if this system
0146      * requires it.
0147      */
0148     if (coupled_coherence)
0149         for (i = STATE_NC_WAIT; i < cps_driver.state_count; i++)
0150             cps_driver.states[i].flags |= CPUIDLE_FLAG_COUPLED;
0151 
0152     err = cpuidle_register_driver(&cps_driver);
0153     if (err) {
0154         pr_err("Failed to register CPS cpuidle driver\n");
0155         return err;
0156     }
0157 
0158     for_each_possible_cpu(cpu) {
0159         device = &per_cpu(cpuidle_dev, cpu);
0160         device->cpu = cpu;
0161 #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
0162         cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);
0163 #endif
0164 
0165         err = cpuidle_register_device(device);
0166         if (err) {
0167             pr_err("Failed to register CPU%d cpuidle device\n",
0168                    cpu);
0169             goto err_out;
0170         }
0171     }
0172 
0173     return 0;
0174 err_out:
0175     cps_cpuidle_unregister();
0176     return err;
0177 }
0178 device_initcall(cps_cpuidle_init);