* [PATCH 0/4] OMAP OPP layer @ 2010-08-11 0:07 Kevin Hilman 2010-08-11 0:07 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman 0 siblings, 1 reply; 21+ messages in thread From: Kevin Hilman @ 2010-08-11 0:07 UTC (permalink / raw) To: linux-omap This series introduces a layer to manage Operating Performance Points (OPPs) for OMAP SoCs. Special thanks to Nishanth for doing the bulk of the grunt work for this series and to Thara for doing significant review, testing and updates. Kevin Hilman (1): omap3: pm: remove OPP interfaces from OMAP PM layer Nishanth Menon (1): OMAP: introduce OPP layer for device-specific OPPs Paul Walmsley (1): omap: opp: twl/tps: Introduce TWL/TPS-specific code Thara Gopinath (1): OMAP: Remove dependency of generic opp layer on cpufreq. Documentation/arm/OMAP/omap_pm | 93 +++++ arch/arm/mach-omap2/io.c | 3 +- arch/arm/plat-omap/Makefile | 8 +- arch/arm/plat-omap/include/plat/omap-pm.h | 31 +- arch/arm/plat-omap/include/plat/opp.h | 160 ++++++++ arch/arm/plat-omap/include/plat/opp_twl_tps.h | 21 + arch/arm/plat-omap/omap-pm-noop.c | 11 +- arch/arm/plat-omap/opp.c | 513 +++++++++++++++++++++++++ arch/arm/plat-omap/opp_twl_tps.c | 41 ++ 9 files changed, 847 insertions(+), 34 deletions(-) create mode 100644 arch/arm/plat-omap/include/plat/opp.h create mode 100644 arch/arm/plat-omap/include/plat/opp_twl_tps.h create mode 100644 arch/arm/plat-omap/opp.c create mode 100644 arch/arm/plat-omap/opp_twl_tps.c -- 1.7.2.1 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-08-11 0:07 [PATCH 0/4] OMAP OPP layer Kevin Hilman @ 2010-08-11 0:07 ` Kevin Hilman 0 siblings, 0 replies; 21+ messages in thread From: Kevin Hilman @ 2010-08-11 0:07 UTC (permalink / raw) To: linux-omap Cc: Nishanth Menon, Benoit Cousson, Madhusudhan Chikkature Rajashekar, Phil Carmody, Roberto Granados Dorado, Santosh Shilimkar, Sergio Alberto Aguirre Rodriguez, Tero Kristo, Eduardo Valentin, Paul Walmsley, Romit Dasgupta, Sanjeev Premi, Thara Gopinath, Vishwanath BS From: Nishanth Menon <nm@ti.com> OMAP SOCs have a standard set of tuples consisting of frequency and voltage pairs that the device will support per voltage domain. These are called Operating Performance Points or OPPs. The actual definitions of OMAP Operating Points varies over silicon within the same family of devices. For a specific domain, you can have a set of {frequency, voltage} pairs. As the kernel boots and more information is available, a set of these are activated based on the precise nature of device the kernel boots up on. It is interesting to remember that each IP which belongs to a voltage domain may define their own set of OPPs on top of this. This introduces a common handling OPP mechanism accross all OMAPs. As a start this is used for OMAP3. Note: OPP is a concept that can be used in all OMAPs, it is hence introduced under plat-omap Contributions include: Sanjeev Premi for the initial concept: http://patchwork.kernel.org/patch/50998/ Kevin Hilman for converting original design to device-based Kevin Hilman and Paul Walmsey for cleaning up many of the function abstractions, improvements and data structure handling Romit Dasgupta for using enums instead of opp pointers Thara Gopinath, Eduardo Valentin and Vishwanath BS for fixes and cleanups. Discussions and comments from: http://marc.info/?l=linux-omap&m=126033945313269&w=2 http://marc.info/?l=linux-omap&m=125482970102327&w=2 http://marc.info/?t=125809247500002&r=1&w=2 http://marc.info/?l=linux-omap&m=126025973426007&w=2 incorporated. Cc: Benoit Cousson <b-cousson@ti.com> Cc: Madhusudhan Chikkature Rajashekar <madhu.cr@ti.com> Cc: Phil Carmody <ext-phil.2.carmody@nokia.com> Cc: Roberto Granados Dorado <x0095451@ti.com> Cc: Santosh Shilimkar <santosh.shilimkar@ti.com> Cc: Sergio Alberto Aguirre Rodriguez <saaguirre@ti.com> Cc: Tero Kristo <Tero.Kristo@nokia.com> Signed-off-by: Eduardo Valentin <eduardo.valentin@nokia.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Paul Walmsley <paul@pwsan.com> Signed-off-by: Romit Dasgupta <romit@ti.com> Signed-off-by: Sanjeev Premi <premi@ti.com> Signed-off-by: Thara Gopinath <thara@ti.com> Signed-off-by: Vishwanath BS <vishwanath.bs@ti.com> --- Documentation/arm/OMAP/omap_pm | 93 ++++++ arch/arm/plat-omap/Makefile | 5 + arch/arm/plat-omap/include/plat/opp.h | 160 ++++++++++ arch/arm/plat-omap/opp.c | 513 +++++++++++++++++++++++++++++++++ 4 files changed, 771 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/include/plat/opp.h create mode 100644 arch/arm/plat-omap/opp.c diff --git a/Documentation/arm/OMAP/omap_pm b/Documentation/arm/OMAP/omap_pm index 5389440..b046ebc 100644 --- a/Documentation/arm/OMAP/omap_pm +++ b/Documentation/arm/OMAP/omap_pm @@ -127,3 +127,96 @@ implementation needs: 10. (*pdata->cpu_set_freq)(unsigned long f) 11. (*pdata->cpu_get_freq)(void) + +OMAP OPP Layer +============== +OMAP SOCs have a standard set of tuples consisting of frequency and +voltage pairs that the device will support per voltage domain. This +is called Operating Performance Point or OPP. The actual definitions +of OMAP OPP varies over silicon within the same family of devices. +For a specific domain, you can have a set of {frequency, voltage} +pairs. As the kernel boots and more information is available, a set +of these are activated based on the precise nature of device the kernel +boots up on. It is interesting to remember that each IP which belongs +to a voltage domain may define their own set of OPPs on top of this. + +OPP layer of its own depends on silicon specific implementation and +board specific data to finalize on the final set of OPPs available +in a system + +Initial list initialization: +--------------------------- +The normal initialization sequence is for boardxyz_init_irq to call +omap2_init_common_hw (for omap2+) and which in turn does the default +setup required. + +Silicon specific initialization: First the OPP layer needs to be told +to initialize the tables for OMAP3, there are two options here: +a) If the desire is to use the default tables defined for that silicon, +the board file does not need to call any initialization function, the +defaults are setup as part of initialization flow when +omap2_init_common_hw is called. + +b) board files would like to customize the default definition. In this +case, board file needs to call explicitly prior to table operations. +the sequence is: +boardxyz_init_irq() +{ + ... do things .. + omap3_pm_init_opp_table() + .. customizations and other things .. + omap2_init_common_hw() +} +1. omap3_pm_init_opp_table - this in turn calls opp_init_list for all +OPP types. This is the generic silicon operating points, however, the +system may have additional features or customizations required. This +flexibility is provided by the following apis: + +Query functions: +---------------- +Search for OPPs for various cases: +2. opp_find_freq_exact - exact search function +3. opp_find_freq_floor - round_up search function +4. opp_find_freq_ceil - round_down search function + +OPP modifier functions: +---------------------- +This allows opp layer users to add customized OPPs or change the table +for any need they may have +5. opp_add - add a new OPP - NOTE: use struct omap_opp_def and define +the custom OPP with OMAP_OPP_DEF for usage. +6. opp_enable - enable a disabled OPP +7. opp_disable - disable an enabled OPP + +OPP Data retrieval functions: +---------------------------- +The following sets of functions are useful for drivers to retrieve +data stored in opp layer for various functions. +8. opp_get_voltage - retrieve voltage for an opp +9. opp_get_freq - get the frequency for an opp +10. opp_get_opp_count - get number of opps enabled for a domain + +Cpufreq table generation: +------------------------ +11. opp_init_cpufreq_table - this translates the OPP layer's internal +OPP arrangement into a table understood and operated upon by the +cpufreq layer. + +Deprecated functions: +-------------------- +To maintain compatibility with out of tree drivers, opp_id is needed. +OPP IDs are not constant across silicon families, nor are they constant +within a given silicon family. The rest of the layers should stop using +these functions at the earliest. For the out of tree drivers currently, +This is exposed by the following deprecated functions: +12. opp_find_by_opp_id - get an OPP for an OPP ID +13. opp_get_opp_id - get the OPP ID for an OPP + +Data Structures: +--------------- +struct omap_opp * is a handle structure whose internals are known only +to the OPP layer and is meant to hide the complexity away from users of +opp layer. + +struct omap_opp_def * is the definitions that users can interface with +opp layer and is meant to define one OPP. diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 9405831..70b012d 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -12,6 +12,11 @@ obj- := # OCPI interconnect support for 1710, 1610 and 5912 obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o +# OPP support in (OMAP3+ only at the moment) +ifdef CONFIG_CPU_FREQ +obj-$(CONFIG_ARCH_OMAP3) += opp.o +endif + # omap_device support (OMAP2+ only at the moment) obj-$(CONFIG_ARCH_OMAP2) += omap_device.o obj-$(CONFIG_ARCH_OMAP3) += omap_device.o diff --git a/arch/arm/plat-omap/include/plat/opp.h b/arch/arm/plat-omap/include/plat/opp.h new file mode 100644 index 0000000..403b69b --- /dev/null +++ b/arch/arm/plat-omap/include/plat/opp.h @@ -0,0 +1,160 @@ +/* + * OMAP OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta <romit@ti.com> + * Copyright (C) 2009 Deep Root Systems, LLC. + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_OMAP_OPP_H +#define __ASM_ARM_OMAP_OPP_H + +#include <linux/err.h> +#include <linux/cpufreq.h> + +#include <plat/common.h> + +/** + * struct omap_opp_def - OMAP OPP Definition + * @hwmod_name: Name of the hwmod for this domain + * @freq: Frequency in hertz corresponding to this OPP + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @enabled: True/false - is this OPP enabled/disabled by default + * + * OMAP SOCs have a standard set of tuples consisting of frequency and voltage + * pairs that the device will support per voltage domain. This is called + * Operating Points or OPP. The actual definitions of OMAP Operating Points + * varies over silicon within the same family of devices. For a specific + * domain, you can have a set of {frequency, voltage} pairs and this is denoted + * by an array of omap_opp_def. As the kernel boots and more information is + * available, a set of these are activated based on the precise nature of + * device the kernel boots up on. It is interesting to remember that each IP + * which belongs to a voltage domain may define their own set of OPPs on top + * of this - but this is handled by the appropriate driver. + */ +struct omap_opp_def { + char *hwmod_name; + + unsigned long freq; + unsigned long u_volt; + + bool enabled; +}; + +/* + * Initialization wrapper used to define an OPP. + * To point at the end of a terminator of a list of OPPs, + * use OMAP_OPP_DEF(NULL, 0, 0, 0) + */ +#define OMAP_OPP_DEF(_hwmod_name, _enabled, _freq, _uv) \ +{ \ + .hwmod_name = _hwmod_name, \ + .enabled = _enabled, \ + .freq = _freq, \ + .u_volt = _uv, \ +} + +struct omap_opp; + +#ifdef CONFIG_CPU_FREQ + +unsigned long opp_get_voltage(const struct omap_opp *opp); + +unsigned long opp_get_freq(const struct omap_opp *opp); + +int opp_get_opp_count(struct device *dev); + +struct omap_opp *opp_find_freq_exact(struct device *dev, + unsigned long freq, bool enabled); + +struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq); + +struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq); + +int opp_add(const struct omap_opp_def *opp_def); + +int opp_enable(struct omap_opp *opp); + +int opp_disable(struct omap_opp *opp); + +struct omap_opp *__deprecated opp_find_by_opp_id(struct device *dev, + u8 opp_id); +u8 __deprecated opp_get_opp_id(struct omap_opp *opp); + +void opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table); +#else +static inline unsigned long opp_get_voltage(const struct omap_opp *opp) +{ + return 0; +} + +static inline unsigned long opp_get_freq(const struct omap_opp *opp) +{ + return 0; +} + +static inline int opp_get_opp_count(struct omap_opp *oppl) +{ + return 0; +} + +static inline struct omap_opp *opp_find_freq_exact(struct omap_opp *oppl, + unsigned long freq, + bool enabled) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_find_freq_floor(struct omap_opp *oppl, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_find_freq_ceil(struct omap_opp *oppl, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_add(struct omap_opp *oppl, + const struct omap_opp_def *opp_def) +{ + return ERR_PTR(-EINVAL); +} + +static inline int opp_enable(struct omap_opp *opp) +{ + return 0; +} + +static inline int opp_disable(struct omap_opp *opp) +{ + return 0; +} + +static inline struct omap_opp *__deprecated +opp_find_by_opp_id(struct omap_opp *opps, u8 opp_id) +{ + return ERR_PTR(-EINVAL); +} + +static inline u8 __deprecated opp_get_opp_id(struct omap_opp *opp) +{ + return 0; +} + +static inline +void opp_init_cpufreq_table(struct omap_opp *opps, + struct cpufreq_frequency_table **table) +{ +} + +#endif /* CONFIG_CPU_FREQ */ +#endif /* __ASM_ARM_OMAP_OPP_H */ diff --git a/arch/arm/plat-omap/opp.c b/arch/arm/plat-omap/opp.c new file mode 100644 index 0000000..b9b7bda --- /dev/null +++ b/arch/arm/plat-omap/opp.c @@ -0,0 +1,513 @@ +/* + * OMAP OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta <romit@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/list.h> + +#include <plat/opp.h> +#include <plat/omap_device.h> + +/** + * struct omap_opp - OMAP OPP description structure + * @enabled: true/false - marking this OPP as enabled/disabled + * @rate: Frequency in hertz + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @opp_id: opp identifier (deprecated) + * @dev_opp: contains the device_opp struct + * + * This structure stores the OPP information for a given domain. + */ +struct omap_opp { + struct list_head node; + + bool enabled; + unsigned long rate; + unsigned long u_volt; + u8 opp_id; + + struct device_opp *dev_opp; +}; + +/** + * struct device_opp - Device opp structure + * @node: list node + * @oh: hwmod handle + * @dev: device handle + * @opp_list: list of opps + * @opp_count: num opps + * @enabled_opp_count: how many opps are actually enabled + * + * This is an internal datastructure maintaining the link to + * omap_opps attached to a domain device. This structure is not + * meant to be shared with users as it private to opp layer. + */ +struct device_opp { + struct list_head node; + + struct omap_hwmod *oh; + struct device *dev; + + struct list_head opp_list; + u32 opp_count; + u32 enabled_opp_count; +}; + +static LIST_HEAD(dev_opp_list); + +/** + * find_device_opp() - find device_opp struct using device pointer + * @dev: device pointer used to lookup device OPPs + * + * Search list of device OPPs for one containing matching device. + * + * Returns pointer to 'struct device_opp' if found, otherwise -ENODEV or + * -EINVAL based on type of error. + */ +static struct device_opp *find_device_opp(struct device *dev) +{ + struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV); + + if (unlikely(!dev || IS_ERR(dev))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return ERR_PTR(-EINVAL); + } + + list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { + if (tmp_dev_opp->dev == dev) { + dev_opp = tmp_dev_opp; + break; + } + } + + return dev_opp; +} + +/** + * opp_get_voltage() - Gets the voltage corresponding to an opp + * @opp: opp for which voltage has to be returned for + * + * Return voltage in micro volt corresponding to the opp, else + * return 0 + */ +unsigned long opp_get_voltage(const struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return 0; + } + + return opp->u_volt; +} + +/** + * opp_get_freq() - Gets the frequency corresponding to an opp + * @opp: opp for which frequency has to be returned for + * + * Return frequency in hertz corresponding to the opp, else + * return 0 + */ +unsigned long opp_get_freq(const struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return 0; + } + + return opp->rate; +} + +/** + * opp_find_by_opp_id - look up OPP by OPP ID (deprecated) + * @opp_type: OPP type where we want the look up to happen. + * @opp_id: OPP ID to search for + * + * Returns the struct omap_opp pointer corresponding to the given OPP + * ID @opp_id, or returns NULL on error. + */ +struct omap_opp * __deprecated opp_find_by_opp_id(struct device *dev, + u8 opp_id) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->opp_id == opp_id) { + opp = temp_opp; + break; + } + } + + return opp; +} + +/** + * opp_get_opp_id() - Provide OPP ID corresponding to an OPP (deprecated) + * @opp: opp for which frequency has to be returned for + * + * Returns an OPP ID for the OPP required, if error, returns 0 + */ +u8 __deprecated opp_get_opp_id(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameter being passed\n", __func__); + return 0; + } + + return opp->opp_id; +} + +/** + * opp_get_opp_count() - Get number of opps enabled in the opp list + * @opp_type: OPP type we want to count + * + * This functions returns the number of opps if there are any OPPs enabled, + * else returns corresponding error value. + */ +int opp_get_opp_count(struct device *dev) +{ + struct device_opp *dev_opp; + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return -ENODEV; + + return dev_opp->enabled_opp_count; +} + +/** + * opp_find_freq_exact() - search for an exact frequency + * @opp_type: OPP type we want to search in. + * @freq: frequency to search for + * @enabled: enabled/disabled OPP to search for + * + * Searches for exact match in the opp list and returns handle to the matching + * opp if found, else returns ERR_PTR in case of error and should be handled + * using IS_ERR. + * + * Note enabled is a modifier for the search. if enabled=true, then the match is + * for exact matching frequency and is enabled. if false, the match is for exact + * frequency which is disabled. + */ +struct omap_opp *opp_find_freq_exact(struct device *dev, + unsigned long freq, bool enabled) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate == freq) { + opp = temp_opp; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_ceil() - Search for an rounded ceil freq + * @opp_type: OPP type where we want to search in + * @freq: Start frequency + * + * Search for the matching ceil *enabled* OPP from a starting freq + * for a domain. + * + * Returns *opp and *freq is populated with the match, else + * returns NULL opp if no match, else returns ERR_PTR in case of error. + * + * Example usages: + * * find match/next highest available frequency * + * freq = 350000; + * opp = opp_find_freq_ceil(OPP_MPU, &freq)) + * if (IS_ERR(opp)) + * pr_err("unable to find a higher frequency\n"); + * else + * pr_info("match freq = %ld\n", freq); + * + * * print all supported frequencies in ascending order * + * freq = 0; * Search for the lowest enabled frequency * + * while (!IS_ERR(opp = opp_find_freq_ceil(OPP_MPU, &freq)) { + * pr_info("freq = %ld\n", freq); + * freq++; * for next higher match * + * } + */ +struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate >= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_floor() - Search for an rounded floor freq + * @opp_type: OPP type we want to search in + * @freq: Start frequency + * + * Search for the matching floor *enabled* OPP from a starting freq + * for a domain. + * + * Returns *opp and *freq is populated with the next match, else + * returns NULL opp if no match, else returns ERR_PTR in case of error. + * + * Example usages: + * * find match/next lowest available frequency + * freq = 350000; + * opp = opp_find_freq_floor(OPP_MPU, &freq))) + * if (IS_ERR(opp)) + * pr_err ("unable to find a lower frequency\n"); + * else + * pr_info("match freq = %ld\n", freq); + * + * * print all supported frequencies in descending order * + * freq = ULONG_MAX; * search highest enabled frequency * + * while (!IS_ERR(opp = opp_find_freq_floor(OPP_MPU, &freq)) { + * pr_info("freq = %ld\n", freq); + * freq--; * for next lower match * + * } + */ +struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry_reverse(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate <= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} + +/* wrapper to reuse converting opp_def to opp struct */ +static void omap_opp_populate(struct omap_opp *opp, + const struct omap_opp_def *opp_def) +{ + opp->rate = opp_def->freq; + opp->enabled = opp_def->enabled; + opp->u_volt = opp_def->u_volt; +} + +/** + * opp_add() - Add an OPP table from a table definitions + * @opp_def: omap_opp_def to describe the OPP which we want to add. + * + * This function adds an opp definition to the opp list and returns status. + */ +int opp_add(const struct omap_opp_def *opp_def) +{ + struct omap_hwmod *oh; + struct device *dev = NULL; + struct device_opp *tmp_dev_opp, *dev_opp = NULL; + struct omap_opp *opp, *new_opp; + struct platform_device *pdev; + struct list_head *head; + int i; + + /* find the correct hwmod, and device */ + if (!opp_def->hwmod_name) { + pr_err("%s: missing name of omap_hwmod, ignoring.\n", __func__); + return -EINVAL; + } + oh = omap_hwmod_lookup(opp_def->hwmod_name); + if (!oh || !oh->od) { + pr_warn("%s: no hwmod or odev for %s, cannot add OPPs.\n", + __func__, opp_def->hwmod_name); + return -EINVAL; + } + pdev = &oh->od->pdev; + dev = &oh->od->pdev.dev; + + /* Check for existing list for 'dev' */ + list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { + if (dev == tmp_dev_opp->dev) { + dev_opp = tmp_dev_opp; + break; + } + } + + if (!dev_opp) { + /* Allocate a new device OPP table */ + dev_opp = kzalloc(sizeof(struct device_opp), GFP_KERNEL); + if (!dev_opp) { + pr_warning("%s: unable to allocate device struct\n", + __func__); + return -ENOMEM; + } + + dev_opp->oh = oh; + dev_opp->dev = &oh->od->pdev.dev; + INIT_LIST_HEAD(&dev_opp->opp_list); + + list_add(&dev_opp->node, &dev_opp_list); + } + + /* allocate new OPP node */ + new_opp = kzalloc(sizeof(struct omap_opp), GFP_KERNEL); + if (!new_opp) { + if (list_empty(&dev_opp->opp_list)) { + list_del(&dev_opp->node); + kfree(dev_opp); + } + pr_warning("%s: unable to allocate new opp node\n", + __func__); + return -ENOMEM; + } + omap_opp_populate(new_opp, opp_def); + + /* Insert new OPP in order of increasing frequency */ + head = &dev_opp->opp_list; + list_for_each_entry_reverse(opp, &dev_opp->opp_list, node) { + if (new_opp->rate >= opp->rate) { + head = &opp->node; + break; + } + } + list_add(&new_opp->node, head); + dev_opp->opp_count++; + if (new_opp->enabled) + dev_opp->enabled_opp_count++; + + /* renumber (deprecated) OPP IDs based on new order */ + i = 0; + list_for_each_entry(opp, &dev_opp->opp_list, node) + opp->opp_id = i++; + + return 0; +} + +/** + * opp_enable() - Enable a specific OPP + * @opp: Pointer to opp + * + * Enables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. + * + * OPP used here is from the the opp_is_valid/opp_has_freq or other search + * functions + */ +int opp_enable(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return -EINVAL; + } + + if (!opp->enabled && opp->dev_opp) + opp->dev_opp->enabled_opp_count++; + + opp->enabled = true; + + return 0; +} + +/** + * opp_disable() - Disable a specific OPP + * @opp: Pointer to opp + * + * Disables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. + * + * OPP used here is from the the opp_is_valid/opp_has_freq or other search + * functions + */ +int opp_disable(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return -EINVAL; + } + + if (opp->enabled && opp->dev_opp) + opp->dev_opp->enabled_opp_count--; + + opp->enabled = false; + + return 0; +} + +/** + * opp_init_cpufreq_table() - create a cpufreq table for a domain + * @opp_type: OPP type to initialize this list for + * @table: Cpufreq table returned back to caller + * + * Generate a cpufreq table for a provided domain - this assumes that the + * opp list is already initialized and ready for usage + */ +void opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + struct device_opp *dev_opp; + struct omap_opp *opp; + struct cpufreq_frequency_table *freq_table; + int i = 0; + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) { + pr_warning("%s: unable to find device\n", __func__); + return; + } + + freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * + (dev_opp->enabled_opp_count + 1), GFP_ATOMIC); + if (!freq_table) { + pr_warning("%s: failed to allocate frequency table\n", + __func__); + return; + } + + list_for_each_entry(opp, &dev_opp->opp_list, node) { + if (opp->enabled) { + freq_table[i].index = i; + freq_table[i].frequency = opp->rate / 1000; + i++; + } + } + + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + *table = &freq_table[0]; +} -- 1.7.2.1 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 0/4] OMAP OPP layer @ 2010-08-11 0:09 Kevin Hilman 2010-08-11 0:09 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman 0 siblings, 1 reply; 21+ messages in thread From: Kevin Hilman @ 2010-08-11 0:09 UTC (permalink / raw) To: linux-omap This series introduces a layer to manage Operating Performance Points (OPPs) for OMAP SoCs. Special thanks to Nishanth for doing the bulk of the grunt work for this series and to Thara for doing significant review, testing and updates. Kevin Hilman (1): omap3: pm: remove OPP interfaces from OMAP PM layer Nishanth Menon (1): OMAP: introduce OPP layer for device-specific OPPs Paul Walmsley (1): omap: opp: twl/tps: Introduce TWL/TPS-specific code Thara Gopinath (1): OMAP: Remove dependency of generic opp layer on cpufreq. Documentation/arm/OMAP/omap_pm | 93 +++++ arch/arm/mach-omap2/io.c | 3 +- arch/arm/plat-omap/Makefile | 8 +- arch/arm/plat-omap/include/plat/omap-pm.h | 31 +- arch/arm/plat-omap/include/plat/opp.h | 160 ++++++++ arch/arm/plat-omap/include/plat/opp_twl_tps.h | 21 + arch/arm/plat-omap/omap-pm-noop.c | 11 +- arch/arm/plat-omap/opp.c | 513 +++++++++++++++++++++++++ arch/arm/plat-omap/opp_twl_tps.c | 41 ++ 9 files changed, 847 insertions(+), 34 deletions(-) create mode 100644 arch/arm/plat-omap/include/plat/opp.h create mode 100644 arch/arm/plat-omap/include/plat/opp_twl_tps.h create mode 100644 arch/arm/plat-omap/opp.c create mode 100644 arch/arm/plat-omap/opp_twl_tps.c -- 1.7.2.1 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-08-11 0:09 [PATCH 0/4] OMAP OPP layer Kevin Hilman @ 2010-08-11 0:09 ` Kevin Hilman 0 siblings, 0 replies; 21+ messages in thread From: Kevin Hilman @ 2010-08-11 0:09 UTC (permalink / raw) To: linux-omap Cc: Nishanth Menon, Benoit Cousson, Madhusudhan Chikkature Rajashekar, Phil Carmody, Roberto Granados Dorado, Santosh Shilimkar, Sergio Alberto Aguirre Rodriguez, Tero Kristo, Eduardo Valentin, Paul Walmsley, Romit Dasgupta, Sanjeev Premi, Thara Gopinath, Vishwanath BS From: Nishanth Menon <nm@ti.com> OMAP SOCs have a standard set of tuples consisting of frequency and voltage pairs that the device will support per voltage domain. These are called Operating Performance Points or OPPs. The actual definitions of OMAP Operating Points varies over silicon within the same family of devices. For a specific domain, you can have a set of {frequency, voltage} pairs. As the kernel boots and more information is available, a set of these are activated based on the precise nature of device the kernel boots up on. It is interesting to remember that each IP which belongs to a voltage domain may define their own set of OPPs on top of this. This introduces a common handling OPP mechanism accross all OMAPs. As a start this is used for OMAP3. Note: OPP is a concept that can be used in all OMAPs, it is hence introduced under plat-omap Contributions include: Sanjeev Premi for the initial concept: http://patchwork.kernel.org/patch/50998/ Kevin Hilman for converting original design to device-based Kevin Hilman and Paul Walmsey for cleaning up many of the function abstractions, improvements and data structure handling Romit Dasgupta for using enums instead of opp pointers Thara Gopinath, Eduardo Valentin and Vishwanath BS for fixes and cleanups. Discussions and comments from: http://marc.info/?l=linux-omap&m=126033945313269&w=2 http://marc.info/?l=linux-omap&m=125482970102327&w=2 http://marc.info/?t=125809247500002&r=1&w=2 http://marc.info/?l=linux-omap&m=126025973426007&w=2 incorporated. Cc: Benoit Cousson <b-cousson@ti.com> Cc: Madhusudhan Chikkature Rajashekar <madhu.cr@ti.com> Cc: Phil Carmody <ext-phil.2.carmody@nokia.com> Cc: Roberto Granados Dorado <x0095451@ti.com> Cc: Santosh Shilimkar <santosh.shilimkar@ti.com> Cc: Sergio Alberto Aguirre Rodriguez <saaguirre@ti.com> Cc: Tero Kristo <Tero.Kristo@nokia.com> Signed-off-by: Eduardo Valentin <eduardo.valentin@nokia.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Paul Walmsley <paul@pwsan.com> Signed-off-by: Romit Dasgupta <romit@ti.com> Signed-off-by: Sanjeev Premi <premi@ti.com> Signed-off-by: Thara Gopinath <thara@ti.com> Signed-off-by: Vishwanath BS <vishwanath.bs@ti.com> --- Documentation/arm/OMAP/omap_pm | 93 ++++++ arch/arm/plat-omap/Makefile | 5 + arch/arm/plat-omap/include/plat/opp.h | 160 ++++++++++ arch/arm/plat-omap/opp.c | 513 +++++++++++++++++++++++++++++++++ 4 files changed, 771 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/include/plat/opp.h create mode 100644 arch/arm/plat-omap/opp.c diff --git a/Documentation/arm/OMAP/omap_pm b/Documentation/arm/OMAP/omap_pm index 5389440..b046ebc 100644 --- a/Documentation/arm/OMAP/omap_pm +++ b/Documentation/arm/OMAP/omap_pm @@ -127,3 +127,96 @@ implementation needs: 10. (*pdata->cpu_set_freq)(unsigned long f) 11. (*pdata->cpu_get_freq)(void) + +OMAP OPP Layer +============== +OMAP SOCs have a standard set of tuples consisting of frequency and +voltage pairs that the device will support per voltage domain. This +is called Operating Performance Point or OPP. The actual definitions +of OMAP OPP varies over silicon within the same family of devices. +For a specific domain, you can have a set of {frequency, voltage} +pairs. As the kernel boots and more information is available, a set +of these are activated based on the precise nature of device the kernel +boots up on. It is interesting to remember that each IP which belongs +to a voltage domain may define their own set of OPPs on top of this. + +OPP layer of its own depends on silicon specific implementation and +board specific data to finalize on the final set of OPPs available +in a system + +Initial list initialization: +--------------------------- +The normal initialization sequence is for boardxyz_init_irq to call +omap2_init_common_hw (for omap2+) and which in turn does the default +setup required. + +Silicon specific initialization: First the OPP layer needs to be told +to initialize the tables for OMAP3, there are two options here: +a) If the desire is to use the default tables defined for that silicon, +the board file does not need to call any initialization function, the +defaults are setup as part of initialization flow when +omap2_init_common_hw is called. + +b) board files would like to customize the default definition. In this +case, board file needs to call explicitly prior to table operations. +the sequence is: +boardxyz_init_irq() +{ + ... do things .. + omap3_pm_init_opp_table() + .. customizations and other things .. + omap2_init_common_hw() +} +1. omap3_pm_init_opp_table - this in turn calls opp_init_list for all +OPP types. This is the generic silicon operating points, however, the +system may have additional features or customizations required. This +flexibility is provided by the following apis: + +Query functions: +---------------- +Search for OPPs for various cases: +2. opp_find_freq_exact - exact search function +3. opp_find_freq_floor - round_up search function +4. opp_find_freq_ceil - round_down search function + +OPP modifier functions: +---------------------- +This allows opp layer users to add customized OPPs or change the table +for any need they may have +5. opp_add - add a new OPP - NOTE: use struct omap_opp_def and define +the custom OPP with OMAP_OPP_DEF for usage. +6. opp_enable - enable a disabled OPP +7. opp_disable - disable an enabled OPP + +OPP Data retrieval functions: +---------------------------- +The following sets of functions are useful for drivers to retrieve +data stored in opp layer for various functions. +8. opp_get_voltage - retrieve voltage for an opp +9. opp_get_freq - get the frequency for an opp +10. opp_get_opp_count - get number of opps enabled for a domain + +Cpufreq table generation: +------------------------ +11. opp_init_cpufreq_table - this translates the OPP layer's internal +OPP arrangement into a table understood and operated upon by the +cpufreq layer. + +Deprecated functions: +-------------------- +To maintain compatibility with out of tree drivers, opp_id is needed. +OPP IDs are not constant across silicon families, nor are they constant +within a given silicon family. The rest of the layers should stop using +these functions at the earliest. For the out of tree drivers currently, +This is exposed by the following deprecated functions: +12. opp_find_by_opp_id - get an OPP for an OPP ID +13. opp_get_opp_id - get the OPP ID for an OPP + +Data Structures: +--------------- +struct omap_opp * is a handle structure whose internals are known only +to the OPP layer and is meant to hide the complexity away from users of +opp layer. + +struct omap_opp_def * is the definitions that users can interface with +opp layer and is meant to define one OPP. diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 9405831..70b012d 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -12,6 +12,11 @@ obj- := # OCPI interconnect support for 1710, 1610 and 5912 obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o +# OPP support in (OMAP3+ only at the moment) +ifdef CONFIG_CPU_FREQ +obj-$(CONFIG_ARCH_OMAP3) += opp.o +endif + # omap_device support (OMAP2+ only at the moment) obj-$(CONFIG_ARCH_OMAP2) += omap_device.o obj-$(CONFIG_ARCH_OMAP3) += omap_device.o diff --git a/arch/arm/plat-omap/include/plat/opp.h b/arch/arm/plat-omap/include/plat/opp.h new file mode 100644 index 0000000..403b69b --- /dev/null +++ b/arch/arm/plat-omap/include/plat/opp.h @@ -0,0 +1,160 @@ +/* + * OMAP OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta <romit@ti.com> + * Copyright (C) 2009 Deep Root Systems, LLC. + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_OMAP_OPP_H +#define __ASM_ARM_OMAP_OPP_H + +#include <linux/err.h> +#include <linux/cpufreq.h> + +#include <plat/common.h> + +/** + * struct omap_opp_def - OMAP OPP Definition + * @hwmod_name: Name of the hwmod for this domain + * @freq: Frequency in hertz corresponding to this OPP + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @enabled: True/false - is this OPP enabled/disabled by default + * + * OMAP SOCs have a standard set of tuples consisting of frequency and voltage + * pairs that the device will support per voltage domain. This is called + * Operating Points or OPP. The actual definitions of OMAP Operating Points + * varies over silicon within the same family of devices. For a specific + * domain, you can have a set of {frequency, voltage} pairs and this is denoted + * by an array of omap_opp_def. As the kernel boots and more information is + * available, a set of these are activated based on the precise nature of + * device the kernel boots up on. It is interesting to remember that each IP + * which belongs to a voltage domain may define their own set of OPPs on top + * of this - but this is handled by the appropriate driver. + */ +struct omap_opp_def { + char *hwmod_name; + + unsigned long freq; + unsigned long u_volt; + + bool enabled; +}; + +/* + * Initialization wrapper used to define an OPP. + * To point at the end of a terminator of a list of OPPs, + * use OMAP_OPP_DEF(NULL, 0, 0, 0) + */ +#define OMAP_OPP_DEF(_hwmod_name, _enabled, _freq, _uv) \ +{ \ + .hwmod_name = _hwmod_name, \ + .enabled = _enabled, \ + .freq = _freq, \ + .u_volt = _uv, \ +} + +struct omap_opp; + +#ifdef CONFIG_CPU_FREQ + +unsigned long opp_get_voltage(const struct omap_opp *opp); + +unsigned long opp_get_freq(const struct omap_opp *opp); + +int opp_get_opp_count(struct device *dev); + +struct omap_opp *opp_find_freq_exact(struct device *dev, + unsigned long freq, bool enabled); + +struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq); + +struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq); + +int opp_add(const struct omap_opp_def *opp_def); + +int opp_enable(struct omap_opp *opp); + +int opp_disable(struct omap_opp *opp); + +struct omap_opp *__deprecated opp_find_by_opp_id(struct device *dev, + u8 opp_id); +u8 __deprecated opp_get_opp_id(struct omap_opp *opp); + +void opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table); +#else +static inline unsigned long opp_get_voltage(const struct omap_opp *opp) +{ + return 0; +} + +static inline unsigned long opp_get_freq(const struct omap_opp *opp) +{ + return 0; +} + +static inline int opp_get_opp_count(struct omap_opp *oppl) +{ + return 0; +} + +static inline struct omap_opp *opp_find_freq_exact(struct omap_opp *oppl, + unsigned long freq, + bool enabled) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_find_freq_floor(struct omap_opp *oppl, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_find_freq_ceil(struct omap_opp *oppl, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_add(struct omap_opp *oppl, + const struct omap_opp_def *opp_def) +{ + return ERR_PTR(-EINVAL); +} + +static inline int opp_enable(struct omap_opp *opp) +{ + return 0; +} + +static inline int opp_disable(struct omap_opp *opp) +{ + return 0; +} + +static inline struct omap_opp *__deprecated +opp_find_by_opp_id(struct omap_opp *opps, u8 opp_id) +{ + return ERR_PTR(-EINVAL); +} + +static inline u8 __deprecated opp_get_opp_id(struct omap_opp *opp) +{ + return 0; +} + +static inline +void opp_init_cpufreq_table(struct omap_opp *opps, + struct cpufreq_frequency_table **table) +{ +} + +#endif /* CONFIG_CPU_FREQ */ +#endif /* __ASM_ARM_OMAP_OPP_H */ diff --git a/arch/arm/plat-omap/opp.c b/arch/arm/plat-omap/opp.c new file mode 100644 index 0000000..b9b7bda --- /dev/null +++ b/arch/arm/plat-omap/opp.c @@ -0,0 +1,513 @@ +/* + * OMAP OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta <romit@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/list.h> + +#include <plat/opp.h> +#include <plat/omap_device.h> + +/** + * struct omap_opp - OMAP OPP description structure + * @enabled: true/false - marking this OPP as enabled/disabled + * @rate: Frequency in hertz + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @opp_id: opp identifier (deprecated) + * @dev_opp: contains the device_opp struct + * + * This structure stores the OPP information for a given domain. + */ +struct omap_opp { + struct list_head node; + + bool enabled; + unsigned long rate; + unsigned long u_volt; + u8 opp_id; + + struct device_opp *dev_opp; +}; + +/** + * struct device_opp - Device opp structure + * @node: list node + * @oh: hwmod handle + * @dev: device handle + * @opp_list: list of opps + * @opp_count: num opps + * @enabled_opp_count: how many opps are actually enabled + * + * This is an internal datastructure maintaining the link to + * omap_opps attached to a domain device. This structure is not + * meant to be shared with users as it private to opp layer. + */ +struct device_opp { + struct list_head node; + + struct omap_hwmod *oh; + struct device *dev; + + struct list_head opp_list; + u32 opp_count; + u32 enabled_opp_count; +}; + +static LIST_HEAD(dev_opp_list); + +/** + * find_device_opp() - find device_opp struct using device pointer + * @dev: device pointer used to lookup device OPPs + * + * Search list of device OPPs for one containing matching device. + * + * Returns pointer to 'struct device_opp' if found, otherwise -ENODEV or + * -EINVAL based on type of error. + */ +static struct device_opp *find_device_opp(struct device *dev) +{ + struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV); + + if (unlikely(!dev || IS_ERR(dev))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return ERR_PTR(-EINVAL); + } + + list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { + if (tmp_dev_opp->dev == dev) { + dev_opp = tmp_dev_opp; + break; + } + } + + return dev_opp; +} + +/** + * opp_get_voltage() - Gets the voltage corresponding to an opp + * @opp: opp for which voltage has to be returned for + * + * Return voltage in micro volt corresponding to the opp, else + * return 0 + */ +unsigned long opp_get_voltage(const struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return 0; + } + + return opp->u_volt; +} + +/** + * opp_get_freq() - Gets the frequency corresponding to an opp + * @opp: opp for which frequency has to be returned for + * + * Return frequency in hertz corresponding to the opp, else + * return 0 + */ +unsigned long opp_get_freq(const struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return 0; + } + + return opp->rate; +} + +/** + * opp_find_by_opp_id - look up OPP by OPP ID (deprecated) + * @opp_type: OPP type where we want the look up to happen. + * @opp_id: OPP ID to search for + * + * Returns the struct omap_opp pointer corresponding to the given OPP + * ID @opp_id, or returns NULL on error. + */ +struct omap_opp * __deprecated opp_find_by_opp_id(struct device *dev, + u8 opp_id) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->opp_id == opp_id) { + opp = temp_opp; + break; + } + } + + return opp; +} + +/** + * opp_get_opp_id() - Provide OPP ID corresponding to an OPP (deprecated) + * @opp: opp for which frequency has to be returned for + * + * Returns an OPP ID for the OPP required, if error, returns 0 + */ +u8 __deprecated opp_get_opp_id(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameter being passed\n", __func__); + return 0; + } + + return opp->opp_id; +} + +/** + * opp_get_opp_count() - Get number of opps enabled in the opp list + * @opp_type: OPP type we want to count + * + * This functions returns the number of opps if there are any OPPs enabled, + * else returns corresponding error value. + */ +int opp_get_opp_count(struct device *dev) +{ + struct device_opp *dev_opp; + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return -ENODEV; + + return dev_opp->enabled_opp_count; +} + +/** + * opp_find_freq_exact() - search for an exact frequency + * @opp_type: OPP type we want to search in. + * @freq: frequency to search for + * @enabled: enabled/disabled OPP to search for + * + * Searches for exact match in the opp list and returns handle to the matching + * opp if found, else returns ERR_PTR in case of error and should be handled + * using IS_ERR. + * + * Note enabled is a modifier for the search. if enabled=true, then the match is + * for exact matching frequency and is enabled. if false, the match is for exact + * frequency which is disabled. + */ +struct omap_opp *opp_find_freq_exact(struct device *dev, + unsigned long freq, bool enabled) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate == freq) { + opp = temp_opp; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_ceil() - Search for an rounded ceil freq + * @opp_type: OPP type where we want to search in + * @freq: Start frequency + * + * Search for the matching ceil *enabled* OPP from a starting freq + * for a domain. + * + * Returns *opp and *freq is populated with the match, else + * returns NULL opp if no match, else returns ERR_PTR in case of error. + * + * Example usages: + * * find match/next highest available frequency * + * freq = 350000; + * opp = opp_find_freq_ceil(OPP_MPU, &freq)) + * if (IS_ERR(opp)) + * pr_err("unable to find a higher frequency\n"); + * else + * pr_info("match freq = %ld\n", freq); + * + * * print all supported frequencies in ascending order * + * freq = 0; * Search for the lowest enabled frequency * + * while (!IS_ERR(opp = opp_find_freq_ceil(OPP_MPU, &freq)) { + * pr_info("freq = %ld\n", freq); + * freq++; * for next higher match * + * } + */ +struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate >= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_floor() - Search for an rounded floor freq + * @opp_type: OPP type we want to search in + * @freq: Start frequency + * + * Search for the matching floor *enabled* OPP from a starting freq + * for a domain. + * + * Returns *opp and *freq is populated with the next match, else + * returns NULL opp if no match, else returns ERR_PTR in case of error. + * + * Example usages: + * * find match/next lowest available frequency + * freq = 350000; + * opp = opp_find_freq_floor(OPP_MPU, &freq))) + * if (IS_ERR(opp)) + * pr_err ("unable to find a lower frequency\n"); + * else + * pr_info("match freq = %ld\n", freq); + * + * * print all supported frequencies in descending order * + * freq = ULONG_MAX; * search highest enabled frequency * + * while (!IS_ERR(opp = opp_find_freq_floor(OPP_MPU, &freq)) { + * pr_info("freq = %ld\n", freq); + * freq--; * for next lower match * + * } + */ +struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry_reverse(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate <= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} + +/* wrapper to reuse converting opp_def to opp struct */ +static void omap_opp_populate(struct omap_opp *opp, + const struct omap_opp_def *opp_def) +{ + opp->rate = opp_def->freq; + opp->enabled = opp_def->enabled; + opp->u_volt = opp_def->u_volt; +} + +/** + * opp_add() - Add an OPP table from a table definitions + * @opp_def: omap_opp_def to describe the OPP which we want to add. + * + * This function adds an opp definition to the opp list and returns status. + */ +int opp_add(const struct omap_opp_def *opp_def) +{ + struct omap_hwmod *oh; + struct device *dev = NULL; + struct device_opp *tmp_dev_opp, *dev_opp = NULL; + struct omap_opp *opp, *new_opp; + struct platform_device *pdev; + struct list_head *head; + int i; + + /* find the correct hwmod, and device */ + if (!opp_def->hwmod_name) { + pr_err("%s: missing name of omap_hwmod, ignoring.\n", __func__); + return -EINVAL; + } + oh = omap_hwmod_lookup(opp_def->hwmod_name); + if (!oh || !oh->od) { + pr_warn("%s: no hwmod or odev for %s, cannot add OPPs.\n", + __func__, opp_def->hwmod_name); + return -EINVAL; + } + pdev = &oh->od->pdev; + dev = &oh->od->pdev.dev; + + /* Check for existing list for 'dev' */ + list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { + if (dev == tmp_dev_opp->dev) { + dev_opp = tmp_dev_opp; + break; + } + } + + if (!dev_opp) { + /* Allocate a new device OPP table */ + dev_opp = kzalloc(sizeof(struct device_opp), GFP_KERNEL); + if (!dev_opp) { + pr_warning("%s: unable to allocate device struct\n", + __func__); + return -ENOMEM; + } + + dev_opp->oh = oh; + dev_opp->dev = &oh->od->pdev.dev; + INIT_LIST_HEAD(&dev_opp->opp_list); + + list_add(&dev_opp->node, &dev_opp_list); + } + + /* allocate new OPP node */ + new_opp = kzalloc(sizeof(struct omap_opp), GFP_KERNEL); + if (!new_opp) { + if (list_empty(&dev_opp->opp_list)) { + list_del(&dev_opp->node); + kfree(dev_opp); + } + pr_warning("%s: unable to allocate new opp node\n", + __func__); + return -ENOMEM; + } + omap_opp_populate(new_opp, opp_def); + + /* Insert new OPP in order of increasing frequency */ + head = &dev_opp->opp_list; + list_for_each_entry_reverse(opp, &dev_opp->opp_list, node) { + if (new_opp->rate >= opp->rate) { + head = &opp->node; + break; + } + } + list_add(&new_opp->node, head); + dev_opp->opp_count++; + if (new_opp->enabled) + dev_opp->enabled_opp_count++; + + /* renumber (deprecated) OPP IDs based on new order */ + i = 0; + list_for_each_entry(opp, &dev_opp->opp_list, node) + opp->opp_id = i++; + + return 0; +} + +/** + * opp_enable() - Enable a specific OPP + * @opp: Pointer to opp + * + * Enables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. + * + * OPP used here is from the the opp_is_valid/opp_has_freq or other search + * functions + */ +int opp_enable(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return -EINVAL; + } + + if (!opp->enabled && opp->dev_opp) + opp->dev_opp->enabled_opp_count++; + + opp->enabled = true; + + return 0; +} + +/** + * opp_disable() - Disable a specific OPP + * @opp: Pointer to opp + * + * Disables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. + * + * OPP used here is from the the opp_is_valid/opp_has_freq or other search + * functions + */ +int opp_disable(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return -EINVAL; + } + + if (opp->enabled && opp->dev_opp) + opp->dev_opp->enabled_opp_count--; + + opp->enabled = false; + + return 0; +} + +/** + * opp_init_cpufreq_table() - create a cpufreq table for a domain + * @opp_type: OPP type to initialize this list for + * @table: Cpufreq table returned back to caller + * + * Generate a cpufreq table for a provided domain - this assumes that the + * opp list is already initialized and ready for usage + */ +void opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + struct device_opp *dev_opp; + struct omap_opp *opp; + struct cpufreq_frequency_table *freq_table; + int i = 0; + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) { + pr_warning("%s: unable to find device\n", __func__); + return; + } + + freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * + (dev_opp->enabled_opp_count + 1), GFP_ATOMIC); + if (!freq_table) { + pr_warning("%s: failed to allocate frequency table\n", + __func__); + return; + } + + list_for_each_entry(opp, &dev_opp->opp_list, node) { + if (opp->enabled) { + freq_table[i].index = i; + freq_table[i].frequency = opp->rate / 1000; + i++; + } + } + + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + *table = &freq_table[0]; +} -- 1.7.2.1 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 0/4] OMAP OPP layer for 2.6.37 @ 2010-09-15 21:56 Kevin Hilman 2010-09-15 21:56 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman 0 siblings, 1 reply; 21+ messages in thread From: Kevin Hilman @ 2010-09-15 21:56 UTC (permalink / raw) To: linux-omap; +Cc: linux-arm-kernel This series introduces a layer to manage Operating Performance Points (OPPs) for OMAP SoCs. Special thanks to Nishanth for doing the bulk of the work on this series and to Thara for doing significant review, testing and updates. Kevin Hilman (1): OMAP3: remove OPP interfaces from OMAP PM layer Nishanth Menon (2): OMAP: introduce OPP layer for device-specific OPPs OMAP3: OPP: add OPP table data and initialization Paul Walmsley (1): OMAP: OPP: twl/tps: Introduce TWL/TPS-specific code Documentation/arm/OMAP/omap_pm | 83 +++++ arch/arm/mach-omap2/Makefile | 2 + arch/arm/mach-omap2/io.c | 3 +- arch/arm/mach-omap2/opp3xxx_data.c | 126 +++++++ arch/arm/mach-omap2/pm.c | 6 + arch/arm/mach-omap2/pm.h | 1 + arch/arm/plat-omap/Makefile | 6 + arch/arm/plat-omap/include/plat/omap-pm.h | 31 +-- arch/arm/plat-omap/include/plat/opp.h | 145 ++++++++ arch/arm/plat-omap/include/plat/opp_twl_tps.h | 21 ++ arch/arm/plat-omap/omap-pm-noop.c | 11 +- arch/arm/plat-omap/opp.c | 461 +++++++++++++++++++++++++ arch/arm/plat-omap/opp_twl_tps.c | 41 +++ 13 files changed, 904 insertions(+), 33 deletions(-) create mode 100644 arch/arm/mach-omap2/opp3xxx_data.c create mode 100644 arch/arm/plat-omap/include/plat/opp.h create mode 100644 arch/arm/plat-omap/include/plat/opp_twl_tps.h create mode 100644 arch/arm/plat-omap/opp.c create mode 100644 arch/arm/plat-omap/opp_twl_tps.c -- 1.7.2.1 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-15 21:56 [PATCH 0/4] OMAP OPP layer for 2.6.37 Kevin Hilman @ 2010-09-15 21:56 ` Kevin Hilman 2010-09-16 10:25 ` Gopinath, Thara ` (2 more replies) 0 siblings, 3 replies; 21+ messages in thread From: Kevin Hilman @ 2010-09-15 21:56 UTC (permalink / raw) To: linux-omap; +Cc: linux-arm-kernel From: Nishanth Menon <nm@ti.com> OMAP SOCs have a standard set of tuples consisting of frequency and voltage pairs that the device will support per voltage domain. These are called Operating Performance Points or OPPs. The actual definitions of OMAP Operating Points varies over silicon within the same family of devices. For a specific domain, you can have a set of {frequency, voltage} pairs. As the kernel boots and more information is available, a set of these are activated based on the precise nature of device the kernel boots up on. It is interesting to remember that each IP which belongs to a voltage domain may define their own set of OPPs on top of this. This introduces a common handling OPP mechanism accross all OMAPs. As a start this is used for OMAP3. Note: OPP is a concept that can be used in all OMAPs, it is hence introduced under plat-omap Contributions include: Sanjeev Premi for the initial concept: http://patchwork.kernel.org/patch/50998/ Kevin Hilman for converting original design to device-based Kevin Hilman and Paul Walmsey for cleaning up many of the function abstractions, improvements and data structure handling Romit Dasgupta for using enums instead of opp pointers Thara Gopinath, Eduardo Valentin and Vishwanath BS for fixes and cleanups. Discussions and comments from: http://marc.info/?l=linux-omap&m=126033945313269&w=2 http://marc.info/?l=linux-omap&m=125482970102327&w=2 http://marc.info/?t=125809247500002&r=1&w=2 http://marc.info/?l=linux-omap&m=126025973426007&w=2 incorporated. Cc: Benoit Cousson <b-cousson@ti.com> Cc: Madhusudhan Chikkature Rajashekar <madhu.cr@ti.com> Cc: Phil Carmody <ext-phil.2.carmody@nokia.com> Cc: Roberto Granados Dorado <x0095451@ti.com> Cc: Santosh Shilimkar <santosh.shilimkar@ti.com> Cc: Sergio Alberto Aguirre Rodriguez <saaguirre@ti.com> Cc: Tero Kristo <Tero.Kristo@nokia.com> Cc: Eduardo Valentin <eduardo.valentin@nokia.com> Cc: Paul Walmsley <paul@pwsan.com> Cc: Romit Dasgupta <romit@ti.com> Cc: Sanjeev Premi <premi@ti.com> Cc: Thara Gopinath <thara@ti.com> Cc: Vishwanath BS <vishwanath.bs@ti.com> Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com> --- Documentation/arm/OMAP/omap_pm | 83 ++++++ arch/arm/plat-omap/Makefile | 5 + arch/arm/plat-omap/include/plat/opp.h | 145 +++++++++++ arch/arm/plat-omap/opp.c | 461 +++++++++++++++++++++++++++++++++ 4 files changed, 694 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/include/plat/opp.h create mode 100644 arch/arm/plat-omap/opp.c diff --git a/Documentation/arm/OMAP/omap_pm b/Documentation/arm/OMAP/omap_pm index 5389440..6527cdf 100644 --- a/Documentation/arm/OMAP/omap_pm +++ b/Documentation/arm/OMAP/omap_pm @@ -127,3 +127,86 @@ implementation needs: 10. (*pdata->cpu_set_freq)(unsigned long f) 11. (*pdata->cpu_get_freq)(void) + +OMAP OPP Layer +============== +OMAP SOCs have a standard set of tuples consisting of frequency and +voltage pairs that the device will support per voltage domain. This +is called Operating Performance Point or OPP. The actual definitions +of OMAP OPP varies over silicon within the same family of devices. +For a specific domain, you can have a set of {frequency, voltage} +pairs. As the kernel boots and more information is available, a set +of these are activated based on the precise nature of device the kernel +boots up on. It is interesting to remember that each IP which belongs +to a voltage domain may define their own set of OPPs on top of this. + +OPP layer of its own depends on silicon specific implementation and +board specific data to finalize on the final set of OPPs available +in a system + +Initial list initialization: +--------------------------- +The normal initialization sequence is for boardxyz_init_irq to call +omap2_init_common_hw (for omap2+) and which in turn does the default +setup required. + +Silicon specific initialization: First the OPP layer needs to be told +to initialize the tables for OMAP3, there are two options here: +a) If the desire is to use the default tables defined for that silicon, +the board file does not need to call any initialization function, the +defaults are setup as part of initialization flow when +omap2_init_common_hw is called. + +b) board files would like to customize the default definition. In this +case, board file needs to call explicitly prior to table operations. +the sequence is: +boardxyz_init_irq() +{ + ... do things .. + omap3_pm_init_opp_table() + .. customizations and other things .. + omap2_init_common_hw() +} +1. omap3_pm_init_opp_table - this in turn calls opp_init_list for all +OPP types. This is the generic silicon operating points, however, the +system may have additional features or customizations required. This +flexibility is provided by the following apis: + +Query functions: +---------------- +Search for OPPs for various cases: +2. opp_find_freq_exact - exact search function +3. opp_find_freq_floor - round_up search function +4. opp_find_freq_ceil - round_down search function + +OPP modifier functions: +---------------------- +This allows opp layer users to add customized OPPs or change the table +for any need they may have +5. opp_add - add a new OPP - NOTE: use struct omap_opp_def and define +the custom OPP with OMAP_OPP_DEF for usage. +6. opp_enable - enable a disabled OPP +7. opp_disable - disable an enabled OPP + +OPP Data retrieval functions: +---------------------------- +The following sets of functions are useful for drivers to retrieve +data stored in opp layer for various functions. +8. opp_get_voltage - retrieve voltage for an opp +9. opp_get_freq - get the frequency for an opp +10. opp_get_opp_count - get number of opps enabled for a domain + +Cpufreq table generation: +------------------------ +11. opp_init_cpufreq_table - this translates the OPP layer's internal +OPP arrangement into a table understood and operated upon by the +cpufreq layer. + +Data Structures: +--------------- +struct omap_opp * is a handle structure whose internals are known only +to the OPP layer and is meant to hide the complexity away from users of +opp layer. + +struct omap_opp_def * is the definitions that users can interface with +opp layer and is meant to define one OPP. diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 9405831..c718a0a 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -12,6 +12,11 @@ obj- := # OCPI interconnect support for 1710, 1610 and 5912 obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o +# OPP support in (OMAP3+ only at the moment) +ifdef CONFIG_PM +obj-$(CONFIG_ARCH_OMAP3) += opp.o +endif + # omap_device support (OMAP2+ only at the moment) obj-$(CONFIG_ARCH_OMAP2) += omap_device.o obj-$(CONFIG_ARCH_OMAP3) += omap_device.o diff --git a/arch/arm/plat-omap/include/plat/opp.h b/arch/arm/plat-omap/include/plat/opp.h new file mode 100644 index 0000000..997b56e --- /dev/null +++ b/arch/arm/plat-omap/include/plat/opp.h @@ -0,0 +1,145 @@ +/* + * OMAP OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta <romit@ti.com> + * Copyright (C) 2009 Deep Root Systems, LLC. + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_OMAP_OPP_H +#define __ASM_ARM_OMAP_OPP_H + +#include <linux/err.h> +#include <linux/cpufreq.h> + +#include <plat/common.h> + +/** + * struct omap_opp_def - OMAP OPP Definition + * @hwmod_name: Name of the hwmod for this domain + * @freq: Frequency in hertz corresponding to this OPP + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @enabled: True/false - is this OPP enabled/disabled by default + * + * OMAP SOCs have a standard set of tuples consisting of frequency and voltage + * pairs that the device will support per voltage domain. This is called + * Operating Points or OPP. The actual definitions of OMAP Operating Points + * varies over silicon within the same family of devices. For a specific + * domain, you can have a set of {frequency, voltage} pairs and this is denoted + * by an array of omap_opp_def. As the kernel boots and more information is + * available, a set of these are activated based on the precise nature of + * device the kernel boots up on. It is interesting to remember that each IP + * which belongs to a voltage domain may define their own set of OPPs on top + * of this - but this is handled by the appropriate driver. + */ +struct omap_opp_def { + char *hwmod_name; + + unsigned long freq; + unsigned long u_volt; + + bool enabled; +}; + +/* + * Initialization wrapper used to define an OPP. + * To point at the end of a terminator of a list of OPPs, + * use OMAP_OPP_DEF(NULL, 0, 0, 0) + */ +#define OMAP_OPP_DEF(_hwmod_name, _enabled, _freq, _uv) \ +{ \ + .hwmod_name = _hwmod_name, \ + .enabled = _enabled, \ + .freq = _freq, \ + .u_volt = _uv, \ +} + +struct omap_opp; + +#ifdef CONFIG_PM + +unsigned long opp_get_voltage(const struct omap_opp *opp); + +unsigned long opp_get_freq(const struct omap_opp *opp); + +int opp_get_opp_count(struct device *dev); + +struct omap_opp *opp_find_freq_exact(struct device *dev, + unsigned long freq, bool enabled); + +struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq); + +struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq); + +int opp_add(const struct omap_opp_def *opp_def); + +int opp_enable(struct omap_opp *opp); + +int opp_disable(struct omap_opp *opp); + +void opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table); +#else +static inline unsigned long opp_get_voltage(const struct omap_opp *opp) +{ + return 0; +} + +static inline unsigned long opp_get_freq(const struct omap_opp *opp) +{ + return 0; +} + +static inline int opp_get_opp_count(struct omap_opp *oppl) +{ + return 0; +} + +static inline struct omap_opp *opp_find_freq_exact(struct omap_opp *oppl, + unsigned long freq, + bool enabled) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_find_freq_floor(struct omap_opp *oppl, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_find_freq_ceil(struct omap_opp *oppl, + unsigned long *freq) +{ + return ERR_PTR(-EINVAL); +} + +static inline struct omap_opp *opp_add(struct omap_opp *oppl, + const struct omap_opp_def *opp_def) +{ + return ERR_PTR(-EINVAL); +} + +static inline int opp_enable(struct omap_opp *opp) +{ + return 0; +} + +static inline int opp_disable(struct omap_opp *opp) +{ + return 0; +} + +static inline +void opp_init_cpufreq_table(struct omap_opp *opps, + struct cpufreq_frequency_table **table) +{ +} + +#endif /* CONFIG_PM */ +#endif /* __ASM_ARM_OMAP_OPP_H */ diff --git a/arch/arm/plat-omap/opp.c b/arch/arm/plat-omap/opp.c new file mode 100644 index 0000000..17f93b2 --- /dev/null +++ b/arch/arm/plat-omap/opp.c @@ -0,0 +1,461 @@ +/* + * OMAP OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta <romit@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/list.h> + +#include <plat/opp.h> +#include <plat/omap_device.h> + +/** + * struct omap_opp - OMAP OPP description structure + * @enabled: true/false - marking this OPP as enabled/disabled + * @rate: Frequency in hertz + * @u_volt: Nominal voltage in microvolts corresponding to this OPP + * @dev_opp: contains the device_opp struct + * + * This structure stores the OPP information for a given domain. + */ +struct omap_opp { + struct list_head node; + + bool enabled; + unsigned long rate; + unsigned long u_volt; + + struct device_opp *dev_opp; +}; + +/** + * struct device_opp - Device opp structure + * @node: list node + * @oh: hwmod handle + * @dev: device handle + * @opp_list: list of opps + * @opp_count: num opps + * @enabled_opp_count: how many opps are actually enabled + * + * This is an internal datastructure maintaining the link to + * omap_opps attached to a domain device. This structure is not + * meant to be shared with users as it private to opp layer. + */ +struct device_opp { + struct list_head node; + + struct omap_hwmod *oh; + struct device *dev; + + struct list_head opp_list; + u32 opp_count; + u32 enabled_opp_count; +}; + +static LIST_HEAD(dev_opp_list); + +/** + * find_device_opp() - find device_opp struct using device pointer + * @dev: device pointer used to lookup device OPPs + * + * Search list of device OPPs for one containing matching device. + * + * Returns pointer to 'struct device_opp' if found, otherwise -ENODEV or + * -EINVAL based on type of error. + */ +static struct device_opp *find_device_opp(struct device *dev) +{ + struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV); + + if (unlikely(!dev || IS_ERR(dev))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return ERR_PTR(-EINVAL); + } + + list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { + if (tmp_dev_opp->dev == dev) { + dev_opp = tmp_dev_opp; + break; + } + } + + return dev_opp; +} + +/** + * opp_get_voltage() - Gets the voltage corresponding to an opp + * @opp: opp for which voltage has to be returned for + * + * Return voltage in micro volt corresponding to the opp, else + * return 0 + */ +unsigned long opp_get_voltage(const struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return 0; + } + + return opp->u_volt; +} + +/** + * opp_get_freq() - Gets the frequency corresponding to an opp + * @opp: opp for which frequency has to be returned for + * + * Return frequency in hertz corresponding to the opp, else + * return 0 + */ +unsigned long opp_get_freq(const struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return 0; + } + + return opp->rate; +} + +/** + * opp_get_opp_count() - Get number of opps enabled in the opp list + * @opp_type: OPP type we want to count + * + * This functions returns the number of opps if there are any OPPs enabled, + * else returns corresponding error value. + */ +int opp_get_opp_count(struct device *dev) +{ + struct device_opp *dev_opp; + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return -ENODEV; + + return dev_opp->enabled_opp_count; +} + +/** + * opp_find_freq_exact() - search for an exact frequency + * @opp_type: OPP type we want to search in. + * @freq: frequency to search for + * @enabled: enabled/disabled OPP to search for + * + * Searches for exact match in the opp list and returns handle to the matching + * opp if found, else returns ERR_PTR in case of error and should be handled + * using IS_ERR. + * + * Note enabled is a modifier for the search. if enabled=true, then the match is + * for exact matching frequency and is enabled. if false, the match is for exact + * frequency which is disabled. + */ +struct omap_opp *opp_find_freq_exact(struct device *dev, + unsigned long freq, bool enabled) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate == freq) { + opp = temp_opp; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_ceil() - Search for an rounded ceil freq + * @opp_type: OPP type where we want to search in + * @freq: Start frequency + * + * Search for the matching ceil *enabled* OPP from a starting freq + * for a domain. + * + * Returns *opp and *freq is populated with the match, else + * returns NULL opp if no match, else returns ERR_PTR in case of error. + * + * Example usages: + * * find match/next highest available frequency * + * freq = 350000; + * opp = opp_find_freq_ceil(OPP_MPU, &freq)) + * if (IS_ERR(opp)) + * pr_err("unable to find a higher frequency\n"); + * else + * pr_info("match freq = %ld\n", freq); + * + * * print all supported frequencies in ascending order * + * freq = 0; * Search for the lowest enabled frequency * + * while (!IS_ERR(opp = opp_find_freq_ceil(OPP_MPU, &freq)) { + * pr_info("freq = %ld\n", freq); + * freq++; * for next higher match * + * } + */ +struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate >= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} + +/** + * opp_find_freq_floor() - Search for an rounded floor freq + * @opp_type: OPP type we want to search in + * @freq: Start frequency + * + * Search for the matching floor *enabled* OPP from a starting freq + * for a domain. + * + * Returns *opp and *freq is populated with the next match, else + * returns NULL opp if no match, else returns ERR_PTR in case of error. + * + * Example usages: + * * find match/next lowest available frequency + * freq = 350000; + * opp = opp_find_freq_floor(OPP_MPU, &freq))) + * if (IS_ERR(opp)) + * pr_err ("unable to find a lower frequency\n"); + * else + * pr_info("match freq = %ld\n", freq); + * + * * print all supported frequencies in descending order * + * freq = ULONG_MAX; * search highest enabled frequency * + * while (!IS_ERR(opp = opp_find_freq_floor(OPP_MPU, &freq)) { + * pr_info("freq = %ld\n", freq); + * freq--; * for next lower match * + * } + */ +struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq) +{ + struct device_opp *dev_opp; + struct omap_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) + return opp; + + list_for_each_entry_reverse(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->enabled && temp_opp->rate <= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} + +/* wrapper to reuse converting opp_def to opp struct */ +static void omap_opp_populate(struct omap_opp *opp, + const struct omap_opp_def *opp_def) +{ + opp->rate = opp_def->freq; + opp->enabled = opp_def->enabled; + opp->u_volt = opp_def->u_volt; +} + +/** + * opp_add() - Add an OPP table from a table definitions + * @opp_def: omap_opp_def to describe the OPP which we want to add. + * + * This function adds an opp definition to the opp list and returns status. + */ +int opp_add(const struct omap_opp_def *opp_def) +{ + struct omap_hwmod *oh; + struct device *dev = NULL; + struct device_opp *tmp_dev_opp, *dev_opp = NULL; + struct omap_opp *opp, *new_opp; + struct platform_device *pdev; + struct list_head *head; + + /* find the correct hwmod, and device */ + if (!opp_def->hwmod_name) { + pr_err("%s: missing name of omap_hwmod, ignoring.\n", __func__); + return -EINVAL; + } + oh = omap_hwmod_lookup(opp_def->hwmod_name); + if (!oh || !oh->od) { + pr_warn("%s: no hwmod or odev for %s, cannot add OPPs.\n", + __func__, opp_def->hwmod_name); + return -EINVAL; + } + pdev = &oh->od->pdev; + dev = &oh->od->pdev.dev; + + /* Check for existing list for 'dev' */ + list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { + if (dev == tmp_dev_opp->dev) { + dev_opp = tmp_dev_opp; + break; + } + } + + if (!dev_opp) { + /* Allocate a new device OPP table */ + dev_opp = kzalloc(sizeof(struct device_opp), GFP_KERNEL); + if (!dev_opp) { + pr_warning("%s: unable to allocate device struct\n", + __func__); + return -ENOMEM; + } + + dev_opp->oh = oh; + dev_opp->dev = &oh->od->pdev.dev; + INIT_LIST_HEAD(&dev_opp->opp_list); + + list_add(&dev_opp->node, &dev_opp_list); + } + + /* allocate new OPP node */ + new_opp = kzalloc(sizeof(struct omap_opp), GFP_KERNEL); + if (!new_opp) { + if (list_empty(&dev_opp->opp_list)) { + list_del(&dev_opp->node); + kfree(dev_opp); + } + pr_warning("%s: unable to allocate new opp node\n", + __func__); + return -ENOMEM; + } + omap_opp_populate(new_opp, opp_def); + + /* Insert new OPP in order of increasing frequency */ + head = &dev_opp->opp_list; + list_for_each_entry_reverse(opp, &dev_opp->opp_list, node) { + if (new_opp->rate >= opp->rate) { + head = &opp->node; + break; + } + } + list_add(&new_opp->node, head); + dev_opp->opp_count++; + if (new_opp->enabled) + dev_opp->enabled_opp_count++; + + return 0; +} + +/** + * opp_enable() - Enable a specific OPP + * @opp: Pointer to opp + * + * Enables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. + * + * OPP used here is from the the opp_is_valid/opp_has_freq or other search + * functions + */ +int opp_enable(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return -EINVAL; + } + + if (!opp->enabled && opp->dev_opp) + opp->dev_opp->enabled_opp_count++; + + opp->enabled = true; + + return 0; +} + +/** + * opp_disable() - Disable a specific OPP + * @opp: Pointer to opp + * + * Disables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. + * + * OPP used here is from the the opp_is_valid/opp_has_freq or other search + * functions + */ +int opp_disable(struct omap_opp *opp) +{ + if (unlikely(!opp || IS_ERR(opp))) { + pr_err("%s: Invalid parameters being passed\n", __func__); + return -EINVAL; + } + + if (opp->enabled && opp->dev_opp) + opp->dev_opp->enabled_opp_count--; + + opp->enabled = false; + + return 0; +} + +/** + * opp_init_cpufreq_table() - create a cpufreq table for a domain + * @opp_type: OPP type to initialize this list for + * @table: Cpufreq table returned back to caller + * + * Generate a cpufreq table for a provided domain - this assumes that the + * opp list is already initialized and ready for usage + */ +void opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + struct device_opp *dev_opp; + struct omap_opp *opp; + struct cpufreq_frequency_table *freq_table; + int i = 0; + + dev_opp = find_device_opp(dev); + if (IS_ERR(dev_opp)) { + pr_warning("%s: unable to find device\n", __func__); + return; + } + + freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * + (dev_opp->enabled_opp_count + 1), GFP_ATOMIC); + if (!freq_table) { + pr_warning("%s: failed to allocate frequency table\n", + __func__); + return; + } + + list_for_each_entry(opp, &dev_opp->opp_list, node) { + if (opp->enabled) { + freq_table[i].index = i; + freq_table[i].frequency = opp->rate / 1000; + i++; + } + } + + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + *table = &freq_table[0]; +} -- 1.7.2.1 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* RE: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-15 21:56 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman @ 2010-09-16 10:25 ` Gopinath, Thara 2010-09-16 10:32 ` Menon, Nishanth 2010-09-16 12:19 ` Linus Walleij 2010-09-16 13:54 ` Roger Quadros 2 siblings, 1 reply; 21+ messages in thread From: Gopinath, Thara @ 2010-09-16 10:25 UTC (permalink / raw) To: Kevin Hilman, linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org >>-----Original Message----- >>From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-owner@vger.kernel.org] On Behalf Of Kevin >>Hilman >>Sent: Thursday, September 16, 2010 3:27 AM >>To: linux-omap@vger.kernel.org >>Cc: linux-arm-kernel@lists.infradead.org >>Subject: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs >> >>From: Nishanth Menon <nm@ti.com> >> >>OMAP SOCs have a standard set of tuples consisting of frequency and >>voltage pairs that the device will support per voltage domain. These >>are called Operating Performance Points or OPPs. The actual >>definitions of OMAP Operating Points varies over silicon within the >>same family of devices. For a specific domain, you can have a set of >>{frequency, voltage} pairs. As the kernel boots and more information >>is available, a set of these are activated based on the precise nature >>of device the kernel boots up on. It is interesting to remember that >>each IP which belongs to a voltage domain may define their own set of >>OPPs on top of this. >> >>This introduces a common handling OPP mechanism accross all OMAPs. >>As a start this is used for OMAP3. >> >>Note: OPP is a concept that can be used in all OMAPs, it is hence >>introduced under plat-omap >> >>Contributions include: >>Sanjeev Premi for the initial concept: >> http://patchwork.kernel.org/patch/50998/ >>Kevin Hilman for converting original design to device-based >>Kevin Hilman and Paul Walmsey for cleaning up many of the function >>abstractions, improvements and data structure handling >>Romit Dasgupta for using enums instead of opp pointers >>Thara Gopinath, Eduardo Valentin and Vishwanath BS for fixes and >>cleanups. >> >>Discussions and comments from: >>http://marc.info/?l=linux-omap&m=126033945313269&w=2 >>http://marc.info/?l=linux-omap&m=125482970102327&w=2 >>http://marc.info/?t=125809247500002&r=1&w=2 >>http://marc.info/?l=linux-omap&m=126025973426007&w=2 >>incorporated. >> >>Cc: Benoit Cousson <b-cousson@ti.com> >>Cc: Madhusudhan Chikkature Rajashekar <madhu.cr@ti.com> >>Cc: Phil Carmody <ext-phil.2.carmody@nokia.com> >>Cc: Roberto Granados Dorado <x0095451@ti.com> >>Cc: Santosh Shilimkar <santosh.shilimkar@ti.com> >>Cc: Sergio Alberto Aguirre Rodriguez <saaguirre@ti.com> >>Cc: Tero Kristo <Tero.Kristo@nokia.com> >>Cc: Eduardo Valentin <eduardo.valentin@nokia.com> >>Cc: Paul Walmsley <paul@pwsan.com> >>Cc: Romit Dasgupta <romit@ti.com> >>Cc: Sanjeev Premi <premi@ti.com> >>Cc: Thara Gopinath <thara@ti.com> >>Cc: Vishwanath BS <vishwanath.bs@ti.com> >>Signed-off-by: Nishanth Menon <nm@ti.com> >>Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com> >>--- >> Documentation/arm/OMAP/omap_pm | 83 ++++++ >> arch/arm/plat-omap/Makefile | 5 + >> arch/arm/plat-omap/include/plat/opp.h | 145 +++++++++++ >> arch/arm/plat-omap/opp.c | 461 +++++++++++++++++++++++++++++++++ >> 4 files changed, 694 insertions(+), 0 deletions(-) >> create mode 100644 arch/arm/plat-omap/include/plat/opp.h >> create mode 100644 arch/arm/plat-omap/opp.c >> >>diff --git a/Documentation/arm/OMAP/omap_pm b/Documentation/arm/OMAP/omap_pm >>index 5389440..6527cdf 100644 >>--- a/Documentation/arm/OMAP/omap_pm >>+++ b/Documentation/arm/OMAP/omap_pm >>@@ -127,3 +127,86 @@ implementation needs: >> 10. (*pdata->cpu_set_freq)(unsigned long f) >> >> 11. (*pdata->cpu_get_freq)(void) >>+ >>+OMAP OPP Layer >>+============== >>+OMAP SOCs have a standard set of tuples consisting of frequency and >>+voltage pairs that the device will support per voltage domain. This >>+is called Operating Performance Point or OPP. The actual definitions >>+of OMAP OPP varies over silicon within the same family of devices. >>+For a specific domain, you can have a set of {frequency, voltage} >>+pairs. As the kernel boots and more information is available, a set >>+of these are activated based on the precise nature of device the kernel >>+boots up on. It is interesting to remember that each IP which belongs >>+to a voltage domain may define their own set of OPPs on top of this. >>+ >>+OPP layer of its own depends on silicon specific implementation and >>+board specific data to finalize on the final set of OPPs available >>+in a system >>+ >>+Initial list initialization: >>+--------------------------- >>+The normal initialization sequence is for boardxyz_init_irq to call >>+omap2_init_common_hw (for omap2+) and which in turn does the default >>+setup required. >>+ >>+Silicon specific initialization: First the OPP layer needs to be told >>+to initialize the tables for OMAP3, there are two options here: >>+a) If the desire is to use the default tables defined for that silicon, >>+the board file does not need to call any initialization function, the >>+defaults are setup as part of initialization flow when >>+omap2_init_common_hw is called. >>+ >>+b) board files would like to customize the default definition. In this >>+case, board file needs to call explicitly prior to table operations. >>+the sequence is: >>+boardxyz_init_irq() >>+{ >>+ ... do things .. >>+ omap3_pm_init_opp_table() >>+ .. customizations and other things .. >>+ omap2_init_common_hw() >>+} >>+1. omap3_pm_init_opp_table - this in turn calls opp_init_list for all >>+OPP types. This is the generic silicon operating points, however, the >>+system may have additional features or customizations required. This >>+flexibility is provided by the following apis: >>+ >>+Query functions: >>+---------------- >>+Search for OPPs for various cases: >>+2. opp_find_freq_exact - exact search function >>+3. opp_find_freq_floor - round_up search function >>+4. opp_find_freq_ceil - round_down search function >>+ >>+OPP modifier functions: >>+---------------------- >>+This allows opp layer users to add customized OPPs or change the table >>+for any need they may have >>+5. opp_add - add a new OPP - NOTE: use struct omap_opp_def and define >>+the custom OPP with OMAP_OPP_DEF for usage. >>+6. opp_enable - enable a disabled OPP >>+7. opp_disable - disable an enabled OPP >>+ >>+OPP Data retrieval functions: >>+---------------------------- >>+The following sets of functions are useful for drivers to retrieve >>+data stored in opp layer for various functions. >>+8. opp_get_voltage - retrieve voltage for an opp >>+9. opp_get_freq - get the frequency for an opp >>+10. opp_get_opp_count - get number of opps enabled for a domain >>+ >>+Cpufreq table generation: >>+------------------------ >>+11. opp_init_cpufreq_table - this translates the OPP layer's internal >>+OPP arrangement into a table understood and operated upon by the >>+cpufreq layer. >>+ >>+Data Structures: >>+--------------- >>+struct omap_opp * is a handle structure whose internals are known only >>+to the OPP layer and is meant to hide the complexity away from users of >>+opp layer. >>+ >>+struct omap_opp_def * is the definitions that users can interface with >>+opp layer and is meant to define one OPP. >>diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile >>index 9405831..c718a0a 100644 >>--- a/arch/arm/plat-omap/Makefile >>+++ b/arch/arm/plat-omap/Makefile >>@@ -12,6 +12,11 @@ obj- := >> # OCPI interconnect support for 1710, 1610 and 5912 >> obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o >> >>+# OPP support in (OMAP3+ only at the moment) >>+ifdef CONFIG_PM >>+obj-$(CONFIG_ARCH_OMAP3) += opp.o >>+endif >>+ >> # omap_device support (OMAP2+ only at the moment) >> obj-$(CONFIG_ARCH_OMAP2) += omap_device.o >> obj-$(CONFIG_ARCH_OMAP3) += omap_device.o >>diff --git a/arch/arm/plat-omap/include/plat/opp.h b/arch/arm/plat-omap/include/plat/opp.h >>new file mode 100644 >>index 0000000..997b56e >>--- /dev/null >>+++ b/arch/arm/plat-omap/include/plat/opp.h >>@@ -0,0 +1,145 @@ >>+/* >>+ * OMAP OPP Interface >>+ * >>+ * Copyright (C) 2009-2010 Texas Instruments Incorporated. >>+ * Nishanth Menon >>+ * Romit Dasgupta <romit@ti.com> >>+ * Copyright (C) 2009 Deep Root Systems, LLC. >>+ * Kevin Hilman >>+ * >>+ * This program is free software; you can redistribute it and/or modify >>+ * it under the terms of the GNU General Public License version 2 as >>+ * published by the Free Software Foundation. >>+ */ >>+#ifndef __ASM_ARM_OMAP_OPP_H >>+#define __ASM_ARM_OMAP_OPP_H >>+ >>+#include <linux/err.h> >>+#include <linux/cpufreq.h> >>+ >>+#include <plat/common.h> >>+ >>+/** >>+ * struct omap_opp_def - OMAP OPP Definition >>+ * @hwmod_name: Name of the hwmod for this domain >>+ * @freq: Frequency in hertz corresponding to this OPP >>+ * @u_volt: Nominal voltage in microvolts corresponding to this OPP >>+ * @enabled: True/false - is this OPP enabled/disabled by default >>+ * >>+ * OMAP SOCs have a standard set of tuples consisting of frequency and voltage >>+ * pairs that the device will support per voltage domain. This is called >>+ * Operating Points or OPP. The actual definitions of OMAP Operating Points >>+ * varies over silicon within the same family of devices. For a specific >>+ * domain, you can have a set of {frequency, voltage} pairs and this is denoted >>+ * by an array of omap_opp_def. As the kernel boots and more information is >>+ * available, a set of these are activated based on the precise nature of >>+ * device the kernel boots up on. It is interesting to remember that each IP >>+ * which belongs to a voltage domain may define their own set of OPPs on top >>+ * of this - but this is handled by the appropriate driver. >>+ */ >>+struct omap_opp_def { >>+ char *hwmod_name; >>+ >>+ unsigned long freq; >>+ unsigned long u_volt; >>+ >>+ bool enabled; >>+}; >>+ >>+/* >>+ * Initialization wrapper used to define an OPP. >>+ * To point at the end of a terminator of a list of OPPs, >>+ * use OMAP_OPP_DEF(NULL, 0, 0, 0) >>+ */ >>+#define OMAP_OPP_DEF(_hwmod_name, _enabled, _freq, _uv) \ >>+{ \ >>+ .hwmod_name = _hwmod_name, \ >>+ .enabled = _enabled, \ >>+ .freq = _freq, \ >>+ .u_volt = _uv, \ >>+} >>+ >>+struct omap_opp; >>+ >>+#ifdef CONFIG_PM >>+ >>+unsigned long opp_get_voltage(const struct omap_opp *opp); >>+ >>+unsigned long opp_get_freq(const struct omap_opp *opp); >>+ >>+int opp_get_opp_count(struct device *dev); >>+ >>+struct omap_opp *opp_find_freq_exact(struct device *dev, >>+ unsigned long freq, bool enabled); >>+ >>+struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq); >>+ >>+struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq); >>+ >>+int opp_add(const struct omap_opp_def *opp_def); >>+ >>+int opp_enable(struct omap_opp *opp); >>+ >>+int opp_disable(struct omap_opp *opp); >>+ >>+void opp_init_cpufreq_table(struct device *dev, >>+ struct cpufreq_frequency_table **table); >>+#else Hello Kevin, IN case of CONFIG_PM not being defined the else part will cause a compilation break as the signature of these APIs defined in the else part do not match with the signature in the if part. >>+static inline unsigned long opp_get_voltage(const struct omap_opp *opp) >>+{ >>+ return 0; >>+} >>+ >>+static inline unsigned long opp_get_freq(const struct omap_opp *opp) >>+{ >>+ return 0; >>+} >>+ >>+static inline int opp_get_opp_count(struct omap_opp *oppl) >>+{ >>+ return 0; >>+} >>+ >>+static inline struct omap_opp *opp_find_freq_exact(struct omap_opp *oppl, >>+ unsigned long freq, >>+ bool enabled) >>+{ >>+ return ERR_PTR(-EINVAL); >>+} >>+ >>+static inline struct omap_opp *opp_find_freq_floor(struct omap_opp *oppl, >>+ unsigned long *freq) >>+{ >>+ return ERR_PTR(-EINVAL); >>+} >>+ >>+static inline struct omap_opp *opp_find_freq_ceil(struct omap_opp *oppl, >>+ unsigned long *freq) >>+{ >>+ return ERR_PTR(-EINVAL); >>+} >>+ >>+static inline struct omap_opp *opp_add(struct omap_opp *oppl, >>+ const struct omap_opp_def *opp_def) >>+{ >>+ return ERR_PTR(-EINVAL); >>+} >>+ >>+static inline int opp_enable(struct omap_opp *opp) >>+{ >>+ return 0; >>+} >>+ >>+static inline int opp_disable(struct omap_opp *opp) >>+{ >>+ return 0; >>+} >>+ >>+static inline >>+void opp_init_cpufreq_table(struct omap_opp *opps, >>+ struct cpufreq_frequency_table **table) >>+{ >>+} >>+ >>+#endif /* CONFIG_PM */ >>+#endif /* __ASM_ARM_OMAP_OPP_H */ <snip> Regards Thara ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 10:25 ` Gopinath, Thara @ 2010-09-16 10:32 ` Menon, Nishanth 2010-09-16 10:33 ` Gopinath, Thara 0 siblings, 1 reply; 21+ messages in thread From: Menon, Nishanth @ 2010-09-16 10:32 UTC (permalink / raw) To: Gopinath, Thara, Kevin Hilman, linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org > -----Original Message----- > From: linux-omap-owner@vger.kernel.org [mailto:linux-omap- > owner@vger.kernel.org] On Behalf Of Gopinath, Thara [...] > >>diff --git a/arch/arm/plat-omap/include/plat/opp.h b/arch/arm/plat- > omap/include/plat/opp.h > >>new file mode 100644 > >>index 0000000..997b56e > >>--- /dev/null > >>+++ b/arch/arm/plat-omap/include/plat/opp.h [..] > >>+ > >>+#ifdef CONFIG_PM > >>+ [..] > >>+struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long > *freq); > >>+ > >>+int opp_add(const struct omap_opp_def *opp_def); > >>+ > >>+int opp_enable(struct omap_opp *opp); > >>+ > >>+int opp_disable(struct omap_opp *opp); > >>+ > >>+void opp_init_cpufreq_table(struct device *dev, > >>+ struct cpufreq_frequency_table **table); > >>+#else > > Hello Kevin, > > IN case of CONFIG_PM not being defined the else part will cause a > compilation break as > the signature of these APIs defined in the else part do not match with the > signature in > the if part. > Thanks for the catch. Will send a patch for this. Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 10:32 ` Menon, Nishanth @ 2010-09-16 10:33 ` Gopinath, Thara 0 siblings, 0 replies; 21+ messages in thread From: Gopinath, Thara @ 2010-09-16 10:33 UTC (permalink / raw) To: Menon, Nishanth, Kevin Hilman, linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org >>-----Original Message----- >>From: Menon, Nishanth >>Sent: Thursday, September 16, 2010 4:02 PM >>To: Gopinath, Thara; Kevin Hilman; linux-omap@vger.kernel.org >>Cc: linux-arm-kernel@lists.infradead.org >>Subject: RE: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs >> >>> -----Original Message----- >>> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap- >>> owner@vger.kernel.org] On Behalf Of Gopinath, Thara >> >>[...] >>> >>diff --git a/arch/arm/plat-omap/include/plat/opp.h b/arch/arm/plat- >>> omap/include/plat/opp.h >>> >>new file mode 100644 >>> >>index 0000000..997b56e >>> >>--- /dev/null >>> >>+++ b/arch/arm/plat-omap/include/plat/opp.h >>[..] >>> >>+ >>> >>+#ifdef CONFIG_PM >>> >>+ >>[..] >>> >>+struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long >>> *freq); >>> >>+ >>> >>+int opp_add(const struct omap_opp_def *opp_def); >>> >>+ >>> >>+int opp_enable(struct omap_opp *opp); >>> >>+ >>> >>+int opp_disable(struct omap_opp *opp); >>> >>+ >>> >>+void opp_init_cpufreq_table(struct device *dev, >>> >>+ struct cpufreq_frequency_table **table); >>> >>+#else >>> >>> Hello Kevin, >>> >>> IN case of CONFIG_PM not being defined the else part will cause a >>> compilation break as >>> the signature of these APIs defined in the else part do not match with the >>> signature in >>> the if part. >>> >>Thanks for the catch. Will send a patch for this. I learnt this the hard way by actually hitting the issue :-)!! Regards Thara ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-15 21:56 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman 2010-09-16 10:25 ` Gopinath, Thara @ 2010-09-16 12:19 ` Linus Walleij 2010-09-16 12:40 ` Nishanth Menon 2010-09-16 15:08 ` Kevin Hilman 2010-09-16 13:54 ` Roger Quadros 2 siblings, 2 replies; 21+ messages in thread From: Linus Walleij @ 2010-09-16 12:19 UTC (permalink / raw) To: Kevin Hilman; +Cc: linux-omap, linux-arm-kernel 2010/9/15 Kevin Hilman <khilman@deeprootsystems.com>: > OMAP SOCs have a standard set of tuples consisting of frequency and > voltage pairs that the device will support per voltage domain. These > are called Operating Performance Points or OPPs. > (...) > This introduces a common handling OPP mechanism accross all OMAPs. > As a start this is used for OMAP3. OPPs are a generic concept, it's in silicon construction textbooks and all. Should this code not be made generic instead? You wouldn't make regulators or even DMA platform-specific these days, so why should OPPs be? What in this code is actually OMAP-specific, more than that you name some functions omap_*, and how hard would it be to put it under arch/arm/common/*.c arch/arm/include/asm/*.h Possible even higher up in the directory hiearchy in include/linux/opp.h for the header and drivers/opp/*.c, because I think SuperH and power are not that different in this respect. Yours, Linus Walleij -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 12:19 ` Linus Walleij @ 2010-09-16 12:40 ` Nishanth Menon 2010-09-16 13:24 ` Linus Walleij 2010-09-16 15:08 ` Kevin Hilman 1 sibling, 1 reply; 21+ messages in thread From: Nishanth Menon @ 2010-09-16 12:40 UTC (permalink / raw) To: Linus Walleij Cc: Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Linus Walleij had written, on 09/16/2010 07:19 AM, the following: > 2010/9/15 Kevin Hilman <khilman@deeprootsystems.com>: > >> OMAP SOCs have a standard set of tuples consisting of frequency and >> voltage pairs that the device will support per voltage domain. These >> are called Operating Performance Points or OPPs. >> (...) >> This introduces a common handling OPP mechanism accross all OMAPs. >> As a start this is used for OMAP3. > > OPPs are a generic concept, it's in silicon construction textbooks and all. > Should this code not be made generic instead? You wouldn't make > regulators or even DMA platform-specific these days, so why should > OPPs be? As far as I see this patch : hwmod[1] which is omap specific which inturn depends on omap_device. - this impacts opp_add function in the opp layer. [1] http://marc.info/?l=linux-omap&m=128226580816341&w=2 -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 12:40 ` Nishanth Menon @ 2010-09-16 13:24 ` Linus Walleij 0 siblings, 0 replies; 21+ messages in thread From: Linus Walleij @ 2010-09-16 13:24 UTC (permalink / raw) To: Nishanth Menon Cc: Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Nishanth Menon wrote: > Linus Walleij had written, on 09/16/2010 07:19 AM, the following: >> 2010/9/15 Kevin Hilman <khilman@deeprootsystems.com>: >> >>> OMAP SOCs have a standard set of tuples consisting of frequency and >>> voltage pairs that the device will support per voltage domain. These >>> are called Operating Performance Points or OPPs. >>> (...) >>> This introduces a common handling OPP mechanism accross all OMAPs. >>> As a start this is used for OMAP3. >> OPPs are a generic concept, it's in silicon construction textbooks and all. >> Should this code not be made generic instead? You wouldn't make >> regulators or even DMA platform-specific these days, so why should >> OPPs be? > As far as I see this patch : > hwmod[1] which is omap specific which inturn depends on omap_device. - > this impacts opp_add function in the opp layer. Then wrap your local OPP in some clever way: struct omap_opp { struct hwmod foo; struct opp opp; }; container_of() is your friend. Alternatively and not as elegant is to provide an void *private_data; in the generic opp struct. And similar design patterns for code and other platform-specific hooks. Overridable struct opp_ops for example, the same way we just abstracted the l2x0 operations for example. It's not like there are no precedents in the linux kernel on how to handle a superclass of some struct, so how hard can it be? The fact that a single struct member is OMAP-specific doesn't nix the fact that the major part of this OPP framework is generic code. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 12:19 ` Linus Walleij 2010-09-16 12:40 ` Nishanth Menon @ 2010-09-16 15:08 ` Kevin Hilman 2010-09-16 15:31 ` Nishanth Menon 1 sibling, 1 reply; 21+ messages in thread From: Kevin Hilman @ 2010-09-16 15:08 UTC (permalink / raw) To: Linus Walleij, Nishanth Menon; +Cc: linux-omap, linux-arm-kernel Hi Linus, Linus Walleij <linus.walleij@stericsson.com> writes: > 2010/9/15 Kevin Hilman <khilman@deeprootsystems.com>: > >> OMAP SOCs have a standard set of tuples consisting of frequency and >> voltage pairs that the device will support per voltage domain. These >> are called Operating Performance Points or OPPs. >> (...) >> This introduces a common handling OPP mechanism accross all OMAPs. >> As a start this is used for OMAP3. > > OPPs are a generic concept, it's in silicon construction textbooks and all. > Should this code not be made generic instead? You wouldn't make > regulators or even DMA platform-specific these days, so why should > OPPs be? You're right. > What in this code is actually OMAP-specific Only the users. ;) We currently register OPPs using an OMAP hwmod name, but we could easily change that to use a struct device instead which would make this much more generic (note we manage OPPs per-device, not just for the CPU) The patch below[1] demonstrates quickly how easily we could remove all OMAP specific stuff from opp.[ch], and move it to the OMAP-specific code that does the opp_add() > more than that you name > some functions omap_*, and how hard would it be to put it under > arch/arm/common/*.c > arch/arm/include/asm/*.h > > Possible even higher up in the directory hiearchy in include/linux/opp.h > for the header and drivers/opp/*.c, because I think SuperH and power > are not that different in this respect. Yeah, I guess this isn't ARM specific either, so should be at a higher level. Nishanth, can take my hack below and continue this evolution? As I demonstrate with this hack, this won't really change anything for us. Kevin From 96c4e27ba0cb3d9a056693340c6221bc093bce2c Mon Sep 17 00:00:00 2001 From: Kevin Hilman <khilman@deeprootsystems.com> Date: Thu, 16 Sep 2010 07:58:16 -0700 Subject: [PATCH] OPP: remove OMAP specifics to make more generic Internal OPP management is based on 'struct device *', so we don't need to have omap_hwmod specific knowledge in this layer. Change opp_add() to take a 'struct device *' instead of using the OMAP hwmod in the opp_def to lookup the struct device. Move OMAP-specific hwmod lookups into the OMAP specific layer which adds OPPs to the database. Quickly tested on OMAP3 to see that all OPPs are still registered correctly with CPUfreq --- arch/arm/mach-omap2/opp3xxx_data.c | 18 +++++++++++++++++- arch/arm/plat-omap/include/plat/opp.h | 2 +- arch/arm/plat-omap/opp.c | 24 ++---------------------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/arch/arm/mach-omap2/opp3xxx_data.c b/arch/arm/mach-omap2/opp3xxx_data.c index e337aeb..f3f9ae4 100644 --- a/arch/arm/mach-omap2/opp3xxx_data.c +++ b/arch/arm/mach-omap2/opp3xxx_data.c @@ -23,6 +23,7 @@ #include <plat/opp.h> #include <plat/cpu.h> +#include <plat/omap_device.h> static struct omap_opp_def __initdata omap34xx_opp_def_list[] = { /* MPU OPP1 */ @@ -114,7 +115,22 @@ int __init omap3_pm_init_opp_table(void) opp_def = omap3_opp_def_list; for (i = 0; i < omap3_opp_def_size; i++) { - r = opp_add(opp_def++); + struct omap_hwmod *oh; + struct device *dev; + + if (!opp_def->hwmod_name) { + pr_err("%s: missing name of omap_hwmod, ignoring.\n", __func__); + return -EINVAL; + } + oh = omap_hwmod_lookup(opp_def->hwmod_name); + if (!oh || !oh->od) { + pr_warn("%s: no hwmod or odev for %s, cannot add OPPs.\n", + __func__, opp_def->hwmod_name); + return -EINVAL; + } + dev = &oh->od->pdev.dev; + + r = opp_add(dev, opp_def++); if (r) pr_err("unable to add OPP %ld Hz for %s\n", opp_def->freq, opp_def->hwmod_name); diff --git a/arch/arm/plat-omap/include/plat/opp.h b/arch/arm/plat-omap/include/plat/opp.h index 9af8c83..82cfdd6 100644 --- a/arch/arm/plat-omap/include/plat/opp.h +++ b/arch/arm/plat-omap/include/plat/opp.h @@ -75,7 +75,7 @@ struct omap_opp *opp_find_freq_floor(struct device *dev, unsigned long *freq); struct omap_opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq); -int opp_add(const struct omap_opp_def *opp_def); +int opp_add(struct device *dev, const struct omap_opp_def *opp_def); int opp_enable(struct omap_opp *opp); diff --git a/arch/arm/plat-omap/opp.c b/arch/arm/plat-omap/opp.c index b26326b..f5295ca 100644 --- a/arch/arm/plat-omap/opp.c +++ b/arch/arm/plat-omap/opp.c @@ -21,7 +21,6 @@ #include <linux/list.h> #include <plat/opp.h> -#include <plat/omap_device.h> /** * struct omap_opp - OMAP OPP description structure @@ -58,7 +57,6 @@ struct omap_opp { struct device_opp { struct list_head node; - struct omap_hwmod *oh; struct device *dev; struct list_head opp_list; @@ -291,29 +289,12 @@ static void omap_opp_populate(struct omap_opp *opp, * * This function adds an opp definition to the opp list and returns status. */ -int opp_add(const struct omap_opp_def *opp_def) +int opp_add(struct device *dev, const struct omap_opp_def *opp_def) { - struct omap_hwmod *oh; - struct device *dev = NULL; struct device_opp *tmp_dev_opp, *dev_opp = NULL; struct omap_opp *opp, *new_opp; - struct platform_device *pdev; struct list_head *head; - /* find the correct hwmod, and device */ - if (!opp_def->hwmod_name) { - pr_err("%s: missing name of omap_hwmod, ignoring.\n", __func__); - return -EINVAL; - } - oh = omap_hwmod_lookup(opp_def->hwmod_name); - if (!oh || !oh->od) { - pr_warn("%s: no hwmod or odev for %s, cannot add OPPs.\n", - __func__, opp_def->hwmod_name); - return -EINVAL; - } - pdev = &oh->od->pdev; - dev = &oh->od->pdev.dev; - /* Check for existing list for 'dev' */ list_for_each_entry(tmp_dev_opp, &dev_opp_list, node) { if (dev == tmp_dev_opp->dev) { @@ -331,8 +312,7 @@ int opp_add(const struct omap_opp_def *opp_def) return -ENOMEM; } - dev_opp->oh = oh; - dev_opp->dev = &oh->od->pdev.dev; + dev_opp->dev = dev; INIT_LIST_HEAD(&dev_opp->opp_list); list_add(&dev_opp->node, &dev_opp_list); -- 1.7.2.1 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 15:08 ` Kevin Hilman @ 2010-09-16 15:31 ` Nishanth Menon 2010-09-16 15:48 ` Kevin Hilman 0 siblings, 1 reply; 21+ messages in thread From: Nishanth Menon @ 2010-09-16 15:31 UTC (permalink / raw) To: Kevin Hilman Cc: Linus Walleij, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Kevin Hilman had written, on 09/16/2010 10:08 AM, the following: [..] >> more than that you name >> some functions omap_*, and how hard would it be to put it under >> arch/arm/common/*.c >> arch/arm/include/asm/*.h >> >> Possible even higher up in the directory hiearchy in include/linux/opp.h >> for the header and drivers/opp/*.c, because I think SuperH and power >> are not that different in this respect. > > Yeah, I guess this isn't ARM specific either, so should be at a higher > level. > > Nishanth, can take my hack below and continue this evolution? As I > demonstrate with this hack, this won't really change anything for us. thanks.. The only contention ahead is: where do we want this? Is drivers/opp/opp_core.c the right place? Given that this is just a support library and not really a driver? for some reason lib/opp.c does'nt sound just right either :( [..] -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 15:31 ` Nishanth Menon @ 2010-09-16 15:48 ` Kevin Hilman 2010-09-16 17:07 ` Linus Walleij 0 siblings, 1 reply; 21+ messages in thread From: Kevin Hilman @ 2010-09-16 15:48 UTC (permalink / raw) To: Nishanth Menon Cc: Linus Walleij, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Nishanth Menon <nm@ti.com> writes: > Kevin Hilman had written, on 09/16/2010 10:08 AM, the following: > [..] >>> more than that you name >>> some functions omap_*, and how hard would it be to put it under >>> arch/arm/common/*.c >>> arch/arm/include/asm/*.h >>> >>> Possible even higher up in the directory hiearchy in include/linux/opp.h >>> for the header and drivers/opp/*.c, because I think SuperH and power >>> are not that different in this respect. >> >> Yeah, I guess this isn't ARM specific either, so should be at a higher >> level. >> >> Nishanth, can take my hack below and continue this evolution? As I >> demonstrate with this hack, this won't really change anything for us. > > thanks.. The only contention ahead is: where do we want this? > Is drivers/opp/opp_core.c the right place? Given that this is just a > support library and not really a driver? for some reason lib/opp.c > does'nt sound just right either :( Well, since it's not really a driver, I don't think drivers/* is appropriate. lib/opp/* seems more appropriate to me with the header at include/linux/opp.h as Linus suggested. Kevin ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 15:48 ` Kevin Hilman @ 2010-09-16 17:07 ` Linus Walleij 2010-09-16 17:10 ` Nishanth Menon 0 siblings, 1 reply; 21+ messages in thread From: Linus Walleij @ 2010-09-16 17:07 UTC (permalink / raw) To: Kevin Hilman Cc: Nishanth Menon, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org 2010/9/16 Kevin Hilman <khilman@deeprootsystems.com>: > lib/opp/* seems more appropriate to me with the header at > include/linux/opp.h as Linus suggested. I second this. I would love to see the generic OPP stuff in lib/opp/* so we put it in the right place from the beginning and don't have to painfully refactor everything later (clk.h especially comes to mind.) Yours, Linus Walleij ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 17:07 ` Linus Walleij @ 2010-09-16 17:10 ` Nishanth Menon 2010-09-16 17:13 ` Shilimkar, Santosh 0 siblings, 1 reply; 21+ messages in thread From: Nishanth Menon @ 2010-09-16 17:10 UTC (permalink / raw) To: Linus Walleij Cc: Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Linus Walleij had written, on 09/16/2010 12:07 PM, the following: > 2010/9/16 Kevin Hilman <khilman@deeprootsystems.com>: > >> lib/opp/* seems more appropriate to me with the header at >> include/linux/opp.h as Linus suggested. > > I second this. I would love to see the generic OPP stuff in > lib/opp/* so we put it in the right place from the beginning and > don't have to painfully refactor everything later (clk.h > especially comes to mind.) cool.. digging a bit deeper into lib directory, I propose the following: lib/opp.c include/linux/opp.h any soc specific data (for registration etc) -> goes to arch/<arch>/mach-<soc>/oppdata_xyz.c or what ever they choose the intent being lib/ is no place to put mach or arch specific data definitions.. it is just noise.. if folks are ok with this, will post a new rev soonish.. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 17:10 ` Nishanth Menon @ 2010-09-16 17:13 ` Shilimkar, Santosh 2010-09-16 18:01 ` Kevin Hilman 0 siblings, 1 reply; 21+ messages in thread From: Shilimkar, Santosh @ 2010-09-16 17:13 UTC (permalink / raw) To: Menon, Nishanth, Linus Walleij Cc: Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org > -----Original Message----- > From: linux-omap-owner@vger.kernel.org [mailto:linux-omap- > owner@vger.kernel.org] On Behalf Of Menon, Nishanth > Sent: Thursday, September 16, 2010 10:40 PM > To: Linus Walleij > Cc: Kevin Hilman; linux-omap@vger.kernel.org; linux-arm- > kernel@lists.infradead.org > Subject: Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific > OPPs > > Linus Walleij had written, on 09/16/2010 12:07 PM, the following: > > 2010/9/16 Kevin Hilman <khilman@deeprootsystems.com>: > > > >> lib/opp/* seems more appropriate to me with the header at > >> include/linux/opp.h as Linus suggested. > > > > I second this. I would love to see the generic OPP stuff in > > lib/opp/* so we put it in the right place from the beginning and > > don't have to painfully refactor everything later (clk.h > > especially comes to mind.) > cool.. digging a bit deeper into lib directory, I propose the following: > lib/opp.c > include/linux/opp.h > > any soc specific data (for registration etc) -> goes to > arch/<arch>/mach-<soc>/oppdata_xyz.c or what ever they choose > > the intent being lib/ is no place to put mach or arch specific data > definitions.. it is just noise.. > > if folks are ok with this, will post a new rev soonish.. > If you like may be you can take this thread over lkml just to get more wider perspective ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 17:13 ` Shilimkar, Santosh @ 2010-09-16 18:01 ` Kevin Hilman 0 siblings, 0 replies; 21+ messages in thread From: Kevin Hilman @ 2010-09-16 18:01 UTC (permalink / raw) To: Shilimkar, Santosh Cc: Menon, Nishanth, Linus Walleij, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org "Shilimkar, Santosh" <santosh.shilimkar@ti.com> writes: >> -----Original Message----- >> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap- >> owner@vger.kernel.org] On Behalf Of Menon, Nishanth >> Sent: Thursday, September 16, 2010 10:40 PM >> To: Linus Walleij >> Cc: Kevin Hilman; linux-omap@vger.kernel.org; linux-arm- >> kernel@lists.infradead.org >> Subject: Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific >> OPPs >> >> Linus Walleij had written, on 09/16/2010 12:07 PM, the following: >> > 2010/9/16 Kevin Hilman <khilman@deeprootsystems.com>: >> > >> >> lib/opp/* seems more appropriate to me with the header at >> >> include/linux/opp.h as Linus suggested. >> > >> > I second this. I would love to see the generic OPP stuff in >> > lib/opp/* so we put it in the right place from the beginning and >> > don't have to painfully refactor everything later (clk.h >> > especially comes to mind.) >> cool.. digging a bit deeper into lib directory, I propose the following: >> lib/opp.c >> include/linux/opp.h >> >> any soc specific data (for registration etc) -> goes to >> arch/<arch>/mach-<soc>/oppdata_xyz.c or what ever they choose >> >> the intent being lib/ is no place to put mach or arch specific data >> definitions.. it is just noise.. >> >> if folks are ok with this, will post a new rev soonish.. >> > If you like may be you can take this thread over lkml just to get more > wider perspective I propose Nishanth does the slight rework to move this to lib/ and then post to l-o, l-a-k and lkml for RFC. Note that only the generic parts should be posted first, the OMAP specific usage of this can be fixed after the generic parts are accepted. Kevin ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-15 21:56 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman 2010-09-16 10:25 ` Gopinath, Thara 2010-09-16 12:19 ` Linus Walleij @ 2010-09-16 13:54 ` Roger Quadros 2010-09-16 14:01 ` Nishanth Menon 2 siblings, 1 reply; 21+ messages in thread From: Roger Quadros @ 2010-09-16 13:54 UTC (permalink / raw) To: nm Cc: ext Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Nishant, Since you are anyways re-sending this you might as well fix these nits. On 09/16/2010 12:56 AM, ext Kevin Hilman wrote: > From: Nishanth Menon<nm@ti.com> > > diff --git a/arch/arm/plat-omap/opp.c b/arch/arm/plat-omap/opp.c > new file mode 100644 > index 0000000..17f93b2 > --- /dev/null > +++ b/arch/arm/plat-omap/opp.c > @@ -0,0 +1,461 @@ > +/* > + * OMAP OPP Interface > + * > + * Copyright (C) 2009-2010 Texas Instruments Incorporated. > + * Nishanth Menon > + * Romit Dasgupta<romit@ti.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +/** > + * opp_get_voltage() - Gets the voltage corresponding to an opp > + * @opp: opp for which voltage has to be returned for > + * > + * Return voltage in micro volt corresponding to the opp, else > + * return 0 > + */ > +unsigned long opp_get_voltage(const struct omap_opp *opp) > +{ > + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { If !opp->enabled, then is it really an invalid parameter being passed? I'm not user if people need to know the voltage of a disabled OPP, but do we need to limit it? > + pr_err("%s: Invalid parameters being passed\n", __func__); > + return 0; > + } > + > + return opp->u_volt; > +} > + > +/** > + * opp_get_freq() - Gets the frequency corresponding to an opp > + * @opp: opp for which frequency has to be returned for > + * > + * Return frequency in hertz corresponding to the opp, else > + * return 0 > + */ > +unsigned long opp_get_freq(const struct omap_opp *opp) > +{ > + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { ditto. > + pr_err("%s: Invalid parameters being passed\n", __func__); > + return 0; > + } > + > + return opp->rate; > +} > + > +/** > + * opp_get_opp_count() - Get number of opps enabled in the opp list > + * @opp_type: OPP type we want to count > + * > + * This functions returns the number of opps if there are any OPPs enabled, > + * else returns corresponding error value. > + */ > +int opp_get_opp_count(struct device *dev) > +{ > + struct device_opp *dev_opp; > + > + dev_opp = find_device_opp(dev); > + if (IS_ERR(dev_opp)) > + return -ENODEV; > + > + return dev_opp->enabled_opp_count; > +} > + > +/** > + * opp_find_freq_exact() - search for an exact frequency > + * @opp_type: OPP type we want to search in. > + * @freq: frequency to search for > + * @enabled: enabled/disabled OPP to search for > + * > + * Searches for exact match in the opp list and returns handle to the matching > + * opp if found, else returns ERR_PTR in case of error and should be handled > + * using IS_ERR. > + * > + * Note enabled is a modifier for the search. if enabled=true, then the match is Good to add some punctuation after 'Note'. > + * for exact matching frequency and is enabled. if false, the match is for exact > + * frequency which is disabled. > + */ cheers, -roger ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 13:54 ` Roger Quadros @ 2010-09-16 14:01 ` Nishanth Menon 2010-09-16 14:20 ` Roger Quadros 0 siblings, 1 reply; 21+ messages in thread From: Nishanth Menon @ 2010-09-16 14:01 UTC (permalink / raw) To: Roger Quadros Cc: ext Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Roger Quadros had written, on 09/16/2010 08:54 AM, the following: > Since you are anyways re-sending this you might as well fix these nits. thanks for reviewing.. > [..] >> diff --git a/arch/arm/plat-omap/opp.c b/arch/arm/plat-omap/opp.c >> new file mode 100644 >> index 0000000..17f93b2 >> --- /dev/null >> +++ b/arch/arm/plat-omap/opp.c >> @@ -0,0 +1,461 @@ >> +/* >> + * OMAP OPP Interface >> + * >> + * Copyright (C) 2009-2010 Texas Instruments Incorporated. >> + * Nishanth Menon >> + * Romit Dasgupta<romit@ti.com> >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License version 2 as >> + * published by the Free Software Foundation. >> + */ >> + > >> +/** >> + * opp_get_voltage() - Gets the voltage corresponding to an opp >> + * @opp: opp for which voltage has to be returned for >> + * >> + * Return voltage in micro volt corresponding to the opp, else >> + * return 0 >> + */ >> +unsigned long opp_get_voltage(const struct omap_opp *opp) >> +{ >> + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { > > If !opp->enabled, then is it really an invalid parameter being passed? > > I'm not user if people need to know the voltage of a disabled OPP, but > do we need to limit it? > >> + pr_err("%s: Invalid parameters being passed\n", __func__); >> + return 0; >> + } >> + >> + return opp->u_volt; >> +} >> + >> +/** >> + * opp_get_freq() - Gets the frequency corresponding to an opp >> + * @opp: opp for which frequency has to be returned for >> + * >> + * Return frequency in hertz corresponding to the opp, else >> + * return 0 >> + */ >> +unsigned long opp_get_freq(const struct omap_opp *opp) >> +{ >> + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { > > ditto. Yes, the intent here was for opp operational apis to function ONLY on the enabled opps - this helps to pull out any bugs in the users who might be unintentionally using bad params. do you have any usecase you can think of where we might want to use these on a disabled opp? > >> + pr_err("%s: Invalid parameters being passed\n", __func__); >> + return 0; >> + } >> + >> + return opp->rate; >> +} >> + >> +/** >> + * opp_get_opp_count() - Get number of opps enabled in the opp list >> + * @opp_type: OPP type we want to count >> + * >> + * This functions returns the number of opps if there are any OPPs enabled, >> + * else returns corresponding error value. >> + */ >> +int opp_get_opp_count(struct device *dev) >> +{ >> + struct device_opp *dev_opp; >> + >> + dev_opp = find_device_opp(dev); >> + if (IS_ERR(dev_opp)) >> + return -ENODEV; >> + >> + return dev_opp->enabled_opp_count; >> +} >> + >> +/** >> + * opp_find_freq_exact() - search for an exact frequency >> + * @opp_type: OPP type we want to search in. >> + * @freq: frequency to search for >> + * @enabled: enabled/disabled OPP to search for >> + * >> + * Searches for exact match in the opp list and returns handle to the matching >> + * opp if found, else returns ERR_PTR in case of error and should be handled >> + * using IS_ERR. >> + * >> + * Note enabled is a modifier for the search. if enabled=true, then the match is > > Good to add some punctuation after 'Note'. Thanks will do > >> + * for exact matching frequency and is enabled. if false, the match is for exact >> + * frequency which is disabled. >> + */ > > cheers, > -roger -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 14:01 ` Nishanth Menon @ 2010-09-16 14:20 ` Roger Quadros 2010-09-16 14:43 ` Nishanth Menon 0 siblings, 1 reply; 21+ messages in thread From: Roger Quadros @ 2010-09-16 14:20 UTC (permalink / raw) To: ext Nishanth Menon Cc: ext Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org On 09/16/2010 05:01 PM, ext Nishanth Menon wrote: > Roger Quadros had written, on 09/16/2010 08:54 AM, the following: >> Since you are anyways re-sending this you might as well fix these nits. > thanks for reviewing.. > >> > [..] > >>> diff --git a/arch/arm/plat-omap/opp.c b/arch/arm/plat-omap/opp.c >>> new file mode 100644 >>> index 0000000..17f93b2 >>> --- /dev/null >>> +++ b/arch/arm/plat-omap/opp.c >>> @@ -0,0 +1,461 @@ >>> +/* >>> + * OMAP OPP Interface >>> + * >>> + * Copyright (C) 2009-2010 Texas Instruments Incorporated. >>> + * Nishanth Menon >>> + * Romit Dasgupta<romit@ti.com> >>> + * >>> + * This program is free software; you can redistribute it and/or modify >>> + * it under the terms of the GNU General Public License version 2 as >>> + * published by the Free Software Foundation. >>> + */ >>> + >> >>> +/** >>> + * opp_get_voltage() - Gets the voltage corresponding to an opp >>> + * @opp: opp for which voltage has to be returned for >>> + * >>> + * Return voltage in micro volt corresponding to the opp, else >>> + * return 0 >>> + */ >>> +unsigned long opp_get_voltage(const struct omap_opp *opp) >>> +{ >>> + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { >> >> If !opp->enabled, then is it really an invalid parameter being passed? >> >> I'm not user if people need to know the voltage of a disabled OPP, but >> do we need to limit it? >> >>> + pr_err("%s: Invalid parameters being passed\n", __func__); >>> + return 0; >>> + } >>> + >>> + return opp->u_volt; >>> +} >>> + >>> +/** >>> + * opp_get_freq() - Gets the frequency corresponding to an opp >>> + * @opp: opp for which frequency has to be returned for >>> + * >>> + * Return frequency in hertz corresponding to the opp, else >>> + * return 0 >>> + */ >>> +unsigned long opp_get_freq(const struct omap_opp *opp) >>> +{ >>> + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { >> >> ditto. > > Yes, the intent here was for opp operational apis to function ONLY on > the enabled opps - this helps to pull out any bugs in the users > who might be unintentionally using bad params. > OK. > do you have any usecase you can think of where we might want to use > these on a disabled opp? Not really. Based on what is an OPP enabled/disabled? Is it possible for an initially enabled OPP to be disabled at some point in time? What triggers this disable? OR does an OPP enabled at boot time remain enabled throughout the power session? regards, -roger ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs 2010-09-16 14:20 ` Roger Quadros @ 2010-09-16 14:43 ` Nishanth Menon 0 siblings, 0 replies; 21+ messages in thread From: Nishanth Menon @ 2010-09-16 14:43 UTC (permalink / raw) To: Roger Quadros Cc: ext Kevin Hilman, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org Roger Quadros had written, on 09/16/2010 09:20 AM, the following: [...] >>>> +/** >>>> + * opp_get_freq() - Gets the frequency corresponding to an opp >>>> + * @opp: opp for which frequency has to be returned for >>>> + * >>>> + * Return frequency in hertz corresponding to the opp, else >>>> + * return 0 >>>> + */ >>>> +unsigned long opp_get_freq(const struct omap_opp *opp) >>>> +{ >>>> + if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) { >>> ditto. >> Yes, the intent here was for opp operational apis to function ONLY on >> the enabled opps - this helps to pull out any bugs in the users >> who might be unintentionally using bad params. >> > OK. > >> do you have any usecase you can think of where we might want to use >> these on a disabled opp? > > Not really. Based on what is an OPP enabled/disabled? Is it possible for > an initially enabled OPP to be disabled at some point in time? What > triggers this disable? > OR does an OPP enabled at boot time remain enabled throughout the power > session? At this point - enable/disable is done at init time - there is flexibility for board files to define their own custom OPPs (as some custom boards need to). they dont change once this initial definition is done. there are plans to do dynamic enable disable based on previous discussion in l-o - the framework to use opp layer in that manner is yet to go up yet. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2010-09-16 18:01 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-08-11 0:07 [PATCH 0/4] OMAP OPP layer Kevin Hilman 2010-08-11 0:07 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman -- strict thread matches above, loose matches on Subject: below -- 2010-08-11 0:09 [PATCH 0/4] OMAP OPP layer Kevin Hilman 2010-08-11 0:09 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman 2010-09-15 21:56 [PATCH 0/4] OMAP OPP layer for 2.6.37 Kevin Hilman 2010-09-15 21:56 ` [PATCH 1/4] OMAP: introduce OPP layer for device-specific OPPs Kevin Hilman 2010-09-16 10:25 ` Gopinath, Thara 2010-09-16 10:32 ` Menon, Nishanth 2010-09-16 10:33 ` Gopinath, Thara 2010-09-16 12:19 ` Linus Walleij 2010-09-16 12:40 ` Nishanth Menon 2010-09-16 13:24 ` Linus Walleij 2010-09-16 15:08 ` Kevin Hilman 2010-09-16 15:31 ` Nishanth Menon 2010-09-16 15:48 ` Kevin Hilman 2010-09-16 17:07 ` Linus Walleij 2010-09-16 17:10 ` Nishanth Menon 2010-09-16 17:13 ` Shilimkar, Santosh 2010-09-16 18:01 ` Kevin Hilman 2010-09-16 13:54 ` Roger Quadros 2010-09-16 14:01 ` Nishanth Menon 2010-09-16 14:20 ` Roger Quadros 2010-09-16 14:43 ` Nishanth Menon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).