diff options
Diffstat (limited to 'arch/sh/kernel/cpu/shmobile')
| -rw-r--r-- | arch/sh/kernel/cpu/shmobile/Makefile | 1 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/shmobile/cpuidle.c | 114 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/shmobile/pm.c | 6 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/shmobile/pm_runtime.c | 308 | 
4 files changed, 51 insertions, 378 deletions
diff --git a/arch/sh/kernel/cpu/shmobile/Makefile b/arch/sh/kernel/cpu/shmobile/Makefile index a39f88ea1a8..e8a5111e848 100644 --- a/arch/sh/kernel/cpu/shmobile/Makefile +++ b/arch/sh/kernel/cpu/shmobile/Makefile @@ -5,4 +5,3 @@  # Power Management & Sleep mode  obj-$(CONFIG_PM)	+= pm.o sleep.o  obj-$(CONFIG_CPU_IDLE)	+= cpuidle.o -obj-$(CONFIG_PM_RUNTIME)	+= pm_runtime.o diff --git a/arch/sh/kernel/cpu/shmobile/cpuidle.c b/arch/sh/kernel/cpu/shmobile/cpuidle.c index 83972aa319c..e3abfd4277e 100644 --- a/arch/sh/kernel/cpu/shmobile/cpuidle.c +++ b/arch/sh/kernel/cpu/shmobile/cpuidle.c @@ -14,9 +14,9 @@  #include <linux/io.h>  #include <linux/suspend.h>  #include <linux/cpuidle.h> +#include <linux/export.h>  #include <asm/suspend.h>  #include <asm/uaccess.h> -#include <asm/hwblk.h>  static unsigned long cpuidle_mode[] = {  	SUSP_SH_SLEEP, /* regular sleep mode */ @@ -25,11 +25,11 @@ static unsigned long cpuidle_mode[] = {  };  static int cpuidle_sleep_enter(struct cpuidle_device *dev, -			       struct cpuidle_state *state) +				struct cpuidle_driver *drv, +				int index)  { -	unsigned long allowed_mode = arch_hwblk_sleep_mode(); -	ktime_t before, after; -	int requested_state = state - &dev->states[0]; +	unsigned long allowed_mode = SUSP_SH_SLEEP; +	int requested_state = index;  	int allowed_state;  	int k; @@ -46,74 +46,56 @@ static int cpuidle_sleep_enter(struct cpuidle_device *dev,  	 */  	k = min_t(int, allowed_state, requested_state); -	dev->last_state = &dev->states[k]; -	before = ktime_get();  	sh_mobile_call_standby(cpuidle_mode[k]); -	after = ktime_get(); -	return ktime_to_ns(ktime_sub(after, before)) >> 10; + +	return k;  } -static struct cpuidle_device cpuidle_dev;  static struct cpuidle_driver cpuidle_driver = { -	.name =		"sh_idle", -	.owner =	THIS_MODULE, +	.name   = "sh_idle", +	.owner  = THIS_MODULE, +	.states = { +		{ +			.exit_latency = 1, +			.target_residency = 1 * 2, +			.power_usage = 3, +			.flags = CPUIDLE_FLAG_TIME_VALID, +			.enter = cpuidle_sleep_enter, +			.name = "C1", +			.desc = "SuperH Sleep Mode", +		}, +		{ +			.exit_latency = 100, +			.target_residency = 1 * 2, +			.power_usage = 1, +			.flags = CPUIDLE_FLAG_TIME_VALID, +			.enter = cpuidle_sleep_enter, +			.name = "C2", +			.desc = "SuperH Sleep Mode [SF]", +			.disabled = true, +		}, +		{ +			.exit_latency = 2300, +			.target_residency = 1 * 2, +			.power_usage = 1, +			.flags = CPUIDLE_FLAG_TIME_VALID, +			.enter = cpuidle_sleep_enter, +			.name = "C3", +			.desc = "SuperH Mobile Standby Mode [SF]", +			.disabled = true, +		}, +	}, +	.safe_state_index = 0, +	.state_count = 3,  }; -void sh_mobile_setup_cpuidle(void) +int __init sh_mobile_setup_cpuidle(void)  { -	struct cpuidle_device *dev = &cpuidle_dev; -	struct cpuidle_state *state; -	int i; - -	cpuidle_register_driver(&cpuidle_driver); - -	for (i = 0; i < CPUIDLE_STATE_MAX; i++) { -		dev->states[i].name[0] = '\0'; -		dev->states[i].desc[0] = '\0'; -	} - -	i = CPUIDLE_DRIVER_STATE_START; - -	state = &dev->states[i++]; -	snprintf(state->name, CPUIDLE_NAME_LEN, "C0"); -	strncpy(state->desc, "SuperH Sleep Mode", CPUIDLE_DESC_LEN); -	state->exit_latency = 1; -	state->target_residency = 1 * 2; -	state->power_usage = 3; -	state->flags = 0; -	state->flags |= CPUIDLE_FLAG_SHALLOW; -	state->flags |= CPUIDLE_FLAG_TIME_VALID; -	state->enter = cpuidle_sleep_enter; - -	dev->safe_state = state; - -	if (sh_mobile_sleep_supported & SUSP_SH_SF) { -		state = &dev->states[i++]; -		snprintf(state->name, CPUIDLE_NAME_LEN, "C1"); -		strncpy(state->desc, "SuperH Sleep Mode [SF]", -			CPUIDLE_DESC_LEN); -		state->exit_latency = 100; -		state->target_residency = 1 * 2; -		state->power_usage = 1; -		state->flags = 0; -		state->flags |= CPUIDLE_FLAG_TIME_VALID; -		state->enter = cpuidle_sleep_enter; -	} - -	if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) { -		state = &dev->states[i++]; -		snprintf(state->name, CPUIDLE_NAME_LEN, "C2"); -		strncpy(state->desc, "SuperH Mobile Standby Mode [SF]", -			CPUIDLE_DESC_LEN); -		state->exit_latency = 2300; -		state->target_residency = 1 * 2; -		state->power_usage = 1; -		state->flags = 0; -		state->flags |= CPUIDLE_FLAG_TIME_VALID; -		state->enter = cpuidle_sleep_enter; -	} +	if (sh_mobile_sleep_supported & SUSP_SH_SF) +		cpuidle_driver.states[1].disabled = false; -	dev->state_count = i; +	if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) +		cpuidle_driver.states[2].disabled = false; -	cpuidle_register_device(dev); +	return cpuidle_register(&cpuidle_driver, NULL);  } diff --git a/arch/sh/kernel/cpu/shmobile/pm.c b/arch/sh/kernel/cpu/shmobile/pm.c index e5596871270..ac37b7234f8 100644 --- a/arch/sh/kernel/cpu/shmobile/pm.c +++ b/arch/sh/kernel/cpu/shmobile/pm.c @@ -16,6 +16,7 @@  #include <asm/suspend.h>  #include <asm/uaccess.h>  #include <asm/cacheflush.h> +#include <asm/bl_bit.h>  /*   * Notifier lists for pre/post sleep notification @@ -141,7 +142,7 @@ static int sh_pm_enter(suspend_state_t state)  	return 0;  } -static struct platform_suspend_ops sh_pm_ops = { +static const struct platform_suspend_ops sh_pm_ops = {  	.enter          = sh_pm_enter,  	.valid          = suspend_valid_only_mem,  }; @@ -149,8 +150,7 @@ static struct platform_suspend_ops sh_pm_ops = {  static int __init sh_pm_init(void)  {  	suspend_set_ops(&sh_pm_ops); -	sh_mobile_setup_cpuidle(); -	return 0; +	return sh_mobile_setup_cpuidle();  }  late_initcall(sh_pm_init); diff --git a/arch/sh/kernel/cpu/shmobile/pm_runtime.c b/arch/sh/kernel/cpu/shmobile/pm_runtime.c deleted file mode 100644 index 6dcb8166a64..00000000000 --- a/arch/sh/kernel/cpu/shmobile/pm_runtime.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * arch/sh/kernel/cpu/shmobile/pm_runtime.c - * - * Runtime PM support code for SuperH Mobile - * - *  Copyright (C) 2009 Magnus Damm - * - * This file is subject to the terms and conditions of the GNU General Public - * License.  See the file "COPYING" in the main directory of this archive - * for more details. - */ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/io.h> -#include <linux/pm_runtime.h> -#include <linux/platform_device.h> -#include <linux/mutex.h> -#include <asm/hwblk.h> - -static DEFINE_SPINLOCK(hwblk_lock); -static LIST_HEAD(hwblk_idle_list); -static struct work_struct hwblk_work; - -extern struct hwblk_info *hwblk_info; - -static void platform_pm_runtime_not_idle(struct platform_device *pdev) -{ -	unsigned long flags; - -	/* remove device from idle list */ -	spin_lock_irqsave(&hwblk_lock, flags); -	if (test_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags)) { -		list_del(&pdev->archdata.entry); -		__clear_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags); -	} -	spin_unlock_irqrestore(&hwblk_lock, flags); -} - -static int __platform_pm_runtime_resume(struct platform_device *pdev) -{ -	struct device *d = &pdev->dev; -	struct pdev_archdata *ad = &pdev->archdata; -	int hwblk = ad->hwblk_id; -	int ret = -ENOSYS; - -	dev_dbg(d, "__platform_pm_runtime_resume() [%d]\n", hwblk); - -	if (d->driver) { -		hwblk_enable(hwblk_info, hwblk); -		ret = 0; - -		if (test_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags)) { -			if (d->driver->pm && d->driver->pm->runtime_resume) -				ret = d->driver->pm->runtime_resume(d); - -			if (!ret) -				clear_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags); -			else -				hwblk_disable(hwblk_info, hwblk); -		} -	} - -	dev_dbg(d, "__platform_pm_runtime_resume() [%d] - returns %d\n", -		hwblk, ret); - -	return ret; -} - -static int __platform_pm_runtime_suspend(struct platform_device *pdev) -{ -	struct device *d = &pdev->dev; -	struct pdev_archdata *ad = &pdev->archdata; -	int hwblk = ad->hwblk_id; -	int ret = -ENOSYS; - -	dev_dbg(d, "__platform_pm_runtime_suspend() [%d]\n", hwblk); - -	if (d->driver) { -		BUG_ON(!test_bit(PDEV_ARCHDATA_FLAG_IDLE, &ad->flags)); -		ret = 0; - -		if (d->driver->pm && d->driver->pm->runtime_suspend) { -			hwblk_enable(hwblk_info, hwblk); -			ret = d->driver->pm->runtime_suspend(d); -			hwblk_disable(hwblk_info, hwblk); -		} - -		if (!ret) { -			set_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags); -			platform_pm_runtime_not_idle(pdev); -			hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE); -		} -	} - -	dev_dbg(d, "__platform_pm_runtime_suspend() [%d] - returns %d\n", -		hwblk, ret); - -	return ret; -} - -static void platform_pm_runtime_work(struct work_struct *work) -{ -	struct platform_device *pdev; -	unsigned long flags; -	int ret; - -	/* go through the idle list and suspend one device at a time */ -	do { -		spin_lock_irqsave(&hwblk_lock, flags); -		if (list_empty(&hwblk_idle_list)) -			pdev = NULL; -		else -			pdev = list_first_entry(&hwblk_idle_list, -						struct platform_device, -						archdata.entry); -		spin_unlock_irqrestore(&hwblk_lock, flags); - -		if (pdev) { -			mutex_lock(&pdev->archdata.mutex); -			ret = __platform_pm_runtime_suspend(pdev); - -			/* at this point the platform device may be: -			 * suspended: ret = 0, FLAG_SUSP set, clock stopped -			 * failed: ret < 0, FLAG_IDLE set, clock stopped -			 */ -			mutex_unlock(&pdev->archdata.mutex); -		} else { -			ret = -ENODEV; -		} -	} while (!ret); -} - -/* this function gets called from cpuidle context when all devices in the - * main power domain are unused but some are counted as idle, ie the hwblk - * counter values are (HWBLK_CNT_USAGE == 0) && (HWBLK_CNT_IDLE != 0) - */ -void platform_pm_runtime_suspend_idle(void) -{ -	queue_work(pm_wq, &hwblk_work); -} - -int platform_pm_runtime_suspend(struct device *dev) -{ -	struct platform_device *pdev = to_platform_device(dev); -	struct pdev_archdata *ad = &pdev->archdata; -	unsigned long flags; -	int hwblk = ad->hwblk_id; -	int ret = 0; - -	dev_dbg(dev, "platform_pm_runtime_suspend() [%d]\n", hwblk); - -	/* ignore off-chip platform devices */ -	if (!hwblk) -		goto out; - -	/* interrupt context not allowed */ -	might_sleep(); - -	/* catch misconfigured drivers not starting with resume */ -	if (test_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags)) { -		ret = -EINVAL; -		goto out; -	} - -	/* serialize */ -	mutex_lock(&ad->mutex); - -	/* disable clock */ -	hwblk_disable(hwblk_info, hwblk); - -	/* put device on idle list */ -	spin_lock_irqsave(&hwblk_lock, flags); -	list_add_tail(&pdev->archdata.entry, &hwblk_idle_list); -	__set_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags); -	spin_unlock_irqrestore(&hwblk_lock, flags); - -	/* increase idle count */ -	hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_IDLE); - -	/* at this point the platform device is: -	 * idle: ret = 0, FLAG_IDLE set, clock stopped -	 */ -	mutex_unlock(&ad->mutex); - -out: -	dev_dbg(dev, "platform_pm_runtime_suspend() [%d] returns %d\n", -		hwblk, ret); - -	return ret; -} - -int platform_pm_runtime_resume(struct device *dev) -{ -	struct platform_device *pdev = to_platform_device(dev); -	struct pdev_archdata *ad = &pdev->archdata; -	int hwblk = ad->hwblk_id; -	int ret = 0; - -	dev_dbg(dev, "platform_pm_runtime_resume() [%d]\n", hwblk); - -	/* ignore off-chip platform devices */ -	if (!hwblk) -		goto out; - -	/* interrupt context not allowed */ -	might_sleep(); - -	/* serialize */ -	mutex_lock(&ad->mutex); - -	/* make sure device is removed from idle list */ -	platform_pm_runtime_not_idle(pdev); - -	/* decrease idle count */ -	if (!test_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags) && -	    !test_bit(PDEV_ARCHDATA_FLAG_SUSP, &pdev->archdata.flags)) -		hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE); - -	/* resume the device if needed */ -	ret = __platform_pm_runtime_resume(pdev); - -	/* the driver has been initialized now, so clear the init flag */ -	clear_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags); - -	/* at this point the platform device may be: -	 * resumed: ret = 0, flags = 0, clock started -	 * failed: ret < 0, FLAG_SUSP set, clock stopped -	 */ -	mutex_unlock(&ad->mutex); -out: -	dev_dbg(dev, "platform_pm_runtime_resume() [%d] returns %d\n", -		hwblk, ret); - -	return ret; -} - -int platform_pm_runtime_idle(struct device *dev) -{ -	struct platform_device *pdev = to_platform_device(dev); -	int hwblk = pdev->archdata.hwblk_id; -	int ret = 0; - -	dev_dbg(dev, "platform_pm_runtime_idle() [%d]\n", hwblk); - -	/* ignore off-chip platform devices */ -	if (!hwblk) -		goto out; - -	/* interrupt context not allowed, use pm_runtime_put()! */ -	might_sleep(); - -	/* suspend synchronously to disable clocks immediately */ -	ret = pm_runtime_suspend(dev); -out: -	dev_dbg(dev, "platform_pm_runtime_idle() [%d] done!\n", hwblk); -	return ret; -} - -static int platform_bus_notify(struct notifier_block *nb, -			       unsigned long action, void *data) -{ -	struct device *dev = data; -	struct platform_device *pdev = to_platform_device(dev); -	int hwblk = pdev->archdata.hwblk_id; - -	/* ignore off-chip platform devices */ -	if (!hwblk) -		return 0; - -	switch (action) { -	case BUS_NOTIFY_ADD_DEVICE: -		INIT_LIST_HEAD(&pdev->archdata.entry); -		mutex_init(&pdev->archdata.mutex); -		/* platform devices without drivers should be disabled */ -		hwblk_enable(hwblk_info, hwblk); -		hwblk_disable(hwblk_info, hwblk); -		/* make sure driver re-inits itself once */ -		__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags); -		break; -	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */ -	case BUS_NOTIFY_BOUND_DRIVER: -		/* keep track of number of devices in use per hwblk */ -		hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_DEVICES); -		break; -	case BUS_NOTIFY_UNBOUND_DRIVER: -		/* keep track of number of devices in use per hwblk */ -		hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_DEVICES); -		/* make sure driver re-inits itself once */ -		__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags); -		break; -	case BUS_NOTIFY_DEL_DEVICE: -		break; -	} -	return 0; -} - -static struct notifier_block platform_bus_notifier = { -	.notifier_call = platform_bus_notify -}; - -static int __init sh_pm_runtime_init(void) -{ -	INIT_WORK(&hwblk_work, platform_pm_runtime_work); - -	bus_register_notifier(&platform_bus_type, &platform_bus_notifier); -	return 0; -} -core_initcall(sh_pm_runtime_init);  | 
