@@ -2,9 +2,9 @@
* OMAP OPP Interface
*
* Copyright (C) 2009 Texas Instruments Incorporated.
- * Nishanth Menon
- * Copyright (C) 2009 Deep Root Systems, LLC.
- * Kevin Hilman
+ * Romit Dasgupta <romit@ti.com>
+ * Derived from the original series of patches by Nishanth Menon &
+ * 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
@@ -13,223 +13,88 @@
#ifndef __ASM_ARM_OMAP_OPP_H
#define __ASM_ARM_OMAP_OPP_H
-extern struct omap_opp *mpu_opps;
-extern struct omap_opp *dsp_opps;
-extern struct omap_opp *l3_opps;
+#ifdef CONFIG_CPU_FREQ
+#include <linux/cpufreq.h>
+#endif
-struct omap_opp;
+#define OPP_ENABLED (1 << 0)
+#define OPP_DISABLED (1 << 1)
+#define OPP_H (1 << 2)
+#define OPP_L (1 << 3)
+#define OPP_EQ (1 << 4)
+#define OPP_ALL (OPP_ENABLED | OPP_DISABLED)
-/**
- * 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
+/*
+ * XXX: Pending documentation.
*/
-unsigned long opp_get_voltage(const struct omap_opp *opp);
-/**
- * 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);
+struct omap_opp {
+ struct list_head opp_node;
+ unsigned int enabled:1;
+ unsigned long rate;
+ unsigned long uvolt;
+};
-/**
- * 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(struct omap_opp *oppl);
+enum opp_t {
+ OPP_NONE = 0,
+ OPP_MPU,
+ OPP_L3,
+ OPP_DSP,
+};
-/**
- * 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);
+static inline long opp_to_volt(struct omap_opp *opp)
+{
+ return opp ? opp->uvolt : 0;
+}
-/* XXX This documentation needs fixing */
+static inline long opp_to_freq(struct omap_opp *opp)
+{
+ return opp ? opp->rate : 0;
+}
-/**
- * opp_find_freq_floor() - Search for an rounded freq
- * @oppl: Starting list
- * @freq: Start frequency
- *
- * Search for the 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 lowest available frequency
- * freq = 350000;
- * opp = opp_find_freq_floor(oppl, &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 *
- * opp = oppl;
- * freq = ULONG_MAX;
- * while (!IS_ERR(opp = opp_find_freq_floor(opp, &freq)) {
- * pr_info("freq = %ld\n", freq);
- * freq--; * for next lower match *
- * }
- *
- * NOTE: if we set freq as ULONG_MAX and search low, we get the
- * highest enabled frequency
- */
-struct omap_opp *opp_find_freq_floor(struct omap_opp *oppl,
- unsigned long *freq);
+static inline unsigned int opp_is_valid(struct omap_opp *opp)
+{
+ return opp ? opp->enabled : 0;
+}
-/* XXX This documentation needs fixing */
+extern struct omap_opp *find_opp_by_freq(enum opp_t, unsigned long,
+ unsigned long);
-/**
- * opp_find_freq_ceil() - Search for an rounded freq
- * @oppl: Starting list
- * @freq: Start frequency
- *
- * Search for the higher *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_ceil(oppl, &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 *
- * opp = oppl;
- * freq = 0;
- * while (!IS_ERR(opp = opp_find_freq_ceil(opp, &freq)) {
- * pr_info("freq = %ld\n", freq);
- * freq++; * for next higher match *
- * }
- */
-struct omap_opp *opp_find_freq_ceil(struct omap_opp *oppl, unsigned long *freq);
+extern unsigned int get_opp_count(enum opp_t, unsigned long);
+extern int opp_add(enum opp_t, struct omap_opp *, unsigned long);
+extern int opp_del(enum opp_t, struct omap_opp *);
-/**
- * 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;
- unsigned long u_volt;
-};
+extern int create_opp_list(enum opp_t, struct omap_opp *);
-/*
- * Initialization wrapper used to define an OPP
- * to point at the end of a terminator of a list of OPPs,
- * use OMAP_OPP_DEF(0, 0, 0)
- */
-#define OMAP_OPP_DEF(_enabled, _freq, _uv) \
-{ \
- .enabled = _enabled, \
- .freq = _freq, \
- .u_volt = _uv, \
+static inline void opp_enable(struct omap_opp *opp)
+{
+ opp ? opp->enabled = 1 : 0;
+ return;
}
-/**
- * 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);
-
-/**
- * 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);
+static inline void opp_disable(struct omap_opp *opp)
+{
+ opp ? opp->enabled = 0 : 0;
+ return;
+}
-/**
- * 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);
+extern void dump_list(enum opp_t);
-/**
- * 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
+/*
+ * Initialization wrapper used to define an OPP.
*/
-int opp_disable(struct omap_opp *opp);
-struct omap_opp * __deprecated opp_find_by_opp_id(struct omap_opp *opps,
- u8 opp_id);
-u8 __deprecated opp_get_opp_id(struct omap_opp *opp);
+#define OMAP_OPP_DEF(_enabled, _freq, _uv) \
+{ \
+ .enabled = _enabled, \
+ .rate = _freq, \
+ .uvolt = _uv, \
+}
-void opp_init_cpufreq_table(struct omap_opp *opps,
- struct cpufreq_frequency_table **table);
+#ifdef CONFIG_CPU_FREQ
+extern void opp_init_cpufreq_table(struct cpufreq_frequency_table **);
+#endif
#endif /* __ASM_ARM_OMAP_OPP_H */
@@ -2,7 +2,9 @@
* OMAP OPP Interface
*
* Copyright (C) 2009 Texas Instruments Incorporated.
- * Nishanth Menon
+ * Romit Dasgupta <romit@ti.com>
+ * Derived from the original series of patches by
+ * Nishanth Menon & 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
@@ -11,318 +13,362 @@
#include <linux/kernel.h>
#include <linux/errno.h>
-#include <linux/err.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
#include <linux/cpufreq.h>
#include <plat/opp_twl_tps.h>
#include <plat/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
- * @u_volt: minimum microvolts DC required for this OPP to function
- *
- * 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;
- unsigned long u_volt;
- u8 opp_id;
+/* For 3xxx we have MPU, DSP & L3 OPPs */
+#ifdef CONFIG_ARCH_OMAP3
+#define NUM_OPP_TYPES 3
+#else
+#error "define the OPP types for OMAP type"
+#endif
+
+struct opp_list {
+ struct list_head opp_head;
+ struct mutex opp_mtx;
+ enum opp_t id;
};
/*
- * DEPRECATED: Meant to detect end of opp array
- * This is meant to help co-exist with current SRF etc
- * TODO: REMOVE!
+ * Will hold OPPs for MPU, L3, DSP etc.
+ * This has been statically allocated as OPP types
+ * are known for eachy platform. However individual OPPs
+ * can be dynamically introduced or revoked.
*/
-#define OPP_TERM(opp) (!(opp)->rate && !(opp)->u_volt && !(opp)->enabled)
+static struct opp_list opp_types[NUM_OPP_TYPES];
-unsigned long opp_get_voltage(const struct omap_opp *opp)
+static struct opp_list *get_slot(void)
{
- if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return 0;
+ struct opp_list *arr = opp_types;
+ int i;
+
+ for (i = 0; i < NUM_OPP_TYPES; i++)
+ if (arr->id == OPP_NONE)
+ break;
+ else
+ arr++;
+
+ if (i == NUM_OPP_TYPES)
+ return NULL;
+ else {
+ INIT_LIST_HEAD(&arr->opp_head);
+ mutex_init(&arr->opp_mtx);
+ return arr;
}
- return opp->u_volt;
}
-unsigned long opp_get_freq(const struct omap_opp *opp)
+static void put_slot(struct opp_list *arr)
{
- if (unlikely(!opp || IS_ERR(opp)) || !opp->enabled) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return 0;
- }
- return opp->rate;
+ arr->id = OPP_NONE;
}
-/**
- * opp_find_by_opp_id - look up OPP by OPP ID (deprecated)
- * @opps: pointer to an array of struct omap_opp
- *
- * 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 omap_opp *opps,
- u8 opp_id)
+static struct opp_list *exists(enum opp_t id)
{
+ struct opp_list *arr = opp_types;
int i = 0;
- if (!opps || !opp_id)
+ if (!id)
return NULL;
- while (!OPP_TERM(&opps[i])) {
- if (opps[i].enabled && (opps[i].opp_id == opp_id))
- return &opps[i];
-
- i++;
+ for (i = 0; i < NUM_OPP_TYPES; i++) {
+ if (id == arr->id)
+ return arr;
+ arr++;
}
+ return NULL;
+}
+
+#define __OPP_NOLOCK (1 << 31)
+static struct omap_opp *_find_opp_by_freq_eq(struct list_head *head,
+ unsigned long freq)
+{
+ struct omap_opp *opp;
+
+ list_for_each_entry(opp, head, opp_node)
+ if (opp->rate == freq)
+ return opp;
return NULL;
}
-u8 __deprecated opp_get_opp_id(struct omap_opp *opp)
+static struct omap_opp *_find_first_enabled_h(struct list_head *head,
+ struct omap_opp *opp)
{
- return opp->opp_id;
+ while (!opp->enabled && opp->opp_node.prev != head)
+ opp = list_entry(opp->opp_node.prev, struct omap_opp,
+ opp_node);
+
+ if (opp->opp_node.prev == head && !opp->enabled)
+ return NULL;
+ else
+ return opp;
}
-int opp_get_opp_count(struct omap_opp *oppl)
+static struct omap_opp *_find_first_enabled_l(struct list_head *head,
+ struct omap_opp *opp)
{
- u8 n = 0;
+ while (!opp->enabled && opp->opp_node.next != head)
+ opp = list_entry(opp->opp_node.next, struct omap_opp,
+ opp_node);
- if (unlikely(!oppl || IS_ERR(oppl))) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return -EINVAL;
- }
- while (!OPP_TERM(oppl)) {
- if (oppl->enabled)
- n++;
- oppl++;
- }
- return n;
+ if (opp->opp_node.next == head && !opp->enabled)
+ return NULL;
+ else
+ return opp;
}
-struct omap_opp *opp_find_freq_exact(struct omap_opp *oppl,
- unsigned long freq, bool enabled)
+static struct omap_opp *_find_opp_by_freq_h(struct list_head *head,
+ unsigned long freq,
+ unsigned long flags)
{
- if (unlikely(!oppl || IS_ERR(oppl))) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return ERR_PTR(-EINVAL);
- }
+ struct omap_opp *opp = NULL;
- while (!OPP_TERM(oppl)) {
- if ((oppl->rate == freq) && (oppl->enabled == enabled))
+ list_for_each_entry(opp, head, opp_node)
+ if (opp->rate > freq) {
+ if (opp->opp_node.prev == head)
+ return NULL;
break;
- oppl++;
- }
+ }
+ opp = list_entry(opp->opp_node.prev, struct omap_opp, opp_node);
- return OPP_TERM(oppl) ? ERR_PTR(-ENOENT) : oppl;
+ if (flags & OPP_ENABLED)
+ opp = _find_first_enabled_h(head, opp);
+
+ return opp;
}
-struct omap_opp *opp_find_freq_ceil(struct omap_opp *oppl, unsigned long *freq)
+static struct omap_opp *_find_opp_by_freq_l(struct list_head *head,
+ unsigned long freq,
+ unsigned long flags)
{
- if (unlikely(!oppl || IS_ERR(oppl) || !freq || IS_ERR(freq))) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return ERR_PTR(-EINVAL);
- }
+ struct omap_opp *opp = NULL;
- while (!OPP_TERM(oppl)) {
- if (oppl->enabled && oppl->rate >= *freq)
+ list_for_each_entry_reverse(opp, head, opp_node)
+ if (opp->rate < freq) {
+ if (opp->opp_node.next == head)
+ return NULL;
break;
+ }
- oppl++;
- }
+ opp = list_entry(opp->opp_node.next, struct omap_opp, opp_node);
+
+ if (flags & OPP_ENABLED)
+ opp = _find_first_enabled_l(head, opp);
- if (OPP_TERM(oppl))
- return ERR_PTR(-ENOENT);
+ return opp;
+}
+
+struct omap_opp *_find_opp_by_freq(struct opp_list *oppl,
+ unsigned long freq,
+ unsigned long flags)
+{
+ struct omap_opp *opp = NULL;
- *freq = oppl->rate;
+ if (!(flags & __OPP_NOLOCK))
+ mutex_lock(&oppl->opp_mtx);
- return oppl;
+ if (list_empty(&oppl->opp_head))
+ goto unlock;
+
+ if (flags & OPP_EQ) {
+ opp = _find_opp_by_freq_eq(&oppl->opp_head, freq);
+ if ((flags & OPP_ENABLED) && !opp_is_valid(opp))
+ opp = NULL;
+ } else if (flags & OPP_L) {
+ opp = _find_opp_by_freq_l(&oppl->opp_head, freq, flags);
+ } else if (flags & OPP_H)
+ opp = _find_opp_by_freq_h(&oppl->opp_head, freq, flags);
+
+unlock:
+ if (!(flags & __OPP_NOLOCK))
+ mutex_unlock(&oppl->opp_mtx);
+
+ return opp;
+}
+
+struct omap_opp *find_opp_by_freq(enum opp_t id, unsigned long freq,
+ unsigned long flags)
+{
+ struct opp_list *oppl;
+ oppl = exists(id);
+ BUG_ON(!oppl);
+ return _find_opp_by_freq(oppl, freq, flags);
}
-struct omap_opp *opp_find_freq_floor(struct omap_opp *oppl, unsigned long *freq)
+static unsigned int _get_opp_count(struct opp_list *oppl, unsigned long flags)
{
- struct omap_opp *prev_opp = oppl;
+ struct omap_opp *opp;
+ unsigned int enabled = 0, total = 0;
+
+ mutex_lock(&oppl->opp_mtx);
- if (unlikely(!oppl || IS_ERR(oppl) || !freq || IS_ERR(freq))) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return ERR_PTR(-EINVAL);
+ list_for_each_entry(opp, &oppl->opp_head, opp_node) {
+ if (flags & OPP_ENABLED && opp->enabled)
+ enabled++;
+ total++;
}
- while (!OPP_TERM(oppl)) {
- if (oppl->enabled) {
- if (oppl->rate > *freq)
- break;
+ mutex_unlock(&oppl->opp_mtx);
- prev_opp = oppl;
- }
+ if (flags & OPP_ENABLED)
+ return enabled;
+ else
+ return total;
+}
- oppl++;
- }
+unsigned int get_opp_count(enum opp_t id, unsigned long flags)
+{
+ struct opp_list *oppl;
- if (prev_opp->rate > *freq)
- return ERR_PTR(-ENOENT);
+ oppl = exists(id);
+ BUG_ON(!oppl);
+ return _get_opp_count(oppl, flags);
+}
- *freq = prev_opp->rate;
+static int _opp_del(struct opp_list *oppl, struct omap_opp *opp)
+{
+ mutex_lock(&oppl->opp_mtx);
+ list_del(&opp->opp_node);
+ mutex_unlock(&oppl->opp_mtx);
+ kfree(opp);
- return oppl;
+ return 0;
}
-/* 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)
+int opp_del(enum opp_t id, struct omap_opp *opp)
{
- opp->rate = opp_def->freq;
- opp->enabled = opp_def->enabled;
- opp->u_volt = opp_def->u_volt;
+ struct opp_list *oppl;
+
+ oppl = exists(id);
+ BUG_ON(!oppl || !opp);
+
+ return _opp_del(oppl, opp);
}
-struct omap_opp *opp_add(struct omap_opp *oppl,
- const struct omap_opp_def *opp_def)
+static int _opp_add(struct opp_list *oppl, struct omap_opp *opp,
+ unsigned long flags)
{
- struct omap_opp *opp, *oppt, *oppr;
- u8 n, i, ins;
+ struct omap_opp *new_opp, *tmp;
- 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);
- }
+ new_opp = kzalloc(sizeof(struct omap_opp), flags);
+ if (!new_opp)
+ return -ENOMEM;
- n = 0;
- opp = oppl;
- while (!OPP_TERM(opp)) {
- n++;
- opp++;
- }
+ INIT_LIST_HEAD(&new_opp->opp_node);
+ new_opp->enabled = opp->enabled;
+ new_opp->rate = opp->rate;
+ new_opp->uvolt = opp->uvolt;
+
+ mutex_lock(&oppl->opp_mtx);
/*
- * Allocate enough entries to copy the original list, plus the new
- * OPP, plus the concluding terminator
+ * List is always sorted w.r.t. frequency. No duplicates.
*/
- oppr = kzalloc(sizeof(struct omap_opp) * (n + 2), 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 = 1;
- do {
- if (ins || opp->rate < opp_def->freq) {
- memcpy(oppt, opp, sizeof(struct omap_opp));
- opp++;
- } else {
- omap_opp_populate(oppt, opp_def);
- ins++;
+ tmp = _find_opp_by_freq(oppl, opp->rate, OPP_H | __OPP_NOLOCK);
+
+ if (tmp) {
+ if (tmp->rate == opp->rate) {
+ kfree(opp);
+ mutex_unlock(&oppl->opp_mtx);
+ return -EEXIST;
}
- oppt->opp_id = i;
- oppt++;
- i++;
- } while (!OPP_TERM(opp));
+ __list_add(&new_opp->opp_node, &tmp->opp_node,
+ tmp->opp_node.next);
+ } else
+ /*
+ * If this is the lowest frequency add it to the front of the
+ * list.
+ */
+ list_add(&new_opp->opp_node, &oppl->opp_head);
- /* If nothing got inserted, this belongs to the end */
- if (!ins) {
- omap_opp_populate(oppt, opp_def);
- oppt->opp_id = i;
- oppt++;
- }
+ mutex_unlock(&oppl->opp_mtx);
- /* Terminator implicitly added by kzalloc() */
+ return 0;
+}
- /* Free the old list */
- kfree(oppl);
+int opp_add(enum opp_t id, struct omap_opp *opp, unsigned long flags)
+{
+ struct opp_list *oppl;
- return oppr;
+ oppl = exists(id);
+ BUG_ON(!oppl || !opp);
+ return _opp_add(oppl, opp, flags);
}
-struct omap_opp __init *opp_init_list(const struct omap_opp_def *opp_defs)
+static void __init destroy_opp_list(struct opp_list *oppl)
{
- struct omap_opp_def *t = (struct omap_opp_def *)opp_defs;
- struct omap_opp *opp, *oppl;
- u8 n = 0, i = 1;
+ struct omap_opp *opp;
- 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++;
- }
+ mutex_lock(&oppl->opp_mtx);
+ list_for_each_entry(opp, &oppl->opp_head, opp_node);
+ kfree(opp);
+ mutex_unlock(&oppl->opp_mtx);
+ return;
+}
- /*
- * Allocate enough entries to copy the original list, plus the
- * concluding terminator
- */
- oppl = kzalloc(sizeof(struct omap_opp) * (n + 1), GFP_KERNEL);
- if (!oppl) {
- pr_err("%s: No memory for opp array\n", __func__);
- return ERR_PTR(-ENOMEM);
- }
+int __init create_opp_list(enum opp_t id, struct omap_opp *oppdefs)
+{
+ int ret = 0, used_slot = 0;
+ struct opp_list *oppl;
- opp = oppl;
- while (n) {
- omap_opp_populate(opp, opp_defs);
- opp->opp_id = i;
- n--;
- opp++;
- opp_defs++;
- i++;
- }
+ if (unlikely(!id))
+ return -EINVAL;
- /* Terminator implicitly added by kzalloc() */
+ if (exists(id))
+ return -EEXIST;
- return oppl;
-}
+ oppl = get_slot();
+ BUG_ON(!oppl);
+ oppl->id = id;
-int opp_enable(struct omap_opp *opp)
-{
- if (unlikely(!opp || IS_ERR(opp))) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return -EINVAL;
+ while (oppdefs->rate || oppdefs->uvolt) {
+ ret = _opp_add(oppl, oppdefs, GFP_KERNEL);
+ if (ret) {
+ destroy_opp_list(oppl);
+ put_slot(oppl);
+ return ret;
+ }
+ oppdefs++;
+ used_slot = 1;
}
- opp->enabled = true;
- return 0;
+
+ if (!used_slot)
+ put_slot(oppl);
+
+ return ret;
}
-int opp_disable(struct omap_opp *opp)
+void dump_list(enum opp_t id)
{
- if (unlikely(!opp || IS_ERR(opp))) {
- pr_err("%s: Invalid parameters being passed\n", __func__);
- return -EINVAL;
- }
- opp->enabled = false;
- return 0;
+ struct omap_opp *opp;
+ struct opp_list *oppl;
+
+ oppl = exists(id);
+ if (oppl)
+ list_for_each_entry(opp, &oppl->opp_head, opp_node)
+ printk(KERN_DEBUG"For OPP list %d, opp:%lu\n", id,
+ opp_to_freq(opp));
+ return;
}
/* XXX document */
-void opp_init_cpufreq_table(struct omap_opp *opps,
- struct cpufreq_frequency_table **table)
+void opp_init_cpufreq_table(struct cpufreq_frequency_table **table)
{
- int i = 0, j;
- int opp_num;
+ int i = 0, opp_num;
+ unsigned long freq = ULONG_MAX;
+ struct omap_opp *opp;
struct cpufreq_frequency_table *freq_table;
- if (!opps) {
- pr_warning("%s: failed to initialize frequency"
- "table\n", __func__);
- return;
- }
-
- opp_num = opp_get_opp_count(opps);
- if (opp_num < 0) {
+ opp_num = get_opp_count(OPP_MPU, OPP_ENABLED);
+ if (!opp_num) {
pr_err("%s: no opp table?\n", __func__);
return;
}
@@ -335,14 +381,17 @@ void opp_init_cpufreq_table(struct omap_opp *opps,
return;
}
- for (j = opp_num; j >= 0; j--) {
- struct omap_opp *opp = &opps[j];
+ opp = find_opp_by_freq(OPP_MPU, freq, OPP_H | OPP_ENABLED);
- if (opp->enabled) {
- freq_table[i].index = i;
- freq_table[i].frequency = opp->rate / 1000;
- i++;
- }
+ while (opp) {
+ freq_table[i].index = i;
+ freq_table[i].frequency = opp_to_freq(opp) / 1000;
+ i++;
+ /*
+ * search the next highest frequency.
+ */
+ freq = opp_to_freq(opp) - 1;
+ opp = find_opp_by_freq(OPP_MPU, freq, OPP_H | OPP_ENABLED);
}
freq_table[i].index = i;