From: Nishanth Menon Date: Mon, 29 Oct 2012 20:01:45 +0000 (-0500) Subject: PM / devfreq: map devfreq drivers to governor using name X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=1b5c1be2c88e8445a20fa1929e26c37e7ca8c926;p=linux-beck.git PM / devfreq: map devfreq drivers to governor using name Allow devfreq drivers to register a preferred governor name and when the devfreq governor loads itself at a later point required drivers are managed appropriately, at the time of unload of a devfreq governor, stop managing those drivers as well. Since the governor structures do not need to be exposed anymore, remove the definitions and make them static NOTE: devfreq_list_lock is now used to protect governor start and stop - as this allows us to protect governors and devfreq with the proper dependencies as needed. As part of this change, change the registration of exynos bus driver to request for ondemand using the governor name. Cc: Rajagopal Venkat Cc: MyungJoo Ham Cc: Kyungmin Park Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon [Merge conflict resolved by MyungJoo Ham] Signed-off-by: MyungJoo Ham --- diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 679ac424472f..0d7be03d561f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -159,6 +159,9 @@ int update_devfreq(struct devfreq *devfreq) return -EINVAL; } + if (!devfreq->governor) + return -EINVAL; + /* Reevaluate the proper frequency */ err = devfreq->governor->get_target_freq(devfreq, &freq); if (err) @@ -379,7 +382,9 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip) list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); - devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); + if (devfreq->governor) + devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); @@ -412,19 +417,20 @@ static void devfreq_dev_release(struct device *dev) * devfreq_add_device() - Add devfreq feature to the device * @dev: the device to add devfreq feature. * @profile: device-specific profile to run devfreq. - * @governor: the policy to choose frequency. + * @governor_name: name of the policy to choose frequency. * @data: private data for the governor. The devfreq framework does not * touch this value. */ struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, - const struct devfreq_governor *governor, + const char *governor_name, void *data) { struct devfreq *devfreq; + struct devfreq_governor *governor; int err = 0; - if (!dev || !profile || !governor) { + if (!dev || !profile || !governor_name) { dev_err(dev, "%s: Invalid parameters.\n", __func__); return ERR_PTR(-EINVAL); } @@ -452,7 +458,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->dev.class = devfreq_class; devfreq->dev.release = devfreq_dev_release; devfreq->profile = profile; - devfreq->governor = governor; + strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); devfreq->previous_freq = profile->initial_freq; devfreq->data = data; devfreq->nb.notifier_call = devfreq_notifier_call; @@ -478,10 +484,14 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_lock(&devfreq_list_lock); list_add(&devfreq->node, &devfreq_list); - mutex_unlock(&devfreq_list_lock); - err = devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_START, NULL); + governor = find_devfreq_governor(devfreq->governor_name); + if (!IS_ERR(governor)) + devfreq->governor = governor; + if (devfreq->governor) + err = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_START, NULL); + mutex_unlock(&devfreq_list_lock); if (err) { dev_err(dev, "%s: Unable to start governor for the device\n", __func__); @@ -524,6 +534,9 @@ int devfreq_suspend_device(struct devfreq *devfreq) if (!devfreq) return -EINVAL; + if (!devfreq->governor) + return 0; + return devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_SUSPEND, NULL); } @@ -538,6 +551,9 @@ int devfreq_resume_device(struct devfreq *devfreq) if (!devfreq) return -EINVAL; + if (!devfreq->governor) + return 0; + return devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_RESUME, NULL); } @@ -550,6 +566,7 @@ EXPORT_SYMBOL(devfreq_resume_device); int devfreq_add_governor(struct devfreq_governor *governor) { struct devfreq_governor *g; + struct devfreq *devfreq; int err = 0; if (!governor) { @@ -568,6 +585,38 @@ int devfreq_add_governor(struct devfreq_governor *governor) list_add(&governor->node, &devfreq_governor_list); + list_for_each_entry(devfreq, &devfreq_list, node) { + int ret = 0; + struct device *dev = devfreq->dev.parent; + + if (!strncmp(devfreq->governor_name, governor->name, + DEVFREQ_NAME_LEN)) { + /* The following should never occur */ + if (devfreq->governor) { + dev_warn(dev, + "%s: Governor %s already present\n", + __func__, devfreq->governor->name); + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev, + "%s: Governor %s stop = %d\n", + __func__, + devfreq->governor->name, ret); + } + /* Fall through */ + } + devfreq->governor = governor; + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_START, NULL); + if (ret) { + dev_warn(dev, "%s: Governor %s start=%d\n", + __func__, devfreq->governor->name, + ret); + } + } + } + err_out: mutex_unlock(&devfreq_list_lock); @@ -582,6 +631,7 @@ EXPORT_SYMBOL(devfreq_add_governor); int devfreq_remove_governor(struct devfreq_governor *governor) { struct devfreq_governor *g; + struct devfreq *devfreq; int err = 0; if (!governor) { @@ -597,6 +647,29 @@ int devfreq_remove_governor(struct devfreq_governor *governor) err = -EINVAL; goto err_out; } + list_for_each_entry(devfreq, &devfreq_list, node) { + int ret; + struct device *dev = devfreq->dev.parent; + + if (!strncmp(devfreq->governor_name, governor->name, + DEVFREQ_NAME_LEN)) { + /* we should have a devfreq governor! */ + if (!devfreq->governor) { + dev_warn(dev, "%s: Governor %s NOT present\n", + __func__, governor->name); + continue; + /* Fall through */ + } + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev, "%s: Governor %s stop=%d\n", + __func__, devfreq->governor->name, + ret); + } + devfreq->governor = NULL; + } + } list_del(&governor->node); err_out: @@ -609,6 +682,9 @@ EXPORT_SYMBOL(devfreq_remove_governor); static ssize_t show_governor(struct device *dev, struct device_attribute *attr, char *buf) { + if (!to_devfreq(dev)->governor) + return -EINVAL; + return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name); } @@ -645,6 +721,9 @@ static ssize_t store_polling_interval(struct device *dev, unsigned int value; int ret; + if (!df->governor) + return -EINVAL; + ret = sscanf(buf, "%u", &value); if (ret != 1) return -EINVAL; diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c index 68145316c49c..b8ac28497b37 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos4_bus.c @@ -1040,7 +1040,7 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev) busfreq_mon_reset(data); data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, - &devfreq_simple_ondemand, NULL); + "simple_ondemand", NULL); if (IS_ERR(data->devfreq)) return PTR_ERR(data->devfreq); diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index db8ff77dbed2..865a36956917 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -40,7 +40,7 @@ static int devfreq_performance_handler(struct devfreq *devfreq, return ret; } -const struct devfreq_governor devfreq_performance = { +static struct devfreq_governor devfreq_performance = { .name = "performance", .get_target_freq = devfreq_performance_func, .event_handler = devfreq_performance_handler, diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index 30f0fca8d635..8612c0f96b79 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -37,7 +37,7 @@ static int devfreq_powersave_handler(struct devfreq *devfreq, return ret; } -const struct devfreq_governor devfreq_powersave = { +static struct devfreq_governor devfreq_powersave = { .name = "powersave", .get_target_freq = devfreq_powersave_func, .event_handler = devfreq_powersave_handler, diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 85f9ed531b1e..a870a24bb56b 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -120,7 +120,7 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, return 0; } -const struct devfreq_governor devfreq_simple_ondemand = { +static struct devfreq_governor devfreq_simple_ondemand = { .name = "simple_ondemand", .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 110f178fec04..34fb80f50cf6 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -135,7 +135,7 @@ static int devfreq_userspace_handler(struct devfreq *devfreq, return ret; } -const struct devfreq_governor devfreq_userspace = { +static struct devfreq_governor devfreq_userspace = { .name = "userspace", .get_target_freq = devfreq_userspace_func, .event_handler = devfreq_userspace_handler, diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 6484a3f8dda8..235248cb2c93 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -125,6 +125,7 @@ struct devfreq_governor { * using devfreq. * @profile: device-specific devfreq profile * @governor: method how to choose frequency based on the usage. + * @governor_name: devfreq governor name for use with this devfreq * @nb: notifier block used to notify devfreq object that it should * reevaluate operable frequencies. Devfreq users may use * devfreq.nb to the corresponding register notifier call chain. @@ -155,6 +156,7 @@ struct devfreq { struct device dev; struct devfreq_dev_profile *profile; const struct devfreq_governor *governor; + char governor_name[DEVFREQ_NAME_LEN]; struct notifier_block nb; struct delayed_work work; @@ -176,7 +178,7 @@ struct devfreq { #if defined(CONFIG_PM_DEVFREQ) extern struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, - const struct devfreq_governor *governor, + const char *governor_name, void *data); extern int devfreq_remove_device(struct devfreq *devfreq); extern int devfreq_suspend_device(struct devfreq *devfreq); @@ -190,17 +192,7 @@ extern int devfreq_register_opp_notifier(struct device *dev, extern int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq); -#ifdef CONFIG_DEVFREQ_GOV_POWERSAVE -extern const struct devfreq_governor devfreq_powersave; -#endif -#ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE -extern const struct devfreq_governor devfreq_performance; -#endif -#ifdef CONFIG_DEVFREQ_GOV_USERSPACE -extern const struct devfreq_governor devfreq_userspace; -#endif #ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND -extern const struct devfreq_governor devfreq_simple_ondemand; /** * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq * and devfreq_add_device @@ -223,7 +215,7 @@ struct devfreq_simple_ondemand_data { #else /* !CONFIG_PM_DEVFREQ */ static struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, - struct devfreq_governor *governor, + const char *governor_name, void *data) { return NULL; @@ -262,11 +254,6 @@ static int devfreq_unregister_opp_notifier(struct device *dev, return -EINVAL; } -#define devfreq_powersave NULL -#define devfreq_performance NULL -#define devfreq_userspace NULL -#define devfreq_simple_ondemand NULL - #endif /* CONFIG_PM_DEVFREQ */ #endif /* __LINUX_DEVFREQ_H__ */