Nishanth Menon <nm@xxxxxx> writes: > Modifies the initial patch From Sanjeev: > http://patchwork.kernel.org/patch/50998/ > Discussions and comments from: > 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. > > 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. 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 introduced for OMAP3 and intends to replace > current OMAP3 opp handling mechanism. > > Note: > fields of struct omap_opp is currently exposed due to the necessity > that SRF and SR logic directly indexes the structure array fields. > The goal however, is to make the direct usage of omap_opp deprecated > and move to using these accessor functions. The usage in SRF and SR > indexes based on opp_id and hence opp_id is marked deprecated to > generate build warnings at least. Further, this usage necessitates > need of terminator entries at the start and end of opp_* tables which > are dynamically allocated. > > The accessor function definitions were collaborated with Kevin, and > doing justice here, this implementation could not go with some of > the better suggestions from kevin due to constraint imposed by SRF > and SR. A better and more optimal implementation is definitely > possible once SRF and SR are cleanedup/replaced. > > NOTE: OPP is a concept that can be used in all OMAPs, it is hence > introduced under plat-omap > > Introduces warning: > arch/arm/plat-omap/opp.c: In function 'opp_add': > arch/arm/plat-omap/opp.c:191: warning: 'opp_id' is deprecated (declared at arch/arm/plat-omap/include/plat/opp.h:33) > arch/arm/plat-omap/opp.c:199: warning: 'opp_id' is deprecated (declared at arch/arm/plat-omap/include/plat/opp.h:33) > arch/arm/plat-omap/opp.c: In function 'opp_init_list': > arch/arm/plat-omap/opp.c:240: warning: 'opp_id' is deprecated (declared at arch/arm/plat-omap/include/plat/opp.h:33) > > Cc: Benoit Cousson <b-cousson@xxxxxx> > Cc: Eduardo Valentin <eduardo.valentin@xxxxxxxxx> > Cc: Madhusudhan Chikkature Rajashekar <madhu.cr@xxxxxx> > Cc: Paul Walmsley <paul@xxxxxxxxx> > Cc: Romit Dasgupta <romit@xxxxxx> > Cc: Santosh Shilimkar <santosh.shilimkar@xxxxxx> > Cc: Sergio Alberto Aguirre Rodriguez <saaguirre@xxxxxx> > Cc: Tero Kristo <Tero.Kristo@xxxxxxxxx> > Cc: Thara Gopinath <thara@xxxxxx> > Cc: Vishwanath Sripathy <vishwanath.bs@xxxxxx> > > Signed-off-by: Sanjeev Premi <premi@xxxxxx> > Signed-off-by: Kevin Hilman <khilman@xxxxxxxxxxxxxxxxxxx> > Signed-off-by: Nishanth Menon <nm@xxxxxx> > --- > Kevin, > I have put your Signed-off-by to ack your contribs. pending > formal confirmation ofcourse. ok > arch/arm/plat-omap/Makefile | 3 + > arch/arm/plat-omap/include/plat/opp.h | 230 ++++++++++++++++++++++++++++ > arch/arm/plat-omap/opp.c | 271 +++++++++++++++++++++++++++++++++ > 3 files changed, 504 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/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile > index 95f8413..e9cf601 100644 > --- a/arch/arm/plat-omap/Makefile > +++ b/arch/arm/plat-omap/Makefile > @@ -12,6 +12,9 @@ obj- := > # OCPI interconnect support for 1710, 1610 and 5912 > obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o > > +# OPP support in (OMAP3+ only at the moment) > +obj-$(CONFIG_ARCH_OMAP3) += opp.o > + > # 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..341c02b > --- /dev/null > +++ b/arch/arm/plat-omap/include/plat/opp.h > @@ -0,0 +1,230 @@ > +/* > + * OMAP OPP Interface > + * > + * Copyright (C) 2009 Texas Instruments Incorporated. > + * Nishanth Menon > + * 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 > + > +/** > + * struct omap_opp - OMAP OPP description structure > + * @enabled: true/false - marking this OPP as enabled/disabled > + * @rate: Frequency in hertz > + * @opp_id: (DEPRECATED) opp identifier > + * @vsel: Voltage in volt processor level(this usage is > + * DEPRECATED to use Voltage in microvolts in future) > + * uV = ((vsel * 12.5) + 600) * 1000 > + * > + * This structure stores the OPP information for a given domain. > + * Due to legacy reasons, this structure is currently exposed and > + * will soon be removed elsewhere and will only be used as a handle > + * from the OPP internal referencing mechanism > + */ > +struct omap_opp { > + bool enabled; > + unsigned long rate; > + u8 opp_id __deprecated; > + u16 vsel; How about we add 'u32 voltage' here and mark vsel as __deprecated. Then we no longer need both an 'struct omap_opp' and a 'struct omap_opp_def'. Or even better, with the uv <--> vsel conversion macros you added, couldn't we alrady define the OPPs in terms of voltage, and drop the vsel already? > +}; > + > +/** > + * 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); ack > +/** > + * 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); ack > +/** > + * opp_get_opp_count() - Get number of opps enabled in the opp list > + * @num: returns the number of opps > + * @oppl: opp list > + * > + * This functions returns the number of opps if there are any OPPs enabled, > + * else returns corresponding error value. > + */ > +int opp_get_opp_count(const struct omap_opp *oppl); ack > +/** > + * opp_find_freq_exact() - search for an exact frequency > + * @oppl: OPP list > + * @freq: frequency to search for > + * @enabled: enabled/disabled OPP to search for > + * > + * searches for the 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 true, the match is for exact > + * frequency which is disabled. > + */ > +struct omap_opp *opp_find_freq_exact(struct omap_opp *oppl, > + unsigned long freq, bool enabled); ack I think we could drop the _exact, and just call it opp_find_freq(), but I'm ok either way. > +#define OPP_SEARCH_HIGH (0 << 1) > +#define OPP_SEARCH_LOW (1 << 1) > +/** > + * opp_find_freq_approx() - Search for an rounded freq > + * @oppl: Starting list > + * @freq: Start frequency > + * @dir_flag: Search direction > + * OPP_SEARCH_HIGH - search for next highest freq > + * OPP_SEARCH_LOW - search for next lowest freq > + * > + * Search for the higher/lower *enabled* OPP from a starting freq > + * from a start opp list. > + * > + * Returns *opp and *freq is populated with the next match, > + * else returns NULL > + * opp if found, else returns ERR_PTR in case of error. > + * > + * Example usages: > + * * find match/next highest available frequency > + * freq = 350000; > + * opp = opp_find_freq_approx(oppl, &freq, OPP_SEARCH_HIGH))) > + * if (IS_ERR(opp)) > + * pr_err ("unable to find a higher frequency\n"); > + * else > + * pr_info("match freq = %ld\n", freq); > + * > + * * find match/next lowest available frequency > + * freq = 350000; > + * opp = opp_find_freq_approx(oppl, &freq, OPP_SEARCH_LOW))) > + * 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 * > + * opp = oppl; > + * freq = ULONG_MAX; > + * while (!IS_ERR(opp = opp_find_freq_approx(opp, &freq, > + * OPP_SEARCH_LOW))) { > + * pr_info("freq = %ld\n", freq); > + * freq--; * for next lower match * > + * } > + * > + * * print all supported frequencies in ascending order * > + * opp = oppl; > + * freq = 0; > + * while (!IS_ERR(opp = opp_find_freq_approx(opp, &freq, > + * OPP_SEARCH_HIGH))) { > + * pr_info("freq = %ld\n", freq); > + * freq++; * for next higher match * > + * } > + * > + * NOTE: if we set freq as ULONG_MAX and search low, we get the highest enabled > + * frequency > + */ > +struct omap_opp *opp_find_freq_approx(struct omap_opp *oppl, > + unsigned long *freq, u8 dir_flag); ack > +/** > + * struct omap_opp_def - OMAP OPP Definition > + * @enabled: True/false - is this OPP enabled/disabled by default > + * @freq: Frequency in hertz corresponding to this OPP > + * @u_volt: Nominal voltage in microvolts corresponding to this OPP > + * > + * 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 { > + bool enabled; > + unsigned long freq; > + u32 u_volt; > +}; See above comment on 'struct omap_opp'. I think these two should be combined. I think the initial intent of having them separated so that the internal struct of 'struct omap_opp' could eventually move to the C file was the original intent, but I think it aids readability to just have a single OPP struct. > +/* Initialization wrapper */ > +#define OMAP_OPP_DEF(_enabled, _freq, _uv) \ > +{ \ > + .enabled = _enabled, \ > + .freq = _freq, \ > + .u_volt = _uv, \ > +} nice > +/* Terminator for the initialization list */ > +#define OMAP_OPP_DEF_TERMINATOR OMAP_OPP_DEF(0, 0, 0) I'd just drop this and use OMAP_OPP_DEF(0, 0, 0) directly in the table. > +/** > + * opp_init_list() - Initialize an opp list from the opp definitions > + * @opp_defs: Initial opp definitions to create the list. > + * > + * This function creates a list of opp definitions and returns a handle. > + * This list can be used to further validation/search/modifications. New > + * opp entries can be added to this list by using opp_add(). > + * > + * In the case of error, ERR_PTR is returned to the caller and should be > + * appropriately handled with IS_ERR. > + */ > +struct omap_opp __init *opp_init_list(const struct omap_opp_def *opp_defs); My original suggestion was that opp_init_list() simply creates a new but empty list. Adding OPPs should be done using opp_add(). I guess I'm OK with having the 'bulk add' feature of init_list() but would rather see a single way to add OPPs. > +/** > + * opp_add() - Add an OPP table from a table definitions > + * @oppl: List to add the OPP to > + * @opp_def: omap_opp_def to describe the OPP which we want to add to list. > + * > + * This function adds an opp definition to the opp list and returns > + * a handle representing the new OPP list. This handle is then used for further > + * validation, search, modification operations on the OPP list. > + * > + * This function returns the pointer to the allocated list through oppl if > + * success, else corresponding ERR_PTR value. Caller should NOT free the oppl. > + * opps_defs can be freed after use. > + * > + * NOTE: caller should assume that on success, oppl is probably populated with > + * a new handle and the new handle should be used for further referencing > + */ > +struct omap_opp *opp_add(struct omap_opp *oppl, > + const struct omap_opp_def *opp_def); c.f. proposal to drop omap_opp_def. otherwise, ack. > +/** > + * 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); ack > +/** > + * opp_disable() - Disable a specific OPP > + * @opp: Pointer to opp > + *axs > + * 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); ack Kevin > +#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..c4dc07b > --- /dev/null > +++ b/arch/arm/plat-omap/opp.c > @@ -0,0 +1,271 @@ > +/* > + * OMAP OPP Interface > + * > + * Copyright (C) 2009 Texas Instruments Incorporated. > + * Nishanth Menon > + * > + * 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 <plat/opp.h> > + > +/* > + * DEPRECATED: Meant to detect end of opp array > + * This is meant to help co-exist with current SRF etc > + * TODO: REMOVE! > + */ > +#define OPP_TERM(opp) (!(opp)->rate && !(opp)->vsel && !(opp)->enabled) > + > +/* > + * DEPRECATED: Meant to convert vsel value to uVolt > + * This is meant to help co-exist with current SRF etc > + * TODO: REMOVE! > + */ > +static inline unsigned long vsel_to_uv(const u8 vsel) > +{ > + return (((vsel * 125) + 6000)) * 100; > +} > + > +/* > + * DEPRECATED: Meant to convert uVolt to vsel value > + * This is meant to help co-exist with current SRF etc > + * TODO: REMOVE! > + */ > +static inline unsigned char uv_to_vsel(unsigned long uV) > +{ > + return ((uV / 100) - 6000) / 125; > +} > + > +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 vsel_to_uv(opp->vsel); > +} > + > +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; > +} > + > +int opp_get_opp_count(const struct omap_opp *oppl) > +{ > + struct omap_opp *opp; > + u8 n = 0; > + > + if (unlikely(!oppl || IS_ERR(oppl))) { > + pr_err("%s: Invalid parameters being passed\n", __func__); > + return -EINVAL; > + } > + opp = (struct omap_opp *)oppl; > + opp++; /* skip initial terminator */ > + while (!OPP_TERM(opp)) { > + if (opp->enabled) > + n++; > + opp++; > + } > + return n; > +} > + > +struct omap_opp *opp_find_freq_exact(struct omap_opp *oppl, > + unsigned long freq, bool enabled) > +{ > + struct omap_opp *opp = (struct omap_opp *)oppl; > + > + if (unlikely(!oppl || IS_ERR(oppl))) { > + pr_err("%s: Invalid parameters being passed\n", __func__); > + return ERR_PTR(-EINVAL); > + } > + /* skip initial terminator */ > + if (OPP_TERM(opp)) > + opp++; > + while (!OPP_TERM(opp)) { > + if ((opp->rate == freq) && (opp->enabled == enabled)) > + break; > + opp++; > + } > + > + return OPP_TERM(opp) ? ERR_PTR(-ENOENT) : opp; > +} > + > +struct omap_opp *opp_find_freq_approx(struct omap_opp *oppl, > + unsigned long *freq, u8 dir_flag) > +{ > + struct omap_opp *opp = (struct omap_opp *)oppl; > + > + if (unlikely(!oppl || IS_ERR(oppl) || !freq || IS_ERR(freq))) { > + pr_err("%s: Invalid parameters being passed\n", __func__); > + return ERR_PTR(-EINVAL); > + } > + /* skip initial terminator */ > + if (OPP_TERM(opp)) { > + opp++; > + /* If searching init list for a high val, skip to very top */ > + if (dir_flag == OPP_SEARCH_LOW) > + while (!OPP_TERM(opp + 1)) > + opp++; > + } > + while (!OPP_TERM(opp)) { > + if (opp->enabled && > + (((dir_flag == OPP_SEARCH_HIGH) && (opp->rate >= *freq)) || > + ((dir_flag == OPP_SEARCH_LOW) && (opp->rate <= *freq)))) > + break; > + opp += (dir_flag == OPP_SEARCH_LOW) ? -1 : 1; > + } > + > + if (OPP_TERM(opp)) > + return ERR_PTR(-ENOENT); > + > + *freq = opp->rate; > + 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->vsel = uv_to_vsel(opp_def->u_volt); > + /* round off to higher voltage */ > + if (opp_def->u_volt > vsel_to_uv(opp->vsel)) > + opp->vsel++; > +} > + > +struct omap_opp *opp_add(struct omap_opp *oppl, > + const struct omap_opp_def *opp_def) > +{ > + struct omap_opp *opp, *oppt, *oppr; > + u8 n, i, ins; > + > + if (unlikely(!oppl || IS_ERR(oppl) || !opp_def || IS_ERR(opp_def))) { > + pr_err("%s: Invalid params being passed\n", __func__); > + return ERR_PTR(-EINVAL); > + } > + /* need a start terminator.. */ > + if (unlikely(!OPP_TERM(oppl))) { > + pr_err("%s: Expected a start terminator!!\n", __func__); > + return ERR_PTR(-EINVAL); > + } > + n = 0; > + opp = oppl; > + opp++; > + while (!OPP_TERM(opp)) { > + n++; > + opp++; > + } > + /* lets now reallocate memory */ > + oppr = kmalloc(sizeof(struct omap_opp) * (n + 3), GFP_KERNEL); > + if (!oppr) { > + pr_err("%s: No memory for new opp array\n", __func__); > + return ERR_PTR(-ENOMEM); > + } > + > + /* Simple insertion sort */ > + opp = oppl; > + oppt = oppr; > + ins = 0; > + i = 0; > + do { > + if (ins || opp->rate < opp_def->freq) { > + memcpy(oppt, opp, sizeof(struct omap_opp)); > + opp++; > + } else { > + omap_opp_populate(oppt, opp_def); > + ins++; > + } > + oppt->opp_id = i; > + oppt++; > + i++; > + } while (!OPP_TERM(opp)); > + > + /* If nothing got inserted, this belongs to the end */ > + if (!ins) { > + omap_opp_populate(oppt, opp_def); > + oppt->opp_id = i; > + oppt++; > + } > + /* Put the terminator back on */ > + memcpy(oppt, opp, sizeof(struct omap_opp)); > + > + /* Free the old list */ > + kfree(oppl); > + > + return oppr; > +} > + > +struct omap_opp __init *opp_init_list(const struct omap_opp_def *opp_defs) > +{ > + struct omap_opp_def *t = (struct omap_opp_def *)opp_defs; > + struct omap_opp *opp, *oppl; > + u8 n = 0, i = 1; > + > + if (unlikely(!opp_defs || IS_ERR(opp_defs))) { > + pr_err("%s: Invalid params being passed\n", __func__); > + return ERR_PTR(-EINVAL); > + } > + /* Grab a count */ > + while (t->enabled || t->freq || t->u_volt) { > + n++; > + t++; > + } > + > + oppl = kmalloc(sizeof(struct omap_opp) * (n + 2), GFP_KERNEL); > + if (!oppl) { > + pr_err("%s: No memory for opp array\n", __func__); > + return ERR_PTR(-ENOMEM); > + } > + opp = oppl; > + /* Setup start terminator - SRF depends on this for indexing :( */ > + opp->rate = 0; > + opp->enabled = 0; > + opp->vsel = 0; > + opp++; > + while (n) { > + omap_opp_populate(opp, opp_defs); > + opp->opp_id = i; > + n--; > + opp++; > + opp_defs++; > + i++; > + } > + /* Setup terminator - this is for our search algos */ > + opp->rate = 0; > + opp->enabled = 0; > + opp->vsel = 0; > + return oppl; > +} > + > +int opp_enable(struct omap_opp *opp) > +{ > + if (unlikely(!opp || IS_ERR(opp))) { > + pr_err("%s: Invalid parameters being passed\n", __func__); > + return -EINVAL; > + } > + opp->enabled = true; > + return 0; > +} > + > +int opp_disable(struct omap_opp *opp) > +{ > + if (unlikely(!opp || IS_ERR(opp))) { > + pr_err("%s: Invalid parameters being passed\n", __func__); > + return -EINVAL; > + } > + opp->enabled = false; > + return 0; > +} > -- > 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html