/** * cpuidle_register_device - registers a CPU's idle PM feature * @dev: the cpu */ int cpuidle_register_device(struct cpuidle_device *dev) { int ret; struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); if (!sys_dev) return -EINVAL; if (!try_module_get(cpuidle_curr_driver->owner)) return -EINVAL; init_completion(&dev->kobj_unregister); mutex_lock(&cpuidle_lock); poll_idle_init(dev); per_cpu(cpuidle_devices, dev->cpu) = dev; list_add(&dev->device_list, &cpuidle_detected_devices); if ((ret = cpuidle_add_sysfs(sys_dev))) { mutex_unlock(&cpuidle_lock); module_put(cpuidle_curr_driver->owner); return ret; } cpuidle_enable_device(dev); cpuidle_install_idle_handler(); mutex_unlock(&cpuidle_lock); return 0; }
/** * __cpuidle_register_device - internal register function called before register * and enable routines * @dev: the cpu * * cpuidle_lock mutex must be held before this is called */ static int __cpuidle_register_device(struct cpuidle_device *dev) { int ret; struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); if (!sys_dev) return -EINVAL; if (!try_module_get(cpuidle_driver->owner)) return -EINVAL; init_completion(&dev->kobj_unregister); poll_idle_init(dev); per_cpu(cpuidle_devices, dev->cpu) = dev; list_add(&dev->device_list, &cpuidle_detected_devices); if ((ret = cpuidle_add_sysfs(sys_dev))) { module_put(cpuidle_driver->owner); return ret; } dev->registered = 1; return 0; }
/** * cpuidle_enable_device - enables idle PM for a CPU * @dev: the CPU * * This function must be called between cpuidle_pause_and_lock and * cpuidle_resume_and_unlock when used externally. */ int cpuidle_enable_device(struct cpuidle_device *dev) { int ret, i; struct cpuidle_driver *drv; if (!dev) return -EINVAL; if (dev->enabled) return 0; drv = cpuidle_get_cpu_driver(dev); if (!drv || !cpuidle_curr_governor) return -EIO; if (!dev->state_count) dev->state_count = drv->state_count; if (dev->registered == 0) { ret = __cpuidle_register_device(dev); if (ret) return ret; } cpuidle_enter_ops = drv->en_core_tk_irqen ? cpuidle_enter_tk : cpuidle_enter; poll_idle_init(drv); ret = cpuidle_add_device_sysfs(dev); if (ret) return ret; if (cpuidle_curr_governor->enable && (ret = cpuidle_curr_governor->enable(drv, dev))) goto fail_sysfs; for (i = 0; i < dev->state_count; i++) { dev->states_usage[i].usage = 0; dev->states_usage[i].time = 0; } dev->last_residency = 0; smp_wmb(); dev->enabled = 1; enabled_devices++; return 0; fail_sysfs: cpuidle_remove_device_sysfs(dev); return ret; }
/** * __cpuidle_register_device - internal register function called before register * and enable routines * @dev: the cpu * * cpuidle_lock mutex must be held before this is called */ static int __cpuidle_register_device(struct cpuidle_device *dev) { int ret; struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); if (!sys_dev) return -EINVAL; if (!try_module_get(cpuidle_driver->owner)) return -EINVAL; init_completion(&dev->kobj_unregister); poll_idle_init(dev); /* * cpuidle driver should set the dev->power_specified bit * before registering the device if the driver provides * power_usage numbers. * * For those devices whose ->power_specified is not set, * we fill in power_usage with decreasing values as the * cpuidle code has an implicit assumption that state Cn * uses less power than C(n-1). * * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned * an power value of -1. So we use -2, -3, etc, for other * c-states. */ if (!dev->power_specified) { int i; for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) dev->states[i].power_usage = -1 - i; } per_cpu(cpuidle_devices, dev->cpu) = dev; list_add(&dev->device_list, &cpuidle_detected_devices); if ((ret = cpuidle_add_sysfs(sys_dev))) { module_put(cpuidle_driver->owner); return ret; } dev->registered = 1; return 0; }
/** * cpuidle_enable_device - enables idle PM for a CPU * @dev: the CPU * * This function must be called between cpuidle_pause_and_lock and * cpuidle_resume_and_unlock when used externally. */ int cpuidle_enable_device(struct cpuidle_device *dev) { int ret, i; if (dev->enabled) return 0; if (!cpuidle_get_driver() || !cpuidle_curr_governor) return -EIO; if (!dev->state_count) return -EINVAL; if (dev->registered == 0) { ret = __cpuidle_register_device(dev); if (ret) return ret; } poll_idle_init(dev); if ((ret = cpuidle_add_state_sysfs(dev))) return ret; if (cpuidle_curr_governor->enable && (ret = cpuidle_curr_governor->enable(dev))) goto fail_sysfs; for (i = 0; i < dev->state_count; i++) { dev->states[i].usage = 0; dev->states[i].time = 0; } dev->last_residency = 0; dev->last_state = NULL; smp_wmb(); dev->enabled = 1; enabled_devices++; return 0; fail_sysfs: cpuidle_remove_state_sysfs(dev); return ret; }