Commit 3969406b1bc1d11ec362b61ee5c48d551e51fed3
Committed by
Afzal Mohammed
1 parent
0c7242c21a
Exists in
master
OMAP2+: DVFS: provide ability to know if dvfs is scaling for a domain
Provide mechanism to know if DVFS is scaling on a specific domain. This API will allow us to detect transition and take appropriate measures in idle path Acked-by: Todd Poynor <toddpoynor@google.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Nishanth Menon <nm@ti.com> [vaibhav.bedia@ti.com: Pull in for AM33xx] Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Showing 2 changed files with 47 additions and 0 deletions Inline Diff
arch/arm/mach-omap2/dvfs.c
1 | /* | 1 | /* |
2 | * OMAP3/OMAP4 DVFS Management Routines | 2 | * OMAP3/OMAP4 DVFS Management Routines |
3 | * | 3 | * |
4 | * Author: Vishwanath BS <vishwanath.bs@ti.com> | 4 | * Author: Vishwanath BS <vishwanath.bs@ti.com> |
5 | * | 5 | * |
6 | * Copyright (C) 2011 Texas Instruments, Inc. | 6 | * Copyright (C) 2011 Texas Instruments, Inc. |
7 | * Vishwanath BS <vishwanath.bs@ti.com> | 7 | * Vishwanath BS <vishwanath.bs@ti.com> |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/err.h> | 14 | #include <linux/err.h> |
15 | #include <linux/spinlock.h> | 15 | #include <linux/spinlock.h> |
16 | #include <linux/plist.h> | 16 | #include <linux/plist.h> |
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/opp.h> | 18 | #include <linux/opp.h> |
19 | #include <linux/clk.h> | 19 | #include <linux/clk.h> |
20 | #include <linux/debugfs.h> | 20 | #include <linux/debugfs.h> |
21 | #include <linux/seq_file.h> | 21 | #include <linux/seq_file.h> |
22 | #include <plat/common.h> | 22 | #include <plat/common.h> |
23 | #include <plat/omap_device.h> | 23 | #include <plat/omap_device.h> |
24 | #include <plat/omap_hwmod.h> | 24 | #include <plat/omap_hwmod.h> |
25 | #include <plat/clock.h> | 25 | #include <plat/clock.h> |
26 | #include <plat/dvfs.h> | 26 | #include <plat/dvfs.h> |
27 | #include "smartreflex.h" | 27 | #include "smartreflex.h" |
28 | #include "powerdomain.h" | 28 | #include "powerdomain.h" |
29 | 29 | ||
30 | /** | 30 | /** |
31 | * DOC: Introduction | 31 | * DOC: Introduction |
32 | * ================= | 32 | * ================= |
33 | * DVFS is a technique that uses the optimal operating frequency and voltage to | 33 | * DVFS is a technique that uses the optimal operating frequency and voltage to |
34 | * allow a task to be performed in the required amount of time. | 34 | * allow a task to be performed in the required amount of time. |
35 | * OMAP processors have voltage domains whose voltage can be scaled to | 35 | * OMAP processors have voltage domains whose voltage can be scaled to |
36 | * various levels depending on which the operating frequencies of certain | 36 | * various levels depending on which the operating frequencies of certain |
37 | * devices belonging to the domain will also need to be scaled. This voltage | 37 | * devices belonging to the domain will also need to be scaled. This voltage |
38 | * frequency tuple is known as Operating Performance Point (OPP). A device | 38 | * frequency tuple is known as Operating Performance Point (OPP). A device |
39 | * can have multiple OPP's. Also a voltage domain could be shared between | 39 | * can have multiple OPP's. Also a voltage domain could be shared between |
40 | * multiple devices. Also there could be dependencies between various | 40 | * multiple devices. Also there could be dependencies between various |
41 | * voltage domains for maintaining system performance like VDD<X> | 41 | * voltage domains for maintaining system performance like VDD<X> |
42 | * should be at voltage v1 when VDD<Y> is at voltage v2. | 42 | * should be at voltage v1 when VDD<Y> is at voltage v2. |
43 | * | 43 | * |
44 | * The design of this framework takes into account all the above mentioned | 44 | * The design of this framework takes into account all the above mentioned |
45 | * points. To summarize the basic design of DVFS framework:- | 45 | * points. To summarize the basic design of DVFS framework:- |
46 | * | 46 | * |
47 | * 1. Have device opp tables for each device whose operating frequency can be | 47 | * 1. Have device opp tables for each device whose operating frequency can be |
48 | * scaled. This is easy now due to the existance of hwmod layer which | 48 | * scaled. This is easy now due to the existance of hwmod layer which |
49 | * allow storing of device specific info. The device opp tables contain | 49 | * allow storing of device specific info. The device opp tables contain |
50 | * the opp pairs (frequency voltage tuples), the voltage domain pointer | 50 | * the opp pairs (frequency voltage tuples), the voltage domain pointer |
51 | * to which the device belongs to, the device specific set_rate and | 51 | * to which the device belongs to, the device specific set_rate and |
52 | * get_rate API's which will do the actual scaling of the device frequency | 52 | * get_rate API's which will do the actual scaling of the device frequency |
53 | * and retrieve the current device frequency. | 53 | * and retrieve the current device frequency. |
54 | * 2. Introduce use counting on a per VDD basis. This is to take care multiple | 54 | * 2. Introduce use counting on a per VDD basis. This is to take care multiple |
55 | * requests to scale a VDD. The VDD will be scaled to the maximum of the | 55 | * requests to scale a VDD. The VDD will be scaled to the maximum of the |
56 | * voltages requested. | 56 | * voltages requested. |
57 | * 3. Keep track of all scalable devices belonging to a particular voltage | 57 | * 3. Keep track of all scalable devices belonging to a particular voltage |
58 | * domain the voltage layer. | 58 | * domain the voltage layer. |
59 | * 4. Keep track of frequency requests for each of the device. This will enable | 59 | * 4. Keep track of frequency requests for each of the device. This will enable |
60 | * to scale individual devices to different frequency (even w/o scaling | 60 | * to scale individual devices to different frequency (even w/o scaling |
61 | * voltage aka frequency throttling) | 61 | * voltage aka frequency throttling) |
62 | * 5. Generic dvfs API that can be called by anybody to scale a device opp. | 62 | * 5. Generic dvfs API that can be called by anybody to scale a device opp. |
63 | * This API takes the device pointer and frequency to which the device | 63 | * This API takes the device pointer and frequency to which the device |
64 | * needs to be scaled to. This API then internally finds out the voltage | 64 | * needs to be scaled to. This API then internally finds out the voltage |
65 | * domain to which the device belongs to and the voltage to which the voltage | 65 | * domain to which the device belongs to and the voltage to which the voltage |
66 | * domain needs to be put to for the device to be scaled to the new frequency | 66 | * domain needs to be put to for the device to be scaled to the new frequency |
67 | * from he device opp table. Then this API will add requested frequency into | 67 | * from he device opp table. Then this API will add requested frequency into |
68 | * the corresponding target device frequency list and add voltage request to | 68 | * the corresponding target device frequency list and add voltage request to |
69 | * the corresponding vdd. Subsequently it calls voltage scale function which | 69 | * the corresponding vdd. Subsequently it calls voltage scale function which |
70 | * will find out the highest requested voltage for the given vdd and scales | 70 | * will find out the highest requested voltage for the given vdd and scales |
71 | * the voltage to the required one. It also runs through the list of all | 71 | * the voltage to the required one. It also runs through the list of all |
72 | * scalable devices belonging to this voltage domain and scale them to the | 72 | * scalable devices belonging to this voltage domain and scale them to the |
73 | * appropriate frequencies using the set_rate pointer in the device opp | 73 | * appropriate frequencies using the set_rate pointer in the device opp |
74 | * tables. | 74 | * tables. |
75 | * 6. Handle inter VDD dependecies. | 75 | * 6. Handle inter VDD dependecies. |
76 | * | 76 | * |
77 | * | 77 | * |
78 | * DOC: The Core DVFS data structure: | 78 | * DOC: The Core DVFS data structure: |
79 | * ================================== | 79 | * ================================== |
80 | * Structure Name Example Tree | 80 | * Structure Name Example Tree |
81 | * --------- | 81 | * --------- |
82 | * /|\ +-------------------+ +-------------------+ | 82 | * /|\ +-------------------+ +-------------------+ |
83 | * | |User2 (dev2, freq2)+---\ |User4 (dev4, freq4)+---\ | 83 | * | |User2 (dev2, freq2)+---\ |User4 (dev4, freq4)+---\ |
84 | * | +-------------------+ | +-------------------+ | | 84 | * | +-------------------+ | +-------------------+ | |
85 | * (struct omap_dev_user_list) | | | 85 | * (struct omap_dev_user_list) | | |
86 | * | +-------------------+ | +-------------------+ | | 86 | * | +-------------------+ | +-------------------+ | |
87 | * | |User1 (dev1, freq1)+---| |User3 (dev3, freq3)+---| | 87 | * | |User1 (dev1, freq1)+---| |User3 (dev3, freq3)+---| |
88 | * \|/ +-------------------+ | +-------------------+ | | 88 | * \|/ +-------------------+ | +-------------------+ | |
89 | * --------- | | | 89 | * --------- | | |
90 | * /|\ +------------+------+ +---------------+--+ | 90 | * /|\ +------------+------+ +---------------+--+ |
91 | * | | DEV1 (dev, | | DEV2 (dev) | | 91 | * | | DEV1 (dev, | | DEV2 (dev) | |
92 | * (struct omap_vdd_dev_list)|omap_dev_user_list)| |omap_dev_user_list| | 92 | * (struct omap_vdd_dev_list)|omap_dev_user_list)| |omap_dev_user_list| |
93 | * | +------------+------+ +--+---------------+ | 93 | * | +------------+------+ +--+---------------+ |
94 | * \|/ /|\ /-----+-------------+------> others.. | 94 | * \|/ /|\ /-----+-------------+------> others.. |
95 | * --------- Frequency | | 95 | * --------- Frequency | |
96 | * /|\ +--+------------------+ | 96 | * /|\ +--+------------------+ |
97 | * | | VDD_n | | 97 | * | | VDD_n | |
98 | * | | (omap_vdd_dev_list, | | 98 | * | | (omap_vdd_dev_list, | |
99 | * (struct omap_vdd_dvfs_info)** | omap_vdd_user_list) | | 99 | * (struct omap_vdd_dvfs_info)** | omap_vdd_user_list) | |
100 | * | +--+------------------+ | 100 | * | +--+------------------+ |
101 | * | | (ROOT NODE: omap_dvfs_info_list) | 101 | * | | (ROOT NODE: omap_dvfs_info_list) |
102 | * \|/ | | 102 | * \|/ | |
103 | * --------- Voltage \---+-------------+----------> others.. | 103 | * --------- Voltage \---+-------------+----------> others.. |
104 | * /|\ \|/ +-------+----+ +-----+--------+ | 104 | * /|\ \|/ +-------+----+ +-----+--------+ |
105 | * | | vdd_user2 | | vdd_user3 | | 105 | * | | vdd_user2 | | vdd_user3 | |
106 | * (struct omap_vdd_user_list) | (dev, volt)| | (dev, volt) | | 106 | * (struct omap_vdd_user_list) | (dev, volt)| | (dev, volt) | |
107 | * \|/ +------------+ +--------------+ | 107 | * \|/ +------------+ +--------------+ |
108 | * --------- | 108 | * --------- |
109 | * Key: ** -> Root of the tree. | 109 | * Key: ** -> Root of the tree. |
110 | * NOTE: we use the priority to store the voltage/frequency | 110 | * NOTE: we use the priority to store the voltage/frequency |
111 | * | 111 | * |
112 | * For voltage dependency description, see: struct dependency: | 112 | * For voltage dependency description, see: struct dependency: |
113 | * voltagedomain -> (description of the voltagedomain) | 113 | * voltagedomain -> (description of the voltagedomain) |
114 | * omap_vdd_info -> (vdd information) | 114 | * omap_vdd_info -> (vdd information) |
115 | * omap_vdd_dep_info[]-> (stores array of depedency info) | 115 | * omap_vdd_dep_info[]-> (stores array of depedency info) |
116 | * omap_vdd_dep_volt[] -> (stores array of maps) | 116 | * omap_vdd_dep_volt[] -> (stores array of maps) |
117 | * (main_volt -> dep_volt) (a singular map) | 117 | * (main_volt -> dep_volt) (a singular map) |
118 | */ | 118 | */ |
119 | 119 | ||
120 | /* Macros to give idea about scaling directions */ | 120 | /* Macros to give idea about scaling directions */ |
121 | #define DVFS_VOLT_SCALE_DOWN 0 | 121 | #define DVFS_VOLT_SCALE_DOWN 0 |
122 | #define DVFS_VOLT_SCALE_NONE 1 | 122 | #define DVFS_VOLT_SCALE_NONE 1 |
123 | #define DVFS_VOLT_SCALE_UP 2 | 123 | #define DVFS_VOLT_SCALE_UP 2 |
124 | 124 | ||
125 | /** | 125 | /** |
126 | * struct omap_dev_user_list - Structure maitain userlist per devide | 126 | * struct omap_dev_user_list - Structure maitain userlist per devide |
127 | * @dev: The device requesting for a particular frequency | 127 | * @dev: The device requesting for a particular frequency |
128 | * @node: The list head entry | 128 | * @node: The list head entry |
129 | * | 129 | * |
130 | * Using this structure, user list (requesting dev * and frequency) for | 130 | * Using this structure, user list (requesting dev * and frequency) for |
131 | * each device is maintained. This is how we can have different devices | 131 | * each device is maintained. This is how we can have different devices |
132 | * at different frequencies (to support frequency locking and throttling). | 132 | * at different frequencies (to support frequency locking and throttling). |
133 | * Even if one of the devices in a given vdd has locked it's frequency, | 133 | * Even if one of the devices in a given vdd has locked it's frequency, |
134 | * other's can still scale their frequency using this list. | 134 | * other's can still scale their frequency using this list. |
135 | * If no one has placed a frequency request for a device, then device is | 135 | * If no one has placed a frequency request for a device, then device is |
136 | * set to the frequency from it's opp table. | 136 | * set to the frequency from it's opp table. |
137 | */ | 137 | */ |
138 | struct omap_dev_user_list { | 138 | struct omap_dev_user_list { |
139 | struct device *dev; | 139 | struct device *dev; |
140 | struct plist_node node; | 140 | struct plist_node node; |
141 | }; | 141 | }; |
142 | 142 | ||
143 | /** | 143 | /** |
144 | * struct omap_vdd_dev_list - Device list per vdd | 144 | * struct omap_vdd_dev_list - Device list per vdd |
145 | * @dev: The device belonging to a particular vdd | 145 | * @dev: The device belonging to a particular vdd |
146 | * @node: The list head entry | 146 | * @node: The list head entry |
147 | * @freq_user_list: The list of users for vdd device | 147 | * @freq_user_list: The list of users for vdd device |
148 | * @clk: frequency control clock for this dev | 148 | * @clk: frequency control clock for this dev |
149 | * @user_lock: The lock for plist manipulation | 149 | * @user_lock: The lock for plist manipulation |
150 | */ | 150 | */ |
151 | struct omap_vdd_dev_list { | 151 | struct omap_vdd_dev_list { |
152 | struct device *dev; | 152 | struct device *dev; |
153 | struct list_head node; | 153 | struct list_head node; |
154 | struct plist_head freq_user_list; | 154 | struct plist_head freq_user_list; |
155 | struct clk *clk; | 155 | struct clk *clk; |
156 | spinlock_t user_lock; /* spinlock for plist */ | 156 | spinlock_t user_lock; /* spinlock for plist */ |
157 | }; | 157 | }; |
158 | 158 | ||
159 | /** | 159 | /** |
160 | * struct omap_vdd_user_list - The per vdd user list | 160 | * struct omap_vdd_user_list - The per vdd user list |
161 | * @dev: The device asking for the vdd to be set at a particular | 161 | * @dev: The device asking for the vdd to be set at a particular |
162 | * voltage | 162 | * voltage |
163 | * @node: The list head entry | 163 | * @node: The list head entry |
164 | */ | 164 | */ |
165 | struct omap_vdd_user_list { | 165 | struct omap_vdd_user_list { |
166 | struct device *dev; | 166 | struct device *dev; |
167 | struct plist_node node; | 167 | struct plist_node node; |
168 | }; | 168 | }; |
169 | 169 | ||
170 | /** | 170 | /** |
171 | * struct omap_vdd_dvfs_info - The per vdd dvfs info | 171 | * struct omap_vdd_dvfs_info - The per vdd dvfs info |
172 | * @node: list node for vdd_dvfs_info list | 172 | * @node: list node for vdd_dvfs_info list |
173 | * @user_lock: spinlock for plist operations | 173 | * @user_lock: spinlock for plist operations |
174 | * @vdd_user_list: The vdd user list | 174 | * @vdd_user_list: The vdd user list |
175 | * @voltdm: Voltage domains for which dvfs info stored | 175 | * @voltdm: Voltage domains for which dvfs info stored |
176 | * @dev_list: Device list maintained per domain | 176 | * @dev_list: Device list maintained per domain |
177 | * @is_scaling: flag to store information about scaling in progress or not | ||
178 | * this flag is already protected by the global mutex. | ||
177 | * | 179 | * |
178 | * This is a fundamental structure used to store all the required | 180 | * This is a fundamental structure used to store all the required |
179 | * DVFS related information for a vdd. | 181 | * DVFS related information for a vdd. |
180 | */ | 182 | */ |
181 | struct omap_vdd_dvfs_info { | 183 | struct omap_vdd_dvfs_info { |
182 | struct list_head node; | 184 | struct list_head node; |
183 | 185 | ||
184 | spinlock_t user_lock; /* spin lock */ | 186 | spinlock_t user_lock; /* spin lock */ |
185 | struct plist_head vdd_user_list; | 187 | struct plist_head vdd_user_list; |
186 | struct voltagedomain *voltdm; | 188 | struct voltagedomain *voltdm; |
187 | struct list_head dev_list; | 189 | struct list_head dev_list; |
190 | bool is_scaling; | ||
188 | }; | 191 | }; |
189 | 192 | ||
190 | static LIST_HEAD(omap_dvfs_info_list); | 193 | static LIST_HEAD(omap_dvfs_info_list); |
191 | static DEFINE_MUTEX(omap_dvfs_lock); | 194 | static DEFINE_MUTEX(omap_dvfs_lock); |
192 | 195 | ||
193 | /* Dvfs scale helper function */ | 196 | /* Dvfs scale helper function */ |
194 | static int _dvfs_scale(struct device *req_dev, struct device *target_dev, | 197 | static int _dvfs_scale(struct device *req_dev, struct device *target_dev, |
195 | struct omap_vdd_dvfs_info *tdvfs_info); | 198 | struct omap_vdd_dvfs_info *tdvfs_info); |
196 | 199 | ||
197 | /* Few search functions to traverse and find pointers of interest */ | 200 | /* Few search functions to traverse and find pointers of interest */ |
198 | 201 | ||
199 | /** | 202 | /** |
200 | * _dvfs_info_to_dev() - Locate the parent device associated to dvfs_info | 203 | * _dvfs_info_to_dev() - Locate the parent device associated to dvfs_info |
201 | * @dvfs_info: dvfs_info to search for | 204 | * @dvfs_info: dvfs_info to search for |
202 | * | 205 | * |
203 | * Returns NULL on failure. | 206 | * Returns NULL on failure. |
204 | */ | 207 | */ |
205 | static struct device *_dvfs_info_to_dev(struct omap_vdd_dvfs_info *dvfs_info) | 208 | static struct device *_dvfs_info_to_dev(struct omap_vdd_dvfs_info *dvfs_info) |
206 | { | 209 | { |
207 | struct omap_vdd_dev_list *tmp_dev; | 210 | struct omap_vdd_dev_list *tmp_dev; |
208 | if (IS_ERR_OR_NULL(dvfs_info)) | 211 | if (IS_ERR_OR_NULL(dvfs_info)) |
209 | return NULL; | 212 | return NULL; |
210 | if (list_empty(&dvfs_info->dev_list)) | 213 | if (list_empty(&dvfs_info->dev_list)) |
211 | return NULL; | 214 | return NULL; |
212 | tmp_dev = list_first_entry(&dvfs_info->dev_list, | 215 | tmp_dev = list_first_entry(&dvfs_info->dev_list, |
213 | struct omap_vdd_dev_list, node); | 216 | struct omap_vdd_dev_list, node); |
214 | return tmp_dev->dev; | 217 | return tmp_dev->dev; |
215 | } | 218 | } |
216 | 219 | ||
217 | /** | 220 | /** |
218 | * _dev_to_dvfs_info() - Locate the dvfs_info for a device | 221 | * _dev_to_dvfs_info() - Locate the dvfs_info for a device |
219 | * @dev: dev to search for | 222 | * @dev: dev to search for |
220 | * | 223 | * |
221 | * Returns NULL on failure. | 224 | * Returns NULL on failure. |
222 | */ | 225 | */ |
223 | static struct omap_vdd_dvfs_info *_dev_to_dvfs_info(struct device *dev) | 226 | static struct omap_vdd_dvfs_info *_dev_to_dvfs_info(struct device *dev) |
224 | { | 227 | { |
225 | struct omap_vdd_dvfs_info *dvfs_info; | 228 | struct omap_vdd_dvfs_info *dvfs_info; |
226 | struct omap_vdd_dev_list *temp_dev; | 229 | struct omap_vdd_dev_list *temp_dev; |
227 | 230 | ||
228 | if (IS_ERR_OR_NULL(dev)) | 231 | if (IS_ERR_OR_NULL(dev)) |
229 | return NULL; | 232 | return NULL; |
230 | 233 | ||
231 | list_for_each_entry(dvfs_info, &omap_dvfs_info_list, node) { | 234 | list_for_each_entry(dvfs_info, &omap_dvfs_info_list, node) { |
232 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { | 235 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { |
233 | if (temp_dev->dev == dev) | 236 | if (temp_dev->dev == dev) |
234 | return dvfs_info; | 237 | return dvfs_info; |
235 | } | 238 | } |
236 | } | 239 | } |
237 | 240 | ||
238 | return NULL; | 241 | return NULL; |
239 | } | 242 | } |
240 | 243 | ||
241 | /** | 244 | /** |
242 | * _voltdm_to_dvfs_info() - Locate a dvfs_info given a voltdm pointer | 245 | * _voltdm_to_dvfs_info() - Locate a dvfs_info given a voltdm pointer |
243 | * @voltdm: voltdm to search for | 246 | * @voltdm: voltdm to search for |
244 | * | 247 | * |
245 | * Returns NULL on failure. | 248 | * Returns NULL on failure. |
246 | */ | 249 | */ |
247 | static | 250 | static |
248 | struct omap_vdd_dvfs_info *_voltdm_to_dvfs_info(struct voltagedomain *voltdm) | 251 | struct omap_vdd_dvfs_info *_voltdm_to_dvfs_info(struct voltagedomain *voltdm) |
249 | { | 252 | { |
250 | struct omap_vdd_dvfs_info *dvfs_info; | 253 | struct omap_vdd_dvfs_info *dvfs_info; |
251 | 254 | ||
252 | if (IS_ERR_OR_NULL(voltdm)) | 255 | if (IS_ERR_OR_NULL(voltdm)) |
253 | return NULL; | 256 | return NULL; |
254 | 257 | ||
255 | list_for_each_entry(dvfs_info, &omap_dvfs_info_list, node) { | 258 | list_for_each_entry(dvfs_info, &omap_dvfs_info_list, node) { |
256 | if (dvfs_info->voltdm == voltdm) | 259 | if (dvfs_info->voltdm == voltdm) |
257 | return dvfs_info; | 260 | return dvfs_info; |
258 | } | 261 | } |
259 | 262 | ||
260 | return NULL; | 263 | return NULL; |
261 | } | 264 | } |
262 | 265 | ||
263 | /** | 266 | /** |
264 | * _volt_to_opp() - Find OPP corresponding to a given voltage | 267 | * _volt_to_opp() - Find OPP corresponding to a given voltage |
265 | * @dev: device pointer associated with the OPP list | 268 | * @dev: device pointer associated with the OPP list |
266 | * @volt: voltage to search for in uV | 269 | * @volt: voltage to search for in uV |
267 | * | 270 | * |
268 | * Searches for exact match in the OPP list and returns handle to the matching | 271 | * Searches for exact match in the OPP list and returns handle to the matching |
269 | * OPP if found, else returns ERR_PTR in case of error and should be handled | 272 | * OPP if found, else returns ERR_PTR in case of error and should be handled |
270 | * using IS_ERR. If there are multiple opps with same voltage, it will return | 273 | * using IS_ERR. If there are multiple opps with same voltage, it will return |
271 | * the first available entry. Return pointer should be checked against IS_ERR. | 274 | * the first available entry. Return pointer should be checked against IS_ERR. |
272 | * | 275 | * |
273 | * NOTE: since this uses OPP functions, use under rcu_lock. This function also | 276 | * NOTE: since this uses OPP functions, use under rcu_lock. This function also |
274 | * assumes that the cpufreq table and OPP table are in sync - any modifications | 277 | * assumes that the cpufreq table and OPP table are in sync - any modifications |
275 | * to either should be synchronized. | 278 | * to either should be synchronized. |
276 | */ | 279 | */ |
277 | static struct opp *_volt_to_opp(struct device *dev, unsigned long volt) | 280 | static struct opp *_volt_to_opp(struct device *dev, unsigned long volt) |
278 | { | 281 | { |
279 | struct opp *opp = ERR_PTR(-ENODEV); | 282 | struct opp *opp = ERR_PTR(-ENODEV); |
280 | unsigned long f = 0; | 283 | unsigned long f = 0; |
281 | 284 | ||
282 | do { | 285 | do { |
283 | opp = opp_find_freq_ceil(dev, &f); | 286 | opp = opp_find_freq_ceil(dev, &f); |
284 | if (IS_ERR(opp)) | 287 | if (IS_ERR(opp)) |
285 | break; | 288 | break; |
286 | if (opp_get_voltage(opp) >= volt) | 289 | if (opp_get_voltage(opp) >= volt) |
287 | break; | 290 | break; |
288 | f++; | 291 | f++; |
289 | } while (1); | 292 | } while (1); |
290 | 293 | ||
291 | return opp; | 294 | return opp; |
292 | } | 295 | } |
293 | 296 | ||
294 | /* rest of the helper functions */ | 297 | /* rest of the helper functions */ |
295 | /** | 298 | /** |
296 | * _add_vdd_user() - Add a voltage request | 299 | * _add_vdd_user() - Add a voltage request |
297 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd | 300 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd |
298 | * @dev: device making the request | 301 | * @dev: device making the request |
299 | * @volt: requested voltage in uV | 302 | * @volt: requested voltage in uV |
300 | * | 303 | * |
301 | * Adds the given device's voltage request into corresponding | 304 | * Adds the given device's voltage request into corresponding |
302 | * vdd's omap_vdd_dvfs_info user list (plist). This list is used | 305 | * vdd's omap_vdd_dvfs_info user list (plist). This list is used |
303 | * to find the maximum voltage request for a given vdd. | 306 | * to find the maximum voltage request for a given vdd. |
304 | * | 307 | * |
305 | * Returns 0 on success. | 308 | * Returns 0 on success. |
306 | */ | 309 | */ |
307 | static int _add_vdd_user(struct omap_vdd_dvfs_info *dvfs_info, | 310 | static int _add_vdd_user(struct omap_vdd_dvfs_info *dvfs_info, |
308 | struct device *dev, unsigned long volt) | 311 | struct device *dev, unsigned long volt) |
309 | { | 312 | { |
310 | struct omap_vdd_user_list *user = NULL, *temp_user; | 313 | struct omap_vdd_user_list *user = NULL, *temp_user; |
311 | 314 | ||
312 | if (!dvfs_info || IS_ERR(dvfs_info)) { | 315 | if (!dvfs_info || IS_ERR(dvfs_info)) { |
313 | dev_warn(dev, "%s: VDD specified does not exist!\n", __func__); | 316 | dev_warn(dev, "%s: VDD specified does not exist!\n", __func__); |
314 | return -EINVAL; | 317 | return -EINVAL; |
315 | } | 318 | } |
316 | 319 | ||
317 | spin_lock(&dvfs_info->user_lock); | 320 | spin_lock(&dvfs_info->user_lock); |
318 | plist_for_each_entry(temp_user, &dvfs_info->vdd_user_list, node) { | 321 | plist_for_each_entry(temp_user, &dvfs_info->vdd_user_list, node) { |
319 | if (temp_user->dev == dev) { | 322 | if (temp_user->dev == dev) { |
320 | user = temp_user; | 323 | user = temp_user; |
321 | break; | 324 | break; |
322 | } | 325 | } |
323 | } | 326 | } |
324 | 327 | ||
325 | if (!user) { | 328 | if (!user) { |
326 | user = kzalloc(sizeof(struct omap_vdd_user_list), GFP_ATOMIC); | 329 | user = kzalloc(sizeof(struct omap_vdd_user_list), GFP_ATOMIC); |
327 | if (!user) { | 330 | if (!user) { |
328 | dev_err(dev, | 331 | dev_err(dev, |
329 | "%s: Unable to creat a new user for vdd_%s\n", | 332 | "%s: Unable to creat a new user for vdd_%s\n", |
330 | __func__, dvfs_info->voltdm->name); | 333 | __func__, dvfs_info->voltdm->name); |
331 | spin_unlock(&dvfs_info->user_lock); | 334 | spin_unlock(&dvfs_info->user_lock); |
332 | return -ENOMEM; | 335 | return -ENOMEM; |
333 | } | 336 | } |
334 | user->dev = dev; | 337 | user->dev = dev; |
335 | } else { | 338 | } else { |
336 | plist_del(&user->node, &dvfs_info->vdd_user_list); | 339 | plist_del(&user->node, &dvfs_info->vdd_user_list); |
337 | } | 340 | } |
338 | 341 | ||
339 | plist_node_init(&user->node, volt); | 342 | plist_node_init(&user->node, volt); |
340 | plist_add(&user->node, &dvfs_info->vdd_user_list); | 343 | plist_add(&user->node, &dvfs_info->vdd_user_list); |
341 | 344 | ||
342 | spin_unlock(&dvfs_info->user_lock); | 345 | spin_unlock(&dvfs_info->user_lock); |
343 | return 0; | 346 | return 0; |
344 | } | 347 | } |
345 | 348 | ||
346 | /** | 349 | /** |
347 | * _remove_vdd_user() - Remove a voltage request | 350 | * _remove_vdd_user() - Remove a voltage request |
348 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd | 351 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd |
349 | * @dev: device making the request | 352 | * @dev: device making the request |
350 | * | 353 | * |
351 | * Removes the given device's voltage request from corresponding | 354 | * Removes the given device's voltage request from corresponding |
352 | * vdd's omap_vdd_dvfs_info user list (plist). | 355 | * vdd's omap_vdd_dvfs_info user list (plist). |
353 | * | 356 | * |
354 | * Returns 0 on success. | 357 | * Returns 0 on success. |
355 | */ | 358 | */ |
356 | static int _remove_vdd_user(struct omap_vdd_dvfs_info *dvfs_info, | 359 | static int _remove_vdd_user(struct omap_vdd_dvfs_info *dvfs_info, |
357 | struct device *dev) | 360 | struct device *dev) |
358 | { | 361 | { |
359 | struct omap_vdd_user_list *user = NULL, *temp_user; | 362 | struct omap_vdd_user_list *user = NULL, *temp_user; |
360 | int ret = 0; | 363 | int ret = 0; |
361 | 364 | ||
362 | if (!dvfs_info || IS_ERR(dvfs_info)) { | 365 | if (!dvfs_info || IS_ERR(dvfs_info)) { |
363 | dev_err(dev, "%s: VDD specified does not exist!\n", __func__); | 366 | dev_err(dev, "%s: VDD specified does not exist!\n", __func__); |
364 | return -EINVAL; | 367 | return -EINVAL; |
365 | } | 368 | } |
366 | 369 | ||
367 | spin_lock(&dvfs_info->user_lock); | 370 | spin_lock(&dvfs_info->user_lock); |
368 | plist_for_each_entry(temp_user, &dvfs_info->vdd_user_list, node) { | 371 | plist_for_each_entry(temp_user, &dvfs_info->vdd_user_list, node) { |
369 | if (temp_user->dev == dev) { | 372 | if (temp_user->dev == dev) { |
370 | user = temp_user; | 373 | user = temp_user; |
371 | break; | 374 | break; |
372 | } | 375 | } |
373 | } | 376 | } |
374 | 377 | ||
375 | if (user) | 378 | if (user) |
376 | plist_del(&user->node, &dvfs_info->vdd_user_list); | 379 | plist_del(&user->node, &dvfs_info->vdd_user_list); |
377 | else { | 380 | else { |
378 | dev_err(dev, "%s: Unable to find the user for vdd_%s\n", | 381 | dev_err(dev, "%s: Unable to find the user for vdd_%s\n", |
379 | __func__, dvfs_info->voltdm->name); | 382 | __func__, dvfs_info->voltdm->name); |
380 | ret = -ENOENT; | 383 | ret = -ENOENT; |
381 | } | 384 | } |
382 | 385 | ||
383 | spin_unlock(&dvfs_info->user_lock); | 386 | spin_unlock(&dvfs_info->user_lock); |
384 | kfree(user); | 387 | kfree(user); |
385 | 388 | ||
386 | return ret; | 389 | return ret; |
387 | } | 390 | } |
388 | 391 | ||
389 | /** | 392 | /** |
390 | * _add_freq_request() - Add a requested device frequency | 393 | * _add_freq_request() - Add a requested device frequency |
391 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd | 394 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd |
392 | * @req_dev: device making the request | 395 | * @req_dev: device making the request |
393 | * @target_dev: target device for which frequency request is being made | 396 | * @target_dev: target device for which frequency request is being made |
394 | * @freq: target device frequency | 397 | * @freq: target device frequency |
395 | * | 398 | * |
396 | * This adds a requested frequency into target device's frequency list. | 399 | * This adds a requested frequency into target device's frequency list. |
397 | * | 400 | * |
398 | * Returns 0 on success. | 401 | * Returns 0 on success. |
399 | */ | 402 | */ |
400 | static int _add_freq_request(struct omap_vdd_dvfs_info *dvfs_info, | 403 | static int _add_freq_request(struct omap_vdd_dvfs_info *dvfs_info, |
401 | struct device *req_dev, struct device *target_dev, unsigned long freq) | 404 | struct device *req_dev, struct device *target_dev, unsigned long freq) |
402 | { | 405 | { |
403 | struct omap_dev_user_list *dev_user = NULL, *tmp_user; | 406 | struct omap_dev_user_list *dev_user = NULL, *tmp_user; |
404 | struct omap_vdd_dev_list *temp_dev; | 407 | struct omap_vdd_dev_list *temp_dev; |
405 | 408 | ||
406 | if (!dvfs_info || IS_ERR(dvfs_info)) { | 409 | if (!dvfs_info || IS_ERR(dvfs_info)) { |
407 | dev_warn(target_dev, "%s: VDD specified does not exist!\n", | 410 | dev_warn(target_dev, "%s: VDD specified does not exist!\n", |
408 | __func__); | 411 | __func__); |
409 | return -EINVAL; | 412 | return -EINVAL; |
410 | } | 413 | } |
411 | 414 | ||
412 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { | 415 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { |
413 | if (temp_dev->dev == target_dev) | 416 | if (temp_dev->dev == target_dev) |
414 | break; | 417 | break; |
415 | } | 418 | } |
416 | 419 | ||
417 | if (temp_dev->dev != target_dev) { | 420 | if (temp_dev->dev != target_dev) { |
418 | dev_warn(target_dev, "%s: target_dev does not exist!\n", | 421 | dev_warn(target_dev, "%s: target_dev does not exist!\n", |
419 | __func__); | 422 | __func__); |
420 | return -EINVAL; | 423 | return -EINVAL; |
421 | } | 424 | } |
422 | 425 | ||
423 | spin_lock(&temp_dev->user_lock); | 426 | spin_lock(&temp_dev->user_lock); |
424 | plist_for_each_entry(tmp_user, &temp_dev->freq_user_list, node) { | 427 | plist_for_each_entry(tmp_user, &temp_dev->freq_user_list, node) { |
425 | if (tmp_user->dev == req_dev) { | 428 | if (tmp_user->dev == req_dev) { |
426 | dev_user = tmp_user; | 429 | dev_user = tmp_user; |
427 | break; | 430 | break; |
428 | } | 431 | } |
429 | } | 432 | } |
430 | 433 | ||
431 | if (!dev_user) { | 434 | if (!dev_user) { |
432 | dev_user = kzalloc(sizeof(struct omap_dev_user_list), | 435 | dev_user = kzalloc(sizeof(struct omap_dev_user_list), |
433 | GFP_ATOMIC); | 436 | GFP_ATOMIC); |
434 | if (!dev_user) { | 437 | if (!dev_user) { |
435 | dev_err(target_dev, | 438 | dev_err(target_dev, |
436 | "%s: Unable to creat a new user for vdd_%s\n", | 439 | "%s: Unable to creat a new user for vdd_%s\n", |
437 | __func__, dvfs_info->voltdm->name); | 440 | __func__, dvfs_info->voltdm->name); |
438 | spin_unlock(&temp_dev->user_lock); | 441 | spin_unlock(&temp_dev->user_lock); |
439 | return -ENOMEM; | 442 | return -ENOMEM; |
440 | } | 443 | } |
441 | dev_user->dev = req_dev; | 444 | dev_user->dev = req_dev; |
442 | } else { | 445 | } else { |
443 | plist_del(&dev_user->node, &temp_dev->freq_user_list); | 446 | plist_del(&dev_user->node, &temp_dev->freq_user_list); |
444 | } | 447 | } |
445 | 448 | ||
446 | plist_node_init(&dev_user->node, freq); | 449 | plist_node_init(&dev_user->node, freq); |
447 | plist_add(&dev_user->node, &temp_dev->freq_user_list); | 450 | plist_add(&dev_user->node, &temp_dev->freq_user_list); |
448 | spin_unlock(&temp_dev->user_lock); | 451 | spin_unlock(&temp_dev->user_lock); |
449 | return 0; | 452 | return 0; |
450 | } | 453 | } |
451 | 454 | ||
452 | /** | 455 | /** |
453 | * _remove_freq_request() - Remove the requested device frequency | 456 | * _remove_freq_request() - Remove the requested device frequency |
454 | * | 457 | * |
455 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd | 458 | * @dvfs_info: omap_vdd_dvfs_info pointer for the required vdd |
456 | * @req_dev: device removing the request | 459 | * @req_dev: device removing the request |
457 | * @target_dev: target device from which frequency request is being removed | 460 | * @target_dev: target device from which frequency request is being removed |
458 | * | 461 | * |
459 | * This removes a requested frequency from target device's frequency list. | 462 | * This removes a requested frequency from target device's frequency list. |
460 | * | 463 | * |
461 | * Returns 0 on success. | 464 | * Returns 0 on success. |
462 | */ | 465 | */ |
463 | static int _remove_freq_request(struct omap_vdd_dvfs_info *dvfs_info, | 466 | static int _remove_freq_request(struct omap_vdd_dvfs_info *dvfs_info, |
464 | struct device *req_dev, struct device *target_dev) | 467 | struct device *req_dev, struct device *target_dev) |
465 | { | 468 | { |
466 | struct omap_dev_user_list *dev_user = NULL, *tmp_user; | 469 | struct omap_dev_user_list *dev_user = NULL, *tmp_user; |
467 | int ret = 0; | 470 | int ret = 0; |
468 | struct omap_vdd_dev_list *temp_dev; | 471 | struct omap_vdd_dev_list *temp_dev; |
469 | 472 | ||
470 | if (!dvfs_info || IS_ERR(dvfs_info)) { | 473 | if (!dvfs_info || IS_ERR(dvfs_info)) { |
471 | dev_warn(target_dev, "%s: VDD specified does not exist!\n", | 474 | dev_warn(target_dev, "%s: VDD specified does not exist!\n", |
472 | __func__); | 475 | __func__); |
473 | return -EINVAL; | 476 | return -EINVAL; |
474 | } | 477 | } |
475 | 478 | ||
476 | 479 | ||
477 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { | 480 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { |
478 | if (temp_dev->dev == target_dev) | 481 | if (temp_dev->dev == target_dev) |
479 | break; | 482 | break; |
480 | } | 483 | } |
481 | 484 | ||
482 | if (temp_dev->dev != target_dev) { | 485 | if (temp_dev->dev != target_dev) { |
483 | dev_warn(target_dev, "%s: target_dev does not exist!\n", | 486 | dev_warn(target_dev, "%s: target_dev does not exist!\n", |
484 | __func__); | 487 | __func__); |
485 | return -EINVAL; | 488 | return -EINVAL; |
486 | } | 489 | } |
487 | 490 | ||
488 | spin_lock(&temp_dev->user_lock); | 491 | spin_lock(&temp_dev->user_lock); |
489 | plist_for_each_entry(tmp_user, &temp_dev->freq_user_list, node) { | 492 | plist_for_each_entry(tmp_user, &temp_dev->freq_user_list, node) { |
490 | if (tmp_user->dev == req_dev) { | 493 | if (tmp_user->dev == req_dev) { |
491 | dev_user = tmp_user; | 494 | dev_user = tmp_user; |
492 | break; | 495 | break; |
493 | } | 496 | } |
494 | } | 497 | } |
495 | 498 | ||
496 | if (dev_user) { | 499 | if (dev_user) { |
497 | plist_del(&dev_user->node, &temp_dev->freq_user_list); | 500 | plist_del(&dev_user->node, &temp_dev->freq_user_list); |
498 | } else { | 501 | } else { |
499 | dev_err(target_dev, | 502 | dev_err(target_dev, |
500 | "%s: Unable to remove the user for vdd_%s\n", | 503 | "%s: Unable to remove the user for vdd_%s\n", |
501 | __func__, dvfs_info->voltdm->name); | 504 | __func__, dvfs_info->voltdm->name); |
502 | ret = -EINVAL; | 505 | ret = -EINVAL; |
503 | } | 506 | } |
504 | 507 | ||
505 | spin_unlock(&temp_dev->user_lock); | 508 | spin_unlock(&temp_dev->user_lock); |
506 | kfree(dev_user); | 509 | kfree(dev_user); |
507 | 510 | ||
508 | return ret; | 511 | return ret; |
509 | } | 512 | } |
510 | 513 | ||
511 | /** | 514 | /** |
512 | * _dep_scan_table() - Scan a dependency table and mark for scaling | 515 | * _dep_scan_table() - Scan a dependency table and mark for scaling |
513 | * @dev: device requesting the dependency scan (req_dev) | 516 | * @dev: device requesting the dependency scan (req_dev) |
514 | * @dep_info: dependency information (contains the table) | 517 | * @dep_info: dependency information (contains the table) |
515 | * @main_volt: voltage dependency to search for | 518 | * @main_volt: voltage dependency to search for |
516 | * | 519 | * |
517 | * This runs down the table provided to find the match for main_volt | 520 | * This runs down the table provided to find the match for main_volt |
518 | * provided and sets up a scale request for the dependent domain | 521 | * provided and sets up a scale request for the dependent domain |
519 | * for the dependent voltage. | 522 | * for the dependent voltage. |
520 | * | 523 | * |
521 | * Returns 0 if all went well. | 524 | * Returns 0 if all went well. |
522 | */ | 525 | */ |
523 | static int _dep_scan_table(struct device *dev, | 526 | static int _dep_scan_table(struct device *dev, |
524 | struct omap_vdd_dep_info *dep_info, unsigned long main_volt) | 527 | struct omap_vdd_dep_info *dep_info, unsigned long main_volt) |
525 | { | 528 | { |
526 | struct omap_vdd_dep_volt *dep_table = dep_info->dep_table; | 529 | struct omap_vdd_dep_volt *dep_table = dep_info->dep_table; |
527 | int i; | 530 | int i; |
528 | unsigned long dep_volt = 0; | 531 | unsigned long dep_volt = 0; |
529 | 532 | ||
530 | if (!dep_table) { | 533 | if (!dep_table) { |
531 | dev_err(dev, "%s: deptable not present for vdd%s\n", | 534 | dev_err(dev, "%s: deptable not present for vdd%s\n", |
532 | __func__, dep_info->name); | 535 | __func__, dep_info->name); |
533 | return -EINVAL; | 536 | return -EINVAL; |
534 | } | 537 | } |
535 | 538 | ||
536 | /* Now scan through the the dep table for a match */ | 539 | /* Now scan through the the dep table for a match */ |
537 | for (i = 0; i < dep_info->nr_dep_entries; i++) { | 540 | for (i = 0; i < dep_info->nr_dep_entries; i++) { |
538 | if (dep_table[i].main_vdd_volt == main_volt) { | 541 | if (dep_table[i].main_vdd_volt == main_volt) { |
539 | dep_volt = dep_table[i].dep_vdd_volt; | 542 | dep_volt = dep_table[i].dep_vdd_volt; |
540 | break; | 543 | break; |
541 | } | 544 | } |
542 | } | 545 | } |
543 | if (!dep_volt) { | 546 | if (!dep_volt) { |
544 | dev_warn(dev, "%s: %ld volt map missing in vdd_%s\n", | 547 | dev_warn(dev, "%s: %ld volt map missing in vdd_%s\n", |
545 | __func__, main_volt, dep_info->name); | 548 | __func__, main_volt, dep_info->name); |
546 | return -EINVAL; | 549 | return -EINVAL; |
547 | } | 550 | } |
548 | 551 | ||
549 | /* populate voltdm if it is not present */ | 552 | /* populate voltdm if it is not present */ |
550 | if (!dep_info->_dep_voltdm) { | 553 | if (!dep_info->_dep_voltdm) { |
551 | dep_info->_dep_voltdm = voltdm_lookup(dep_info->name); | 554 | dep_info->_dep_voltdm = voltdm_lookup(dep_info->name); |
552 | if (!dep_info->_dep_voltdm) { | 555 | if (!dep_info->_dep_voltdm) { |
553 | dev_warn(dev, "%s: unable to get vdm%s\n", | 556 | dev_warn(dev, "%s: unable to get vdm%s\n", |
554 | __func__, dep_info->name); | 557 | __func__, dep_info->name); |
555 | return -ENODEV; | 558 | return -ENODEV; |
556 | } | 559 | } |
557 | } | 560 | } |
558 | 561 | ||
559 | /* See if dep_volt is possible for the vdd*/ | 562 | /* See if dep_volt is possible for the vdd*/ |
560 | i = _add_vdd_user(_voltdm_to_dvfs_info(dep_info->_dep_voltdm), | 563 | i = _add_vdd_user(_voltdm_to_dvfs_info(dep_info->_dep_voltdm), |
561 | dev, dep_volt); | 564 | dev, dep_volt); |
562 | if (i) | 565 | if (i) |
563 | dev_err(dev, "%s: Failed to add dep to domain %s volt=%ld\n", | 566 | dev_err(dev, "%s: Failed to add dep to domain %s volt=%ld\n", |
564 | __func__, dep_info->name, dep_volt); | 567 | __func__, dep_info->name, dep_volt); |
565 | return i; | 568 | return i; |
566 | } | 569 | } |
567 | 570 | ||
568 | /** | 571 | /** |
569 | * _dep_scan_domains() - Scan dependency domains for a device | 572 | * _dep_scan_domains() - Scan dependency domains for a device |
570 | * @dev: device requesting the scan | 573 | * @dev: device requesting the scan |
571 | * @vdd: vdd_info corresponding to the device | 574 | * @vdd: vdd_info corresponding to the device |
572 | * @main_volt: voltage to scan for | 575 | * @main_volt: voltage to scan for |
573 | * | 576 | * |
574 | * Since each domain *may* have multiple dependent domains, we scan | 577 | * Since each domain *may* have multiple dependent domains, we scan |
575 | * through each of the dependent domains and invoke _dep_scan_table to | 578 | * through each of the dependent domains and invoke _dep_scan_table to |
576 | * scan each table for dependent domain for dependency scaling. | 579 | * scan each table for dependent domain for dependency scaling. |
577 | * | 580 | * |
578 | * This assumes that the dependent domain information is NULL entry terminated. | 581 | * This assumes that the dependent domain information is NULL entry terminated. |
579 | * Returns 0 if all went well. | 582 | * Returns 0 if all went well. |
580 | */ | 583 | */ |
581 | static int _dep_scan_domains(struct device *dev, | 584 | static int _dep_scan_domains(struct device *dev, |
582 | struct omap_vdd_info *vdd, unsigned long main_volt) | 585 | struct omap_vdd_info *vdd, unsigned long main_volt) |
583 | { | 586 | { |
584 | struct omap_vdd_dep_info *dep_info = vdd->dep_vdd_info; | 587 | struct omap_vdd_dep_info *dep_info = vdd->dep_vdd_info; |
585 | int ret = 0, r; | 588 | int ret = 0, r; |
586 | 589 | ||
587 | if (!dep_info) { | 590 | if (!dep_info) { |
588 | dev_dbg(dev, "%s: No dependent VDD\n", __func__); | 591 | dev_dbg(dev, "%s: No dependent VDD\n", __func__); |
589 | return 0; | 592 | return 0; |
590 | } | 593 | } |
591 | 594 | ||
592 | /* First scan through the mydomain->dep_domain list */ | 595 | /* First scan through the mydomain->dep_domain list */ |
593 | while (dep_info->nr_dep_entries) { | 596 | while (dep_info->nr_dep_entries) { |
594 | r = _dep_scan_table(dev, dep_info, main_volt); | 597 | r = _dep_scan_table(dev, dep_info, main_volt); |
595 | /* Store last failed value */ | 598 | /* Store last failed value */ |
596 | ret = (r) ? r : ret; | 599 | ret = (r) ? r : ret; |
597 | dep_info++; | 600 | dep_info++; |
598 | } | 601 | } |
599 | 602 | ||
600 | return ret; | 603 | return ret; |
601 | } | 604 | } |
602 | 605 | ||
603 | /** | 606 | /** |
604 | * _dep_scale_domains() - Cause a scale of all dependent domains | 607 | * _dep_scale_domains() - Cause a scale of all dependent domains |
605 | * @req_dev: device requesting the scale | 608 | * @req_dev: device requesting the scale |
606 | * @req_vdd: vdd_info corresponding to the requesting device. | 609 | * @req_vdd: vdd_info corresponding to the requesting device. |
607 | * | 610 | * |
608 | * This walks through every dependent domain and triggers a scale | 611 | * This walks through every dependent domain and triggers a scale |
609 | * It is assumed that the corresponding scale handling for the | 612 | * It is assumed that the corresponding scale handling for the |
610 | * domain translates this to freq and voltage scale operations as | 613 | * domain translates this to freq and voltage scale operations as |
611 | * needed. | 614 | * needed. |
612 | * | 615 | * |
613 | * Note: This is uses _dvfs_scale and one should be careful not to | 616 | * Note: This is uses _dvfs_scale and one should be careful not to |
614 | * create a circular depedency (e.g. vdd_mpu->vdd_core->vdd->mpu) | 617 | * create a circular depedency (e.g. vdd_mpu->vdd_core->vdd->mpu) |
615 | * which can create deadlocks. No protection is provided to prevent | 618 | * which can create deadlocks. No protection is provided to prevent |
616 | * this condition and a tree organization is assumed. | 619 | * this condition and a tree organization is assumed. |
617 | * | 620 | * |
618 | * Returns 0 if all went fine. | 621 | * Returns 0 if all went fine. |
619 | */ | 622 | */ |
620 | static int _dep_scale_domains(struct device *req_dev, | 623 | static int _dep_scale_domains(struct device *req_dev, |
621 | struct omap_vdd_info *req_vdd) | 624 | struct omap_vdd_info *req_vdd) |
622 | { | 625 | { |
623 | struct omap_vdd_dep_info *dep_info = req_vdd->dep_vdd_info; | 626 | struct omap_vdd_dep_info *dep_info = req_vdd->dep_vdd_info; |
624 | int ret = 0, r; | 627 | int ret = 0, r; |
625 | 628 | ||
626 | if (!dep_info) { | 629 | if (!dep_info) { |
627 | dev_dbg(req_dev, "%s: No dependent VDD\n", __func__); | 630 | dev_dbg(req_dev, "%s: No dependent VDD\n", __func__); |
628 | return 0; | 631 | return 0; |
629 | } | 632 | } |
630 | 633 | ||
631 | /* First scan through the mydomain->dep_domain list */ | 634 | /* First scan through the mydomain->dep_domain list */ |
632 | while (dep_info->nr_dep_entries) { | 635 | while (dep_info->nr_dep_entries) { |
633 | struct voltagedomain *tvoltdm = dep_info->_dep_voltdm; | 636 | struct voltagedomain *tvoltdm = dep_info->_dep_voltdm; |
634 | 637 | ||
635 | r = 0; | 638 | r = 0; |
636 | /* Scale it only if I have a voltdm mapped up for the dep */ | 639 | /* Scale it only if I have a voltdm mapped up for the dep */ |
637 | if (tvoltdm) { | 640 | if (tvoltdm) { |
638 | struct omap_vdd_dvfs_info *tdvfs_info; | 641 | struct omap_vdd_dvfs_info *tdvfs_info; |
639 | struct device *target_dev; | 642 | struct device *target_dev; |
640 | tdvfs_info = _voltdm_to_dvfs_info(tvoltdm); | 643 | tdvfs_info = _voltdm_to_dvfs_info(tvoltdm); |
641 | if (!tdvfs_info) { | 644 | if (!tdvfs_info) { |
642 | dev_warn(req_dev, "%s: no dvfs_info\n", | 645 | dev_warn(req_dev, "%s: no dvfs_info\n", |
643 | __func__); | 646 | __func__); |
644 | goto next; | 647 | goto next; |
645 | } | 648 | } |
646 | target_dev = _dvfs_info_to_dev(tdvfs_info); | 649 | target_dev = _dvfs_info_to_dev(tdvfs_info); |
647 | if (!target_dev) { | 650 | if (!target_dev) { |
648 | dev_warn(req_dev, "%s: no target_dev\n", | 651 | dev_warn(req_dev, "%s: no target_dev\n", |
649 | __func__); | 652 | __func__); |
650 | goto next; | 653 | goto next; |
651 | } | 654 | } |
652 | r = _dvfs_scale(req_dev, target_dev, tdvfs_info); | 655 | r = _dvfs_scale(req_dev, target_dev, tdvfs_info); |
653 | next: | 656 | next: |
654 | if (r) | 657 | if (r) |
655 | dev_err(req_dev, "%s: dvfs_scale to %s =%d\n", | 658 | dev_err(req_dev, "%s: dvfs_scale to %s =%d\n", |
656 | __func__, dev_name(target_dev), r); | 659 | __func__, dev_name(target_dev), r); |
657 | } | 660 | } |
658 | /* Store last failed value */ | 661 | /* Store last failed value */ |
659 | ret = (r) ? r : ret; | 662 | ret = (r) ? r : ret; |
660 | dep_info++; | 663 | dep_info++; |
661 | } | 664 | } |
662 | 665 | ||
663 | return ret; | 666 | return ret; |
664 | } | 667 | } |
665 | 668 | ||
666 | /** | 669 | /** |
667 | * _dvfs_scale() : Scale the devices associated with a voltage domain | 670 | * _dvfs_scale() : Scale the devices associated with a voltage domain |
668 | * @req_dev: Device requesting the scale | 671 | * @req_dev: Device requesting the scale |
669 | * @target_dev: Device requesting to be scaled | 672 | * @target_dev: Device requesting to be scaled |
670 | * @tdvfs_info: omap_vdd_dvfs_info pointer for the target domain | 673 | * @tdvfs_info: omap_vdd_dvfs_info pointer for the target domain |
671 | * | 674 | * |
672 | * This runs through the list of devices associated with the | 675 | * This runs through the list of devices associated with the |
673 | * voltage domain and scales the device rates to the one requested | 676 | * voltage domain and scales the device rates to the one requested |
674 | * by the user or those corresponding to the new voltage of the | 677 | * by the user or those corresponding to the new voltage of the |
675 | * voltage domain. Target voltage is the highest voltage in the vdd_user_list. | 678 | * voltage domain. Target voltage is the highest voltage in the vdd_user_list. |
676 | * | 679 | * |
677 | * Returns 0 on success else the error value. | 680 | * Returns 0 on success else the error value. |
678 | */ | 681 | */ |
679 | static int _dvfs_scale(struct device *req_dev, struct device *target_dev, | 682 | static int _dvfs_scale(struct device *req_dev, struct device *target_dev, |
680 | struct omap_vdd_dvfs_info *tdvfs_info) | 683 | struct omap_vdd_dvfs_info *tdvfs_info) |
681 | { | 684 | { |
682 | unsigned long curr_volt, new_volt; | 685 | unsigned long curr_volt, new_volt; |
683 | int volt_scale_dir = DVFS_VOLT_SCALE_DOWN; | 686 | int volt_scale_dir = DVFS_VOLT_SCALE_DOWN; |
684 | struct omap_vdd_dev_list *temp_dev; | 687 | struct omap_vdd_dev_list *temp_dev; |
685 | struct plist_node *node; | 688 | struct plist_node *node; |
686 | int ret = 0; | 689 | int ret = 0; |
687 | struct voltagedomain *voltdm; | 690 | struct voltagedomain *voltdm; |
688 | struct omap_vdd_info *vdd; | 691 | struct omap_vdd_info *vdd; |
689 | 692 | ||
690 | voltdm = tdvfs_info->voltdm; | 693 | voltdm = tdvfs_info->voltdm; |
691 | if (IS_ERR_OR_NULL(voltdm)) { | 694 | if (IS_ERR_OR_NULL(voltdm)) { |
692 | dev_err(target_dev, "%s: bad voltdm\n", __func__); | 695 | dev_err(target_dev, "%s: bad voltdm\n", __func__); |
693 | return -EINVAL; | 696 | return -EINVAL; |
694 | } | 697 | } |
695 | vdd = voltdm->vdd; | 698 | vdd = voltdm->vdd; |
696 | 699 | ||
700 | /* Mark that we are scaling for this device */ | ||
701 | tdvfs_info->is_scaling = true; | ||
702 | /* let the other CPU know as well */ | ||
703 | smp_wmb(); | ||
704 | |||
697 | /* Find the highest voltage being requested */ | 705 | /* Find the highest voltage being requested */ |
698 | node = plist_last(&tdvfs_info->vdd_user_list); | 706 | node = plist_last(&tdvfs_info->vdd_user_list); |
699 | new_volt = node->prio; | 707 | new_volt = node->prio; |
700 | 708 | ||
701 | curr_volt = voltdm_get_voltage(voltdm); | 709 | curr_volt = voltdm_get_voltage(voltdm); |
702 | 710 | ||
703 | /* Disable smartreflex module across voltage and frequency scaling */ | 711 | /* Disable smartreflex module across voltage and frequency scaling */ |
704 | omap_sr_disable(voltdm); | 712 | omap_sr_disable(voltdm); |
705 | 713 | ||
706 | if (curr_volt == new_volt) { | 714 | if (curr_volt == new_volt) { |
707 | volt_scale_dir = DVFS_VOLT_SCALE_NONE; | 715 | volt_scale_dir = DVFS_VOLT_SCALE_NONE; |
708 | } else if (curr_volt < new_volt) { | 716 | } else if (curr_volt < new_volt) { |
709 | ret = voltdm_scale(voltdm, new_volt); | 717 | ret = voltdm_scale(voltdm, new_volt); |
710 | if (ret) { | 718 | if (ret) { |
711 | dev_err(target_dev, | 719 | dev_err(target_dev, |
712 | "%s: Unable to scale the %s to %ld volt\n", | 720 | "%s: Unable to scale the %s to %ld volt\n", |
713 | __func__, voltdm->name, new_volt); | 721 | __func__, voltdm->name, new_volt); |
714 | goto out; | 722 | goto out; |
715 | } | 723 | } |
716 | volt_scale_dir = DVFS_VOLT_SCALE_UP; | 724 | volt_scale_dir = DVFS_VOLT_SCALE_UP; |
717 | } | 725 | } |
718 | 726 | ||
719 | /* if we fail scale for dependent domains, go back to prev state */ | 727 | /* if we fail scale for dependent domains, go back to prev state */ |
720 | ret = _dep_scan_domains(target_dev, vdd, new_volt); | 728 | ret = _dep_scan_domains(target_dev, vdd, new_volt); |
721 | if (ret) { | 729 | if (ret) { |
722 | dev_err(target_dev, | 730 | dev_err(target_dev, |
723 | "%s: Error in scan domains for vdd_%s\n", | 731 | "%s: Error in scan domains for vdd_%s\n", |
724 | __func__, voltdm->name); | 732 | __func__, voltdm->name); |
725 | goto fail; | 733 | goto fail; |
726 | } | 734 | } |
727 | 735 | ||
728 | /* unless we are moving down, scale dependents before we shift freq */ | 736 | /* unless we are moving down, scale dependents before we shift freq */ |
729 | if (!(DVFS_VOLT_SCALE_DOWN == volt_scale_dir)) { | 737 | if (!(DVFS_VOLT_SCALE_DOWN == volt_scale_dir)) { |
730 | ret = _dep_scale_domains(target_dev, vdd); | 738 | ret = _dep_scale_domains(target_dev, vdd); |
731 | if (ret) { | 739 | if (ret) { |
732 | dev_err(target_dev, | 740 | dev_err(target_dev, |
733 | "%s: Error(%d)scale dependent with %ld volt\n", | 741 | "%s: Error(%d)scale dependent with %ld volt\n", |
734 | __func__, ret, new_volt); | 742 | __func__, ret, new_volt); |
735 | goto fail; | 743 | goto fail; |
736 | } | 744 | } |
737 | } | 745 | } |
738 | 746 | ||
739 | /* Move all devices in list to the required frequencies */ | 747 | /* Move all devices in list to the required frequencies */ |
740 | list_for_each_entry(temp_dev, &tdvfs_info->dev_list, node) { | 748 | list_for_each_entry(temp_dev, &tdvfs_info->dev_list, node) { |
741 | struct device *dev; | 749 | struct device *dev; |
742 | struct opp *opp; | 750 | struct opp *opp; |
743 | unsigned long freq = 0; | 751 | unsigned long freq = 0; |
744 | int r; | 752 | int r; |
745 | 753 | ||
746 | dev = temp_dev->dev; | 754 | dev = temp_dev->dev; |
747 | if (!plist_head_empty(&temp_dev->freq_user_list)) { | 755 | if (!plist_head_empty(&temp_dev->freq_user_list)) { |
748 | node = plist_last(&temp_dev->freq_user_list); | 756 | node = plist_last(&temp_dev->freq_user_list); |
749 | freq = node->prio; | 757 | freq = node->prio; |
750 | } else { | 758 | } else { |
751 | /* dep domain? we'd probably have a voltage request */ | 759 | /* dep domain? we'd probably have a voltage request */ |
752 | rcu_read_lock(); | 760 | rcu_read_lock(); |
753 | opp = _volt_to_opp(dev, new_volt); | 761 | opp = _volt_to_opp(dev, new_volt); |
754 | if (!IS_ERR(opp)) | 762 | if (!IS_ERR(opp)) |
755 | freq = opp_get_freq(opp); | 763 | freq = opp_get_freq(opp); |
756 | rcu_read_unlock(); | 764 | rcu_read_unlock(); |
757 | if (!freq) | 765 | if (!freq) |
758 | continue; | 766 | continue; |
759 | } | 767 | } |
760 | 768 | ||
761 | if (freq == clk_get_rate(temp_dev->clk)) { | 769 | if (freq == clk_get_rate(temp_dev->clk)) { |
762 | dev_dbg(dev, "%s: Already at the requested" | 770 | dev_dbg(dev, "%s: Already at the requested" |
763 | "rate %ld\n", __func__, freq); | 771 | "rate %ld\n", __func__, freq); |
764 | continue; | 772 | continue; |
765 | } | 773 | } |
766 | 774 | ||
767 | r = clk_set_rate(temp_dev->clk, freq); | 775 | r = clk_set_rate(temp_dev->clk, freq); |
768 | if (r < 0) { | 776 | if (r < 0) { |
769 | dev_err(dev, "%s: clk set rate frq=%ld failed(%d)\n", | 777 | dev_err(dev, "%s: clk set rate frq=%ld failed(%d)\n", |
770 | __func__, freq, r); | 778 | __func__, freq, r); |
771 | ret = r; | 779 | ret = r; |
772 | } | 780 | } |
773 | } | 781 | } |
774 | 782 | ||
775 | if (ret) | 783 | if (ret) |
776 | goto fail; | 784 | goto fail; |
777 | 785 | ||
778 | if (DVFS_VOLT_SCALE_DOWN == volt_scale_dir) { | 786 | if (DVFS_VOLT_SCALE_DOWN == volt_scale_dir) { |
779 | voltdm_scale(voltdm, new_volt); | 787 | voltdm_scale(voltdm, new_volt); |
780 | _dep_scale_domains(target_dev, vdd); | 788 | _dep_scale_domains(target_dev, vdd); |
781 | } | 789 | } |
782 | 790 | ||
783 | /* All clear.. go out gracefully */ | 791 | /* All clear.. go out gracefully */ |
784 | goto out; | 792 | goto out; |
785 | 793 | ||
786 | fail: | 794 | fail: |
787 | pr_warning("%s: domain%s: No clean recovery available! could be bad!\n", | 795 | pr_warning("%s: domain%s: No clean recovery available! could be bad!\n", |
788 | __func__, voltdm->name); | 796 | __func__, voltdm->name); |
789 | out: | 797 | out: |
790 | /* Re-enable Smartreflex module */ | 798 | /* Re-enable Smartreflex module */ |
791 | omap_sr_enable(voltdm); | 799 | omap_sr_enable(voltdm); |
792 | 800 | ||
801 | /* Mark done */ | ||
802 | tdvfs_info->is_scaling = false; | ||
803 | /* let the other CPU know as well */ | ||
804 | smp_wmb(); | ||
805 | |||
793 | return ret; | 806 | return ret; |
794 | } | 807 | } |
795 | 808 | ||
796 | /* Public functions */ | 809 | /* Public functions */ |
797 | 810 | ||
798 | /** | 811 | /** |
799 | * omap_device_scale() - Set a new rate at which the device is to operate | 812 | * omap_device_scale() - Set a new rate at which the device is to operate |
800 | * @req_dev: pointer to the device requesting the scaling. | 813 | * @req_dev: pointer to the device requesting the scaling. |
801 | * @target_dev: pointer to the device that is to be scaled | 814 | * @target_dev: pointer to the device that is to be scaled |
802 | * @rate: the rnew rate for the device. | 815 | * @rate: the rnew rate for the device. |
803 | * | 816 | * |
804 | * This API gets the device opp table associated with this device and | 817 | * This API gets the device opp table associated with this device and |
805 | * tries putting the device to the requested rate and the voltage domain | 818 | * tries putting the device to the requested rate and the voltage domain |
806 | * associated with the device to the voltage corresponding to the | 819 | * associated with the device to the voltage corresponding to the |
807 | * requested rate. Since multiple devices can be assocciated with a | 820 | * requested rate. Since multiple devices can be assocciated with a |
808 | * voltage domain this API finds out the possible voltage the | 821 | * voltage domain this API finds out the possible voltage the |
809 | * voltage domain can enter and then decides on the final device | 822 | * voltage domain can enter and then decides on the final device |
810 | * rate. | 823 | * rate. |
811 | * | 824 | * |
812 | * Return 0 on success else the error value | 825 | * Return 0 on success else the error value |
813 | */ | 826 | */ |
814 | int omap_device_scale(struct device *req_dev, struct device *target_dev, | 827 | int omap_device_scale(struct device *req_dev, struct device *target_dev, |
815 | unsigned long rate) | 828 | unsigned long rate) |
816 | { | 829 | { |
817 | struct opp *opp; | 830 | struct opp *opp; |
818 | unsigned long volt, freq = rate; | 831 | unsigned long volt, freq = rate; |
819 | struct omap_vdd_dvfs_info *tdvfs_info; | 832 | struct omap_vdd_dvfs_info *tdvfs_info; |
820 | struct platform_device *pdev; | 833 | struct platform_device *pdev; |
821 | int ret = 0; | 834 | int ret = 0; |
822 | 835 | ||
823 | pdev = container_of(target_dev, struct platform_device, dev); | 836 | pdev = container_of(target_dev, struct platform_device, dev); |
824 | if (IS_ERR_OR_NULL(pdev)) { | 837 | if (IS_ERR_OR_NULL(pdev)) { |
825 | pr_err("%s: pdev is null!\n", __func__); | 838 | pr_err("%s: pdev is null!\n", __func__); |
826 | return -EINVAL; | 839 | return -EINVAL; |
827 | } | 840 | } |
828 | 841 | ||
829 | /* Lock me to ensure cross domain scaling is secure */ | 842 | /* Lock me to ensure cross domain scaling is secure */ |
830 | mutex_lock(&omap_dvfs_lock); | 843 | mutex_lock(&omap_dvfs_lock); |
831 | 844 | ||
832 | rcu_read_lock(); | 845 | rcu_read_lock(); |
833 | opp = opp_find_freq_ceil(target_dev, &freq); | 846 | opp = opp_find_freq_ceil(target_dev, &freq); |
834 | if (IS_ERR(opp)) { | 847 | if (IS_ERR(opp)) { |
835 | rcu_read_unlock(); | 848 | rcu_read_unlock(); |
836 | dev_err(target_dev, "%s: Unable to find OPP for freq%ld\n", | 849 | dev_err(target_dev, "%s: Unable to find OPP for freq%ld\n", |
837 | __func__, rate); | 850 | __func__, rate); |
838 | ret = -ENODEV; | 851 | ret = -ENODEV; |
839 | goto out; | 852 | goto out; |
840 | } | 853 | } |
841 | volt = opp_get_voltage(opp); | 854 | volt = opp_get_voltage(opp); |
842 | rcu_read_unlock(); | 855 | rcu_read_unlock(); |
843 | 856 | ||
844 | tdvfs_info = _dev_to_dvfs_info(target_dev); | 857 | tdvfs_info = _dev_to_dvfs_info(target_dev); |
845 | if (IS_ERR_OR_NULL(tdvfs_info)) { | 858 | if (IS_ERR_OR_NULL(tdvfs_info)) { |
846 | dev_err(target_dev, "%s: (req=%s) no vdd![f=%ld, v=%ld]\n", | 859 | dev_err(target_dev, "%s: (req=%s) no vdd![f=%ld, v=%ld]\n", |
847 | __func__, dev_name(req_dev), freq, volt); | 860 | __func__, dev_name(req_dev), freq, volt); |
848 | ret = -ENODEV; | 861 | ret = -ENODEV; |
849 | goto out; | 862 | goto out; |
850 | } | 863 | } |
851 | 864 | ||
852 | ret = _add_freq_request(tdvfs_info, req_dev, target_dev, freq); | 865 | ret = _add_freq_request(tdvfs_info, req_dev, target_dev, freq); |
853 | if (ret) { | 866 | if (ret) { |
854 | dev_err(target_dev, "%s: freqadd(%s) failed %d[f=%ld, v=%ld]\n", | 867 | dev_err(target_dev, "%s: freqadd(%s) failed %d[f=%ld, v=%ld]\n", |
855 | __func__, dev_name(req_dev), ret, freq, volt); | 868 | __func__, dev_name(req_dev), ret, freq, volt); |
856 | goto out; | 869 | goto out; |
857 | } | 870 | } |
858 | 871 | ||
859 | ret = _add_vdd_user(tdvfs_info, req_dev, volt); | 872 | ret = _add_vdd_user(tdvfs_info, req_dev, volt); |
860 | if (ret) { | 873 | if (ret) { |
861 | dev_err(target_dev, "%s: vddadd(%s) failed %d[f=%ld, v=%ld]\n", | 874 | dev_err(target_dev, "%s: vddadd(%s) failed %d[f=%ld, v=%ld]\n", |
862 | __func__, dev_name(req_dev), ret, freq, volt); | 875 | __func__, dev_name(req_dev), ret, freq, volt); |
863 | _remove_freq_request(tdvfs_info, req_dev, | 876 | _remove_freq_request(tdvfs_info, req_dev, |
864 | target_dev); | 877 | target_dev); |
865 | goto out; | 878 | goto out; |
866 | } | 879 | } |
867 | 880 | ||
868 | /* Do the actual scaling */ | 881 | /* Do the actual scaling */ |
869 | ret = _dvfs_scale(req_dev, target_dev, tdvfs_info); | 882 | ret = _dvfs_scale(req_dev, target_dev, tdvfs_info); |
870 | if (ret) { | 883 | if (ret) { |
871 | dev_err(target_dev, "%s: scale by %s failed %d[f=%ld, v=%ld]\n", | 884 | dev_err(target_dev, "%s: scale by %s failed %d[f=%ld, v=%ld]\n", |
872 | __func__, dev_name(req_dev), ret, freq, volt); | 885 | __func__, dev_name(req_dev), ret, freq, volt); |
873 | _remove_freq_request(tdvfs_info, req_dev, | 886 | _remove_freq_request(tdvfs_info, req_dev, |
874 | target_dev); | 887 | target_dev); |
875 | _remove_vdd_user(tdvfs_info, target_dev); | 888 | _remove_vdd_user(tdvfs_info, target_dev); |
876 | /* Fall through */ | 889 | /* Fall through */ |
877 | } | 890 | } |
878 | /* Fall through */ | 891 | /* Fall through */ |
879 | out: | 892 | out: |
880 | mutex_unlock(&omap_dvfs_lock); | 893 | mutex_unlock(&omap_dvfs_lock); |
881 | return ret; | 894 | return ret; |
882 | } | 895 | } |
883 | EXPORT_SYMBOL(omap_device_scale); | 896 | EXPORT_SYMBOL(omap_device_scale); |
897 | |||
898 | /** | ||
899 | * omap_dvfs_is_scaling() - Tells the caller if the domain is scaling | ||
900 | * @voltdm: voltage domain we are interested in | ||
901 | * | ||
902 | * Returns true if the domain is in the middle of scale operation, | ||
903 | * returns false if there is no scale operation is in progress or an | ||
904 | * invalid parameter was passed. | ||
905 | */ | ||
906 | bool omap_dvfs_is_scaling(struct voltagedomain *voltdm) | ||
907 | { | ||
908 | struct omap_vdd_dvfs_info *dvfs_info; | ||
909 | |||
910 | if (IS_ERR_OR_NULL(voltdm)) { | ||
911 | pr_err("%s: bad voltdm\n", __func__); | ||
912 | return false; | ||
913 | } | ||
914 | |||
915 | dvfs_info = _voltdm_to_dvfs_info(voltdm); | ||
916 | if (IS_ERR_OR_NULL(dvfs_info)) { | ||
917 | pr_err("%s: no dvfsinfo for voltdm %s\n", | ||
918 | __func__, voltdm->name); | ||
919 | return false; | ||
920 | } | ||
921 | |||
922 | return dvfs_info->is_scaling; | ||
923 | } | ||
924 | EXPORT_SYMBOL(omap_dvfs_is_scaling); | ||
884 | 925 | ||
885 | #ifdef CONFIG_PM_DEBUG | 926 | #ifdef CONFIG_PM_DEBUG |
886 | static int dvfs_dump_vdd(struct seq_file *sf, void *unused) | 927 | static int dvfs_dump_vdd(struct seq_file *sf, void *unused) |
887 | { | 928 | { |
888 | int k; | 929 | int k; |
889 | struct omap_vdd_dvfs_info *dvfs_info; | 930 | struct omap_vdd_dvfs_info *dvfs_info; |
890 | struct omap_vdd_dev_list *tdev; | 931 | struct omap_vdd_dev_list *tdev; |
891 | struct omap_dev_user_list *duser; | 932 | struct omap_dev_user_list *duser; |
892 | struct omap_vdd_user_list *vuser; | 933 | struct omap_vdd_user_list *vuser; |
893 | struct omap_vdd_info *vdd; | 934 | struct omap_vdd_info *vdd; |
894 | struct omap_vdd_dep_info *dep_info; | 935 | struct omap_vdd_dep_info *dep_info; |
895 | struct voltagedomain *voltdm; | 936 | struct voltagedomain *voltdm; |
896 | struct omap_volt_data *volt_data; | 937 | struct omap_volt_data *volt_data; |
897 | int anyreq; | 938 | int anyreq; |
898 | int anyreq2; | 939 | int anyreq2; |
899 | 940 | ||
900 | dvfs_info = (struct omap_vdd_dvfs_info *)sf->private; | 941 | dvfs_info = (struct omap_vdd_dvfs_info *)sf->private; |
901 | if (IS_ERR_OR_NULL(dvfs_info)) { | 942 | if (IS_ERR_OR_NULL(dvfs_info)) { |
902 | pr_err("%s: NO DVFS?\n", __func__); | 943 | pr_err("%s: NO DVFS?\n", __func__); |
903 | return -EINVAL; | 944 | return -EINVAL; |
904 | } | 945 | } |
905 | 946 | ||
906 | voltdm = dvfs_info->voltdm; | 947 | voltdm = dvfs_info->voltdm; |
907 | if (IS_ERR_OR_NULL(voltdm)) { | 948 | if (IS_ERR_OR_NULL(voltdm)) { |
908 | pr_err("%s: NO voltdm?\n", __func__); | 949 | pr_err("%s: NO voltdm?\n", __func__); |
909 | return -EINVAL; | 950 | return -EINVAL; |
910 | } | 951 | } |
911 | 952 | ||
912 | vdd = voltdm->vdd; | 953 | vdd = voltdm->vdd; |
913 | if (IS_ERR_OR_NULL(vdd)) { | 954 | if (IS_ERR_OR_NULL(vdd)) { |
914 | pr_err("%s: NO vdd data?\n", __func__); | 955 | pr_err("%s: NO vdd data?\n", __func__); |
915 | return -EINVAL; | 956 | return -EINVAL; |
916 | } | 957 | } |
917 | 958 | ||
918 | seq_printf(sf, "vdd_%s\n", voltdm->name); | 959 | seq_printf(sf, "vdd_%s\n", voltdm->name); |
919 | mutex_lock(&omap_dvfs_lock); | 960 | mutex_lock(&omap_dvfs_lock); |
920 | spin_lock(&dvfs_info->user_lock); | 961 | spin_lock(&dvfs_info->user_lock); |
921 | 962 | ||
922 | seq_printf(sf, "|- voltage requests\n| |\n"); | 963 | seq_printf(sf, "|- voltage requests\n| |\n"); |
923 | anyreq = 0; | 964 | anyreq = 0; |
924 | plist_for_each_entry(vuser, &dvfs_info->vdd_user_list, node) { | 965 | plist_for_each_entry(vuser, &dvfs_info->vdd_user_list, node) { |
925 | seq_printf(sf, "| |-%d: %s:%s\n", | 966 | seq_printf(sf, "| |-%d: %s:%s\n", |
926 | vuser->node.prio, | 967 | vuser->node.prio, |
927 | dev_driver_string(vuser->dev), dev_name(vuser->dev)); | 968 | dev_driver_string(vuser->dev), dev_name(vuser->dev)); |
928 | anyreq = 1; | 969 | anyreq = 1; |
929 | } | 970 | } |
930 | 971 | ||
931 | spin_unlock(&dvfs_info->user_lock); | 972 | spin_unlock(&dvfs_info->user_lock); |
932 | 973 | ||
933 | if (!anyreq) | 974 | if (!anyreq) |
934 | seq_printf(sf, "| `-none\n"); | 975 | seq_printf(sf, "| `-none\n"); |
935 | else | 976 | else |
936 | seq_printf(sf, "| X\n"); | 977 | seq_printf(sf, "| X\n"); |
937 | seq_printf(sf, "|\n"); | 978 | seq_printf(sf, "|\n"); |
938 | 979 | ||
939 | seq_printf(sf, "|- frequency requests\n| |\n"); | 980 | seq_printf(sf, "|- frequency requests\n| |\n"); |
940 | anyreq2 = 0; | 981 | anyreq2 = 0; |
941 | list_for_each_entry(tdev, &dvfs_info->dev_list, node) { | 982 | list_for_each_entry(tdev, &dvfs_info->dev_list, node) { |
942 | anyreq = 0; | 983 | anyreq = 0; |
943 | seq_printf(sf, "| |- %s:%s\n", | 984 | seq_printf(sf, "| |- %s:%s\n", |
944 | dev_driver_string(tdev->dev), dev_name(tdev->dev)); | 985 | dev_driver_string(tdev->dev), dev_name(tdev->dev)); |
945 | spin_lock(&tdev->user_lock); | 986 | spin_lock(&tdev->user_lock); |
946 | plist_for_each_entry(duser, &tdev->freq_user_list, node) { | 987 | plist_for_each_entry(duser, &tdev->freq_user_list, node) { |
947 | seq_printf(sf, "| | |-%d: %s:%s\n", | 988 | seq_printf(sf, "| | |-%d: %s:%s\n", |
948 | duser->node.prio, | 989 | duser->node.prio, |
949 | dev_driver_string(duser->dev), | 990 | dev_driver_string(duser->dev), |
950 | dev_name(duser->dev)); | 991 | dev_name(duser->dev)); |
951 | anyreq = 1; | 992 | anyreq = 1; |
952 | } | 993 | } |
953 | 994 | ||
954 | spin_unlock(&tdev->user_lock); | 995 | spin_unlock(&tdev->user_lock); |
955 | 996 | ||
956 | if (!anyreq) | 997 | if (!anyreq) |
957 | seq_printf(sf, "| | `-none\n"); | 998 | seq_printf(sf, "| | `-none\n"); |
958 | else | 999 | else |
959 | seq_printf(sf, "| | X\n"); | 1000 | seq_printf(sf, "| | X\n"); |
960 | anyreq2 = 1; | 1001 | anyreq2 = 1; |
961 | } | 1002 | } |
962 | if (!anyreq2) | 1003 | if (!anyreq2) |
963 | seq_printf(sf, "| `-none\n"); | 1004 | seq_printf(sf, "| `-none\n"); |
964 | else | 1005 | else |
965 | seq_printf(sf, "| X\n"); | 1006 | seq_printf(sf, "| X\n"); |
966 | 1007 | ||
967 | volt_data = vdd->volt_data; | 1008 | volt_data = vdd->volt_data; |
968 | seq_printf(sf, "|- Supported voltages\n| |\n"); | 1009 | seq_printf(sf, "|- Supported voltages\n| |\n"); |
969 | anyreq = 0; | 1010 | anyreq = 0; |
970 | while (volt_data && volt_data->volt_nominal) { | 1011 | while (volt_data && volt_data->volt_nominal) { |
971 | seq_printf(sf, "| |-%d\n", volt_data->volt_nominal); | 1012 | seq_printf(sf, "| |-%d\n", volt_data->volt_nominal); |
972 | anyreq = 1; | 1013 | anyreq = 1; |
973 | volt_data++; | 1014 | volt_data++; |
974 | } | 1015 | } |
975 | if (!anyreq) | 1016 | if (!anyreq) |
976 | seq_printf(sf, "| `-none\n"); | 1017 | seq_printf(sf, "| `-none\n"); |
977 | else | 1018 | else |
978 | seq_printf(sf, "| X\n"); | 1019 | seq_printf(sf, "| X\n"); |
979 | 1020 | ||
980 | dep_info = vdd->dep_vdd_info; | 1021 | dep_info = vdd->dep_vdd_info; |
981 | seq_printf(sf, "`- voltage dependencies\n |\n"); | 1022 | seq_printf(sf, "`- voltage dependencies\n |\n"); |
982 | anyreq = 0; | 1023 | anyreq = 0; |
983 | while (dep_info && dep_info->nr_dep_entries) { | 1024 | while (dep_info && dep_info->nr_dep_entries) { |
984 | struct omap_vdd_dep_volt *dep_table = dep_info->dep_table; | 1025 | struct omap_vdd_dep_volt *dep_table = dep_info->dep_table; |
985 | 1026 | ||
986 | seq_printf(sf, " |-on vdd_%s\n", dep_info->name); | 1027 | seq_printf(sf, " |-on vdd_%s\n", dep_info->name); |
987 | 1028 | ||
988 | for (k = 0; k < dep_info->nr_dep_entries; k++) { | 1029 | for (k = 0; k < dep_info->nr_dep_entries; k++) { |
989 | seq_printf(sf, " | |- %d => %d\n", | 1030 | seq_printf(sf, " | |- %d => %d\n", |
990 | dep_table[k].main_vdd_volt, | 1031 | dep_table[k].main_vdd_volt, |
991 | dep_table[k].dep_vdd_volt); | 1032 | dep_table[k].dep_vdd_volt); |
992 | } | 1033 | } |
993 | 1034 | ||
994 | anyreq = 1; | 1035 | anyreq = 1; |
995 | dep_info++; | 1036 | dep_info++; |
996 | } | 1037 | } |
997 | 1038 | ||
998 | if (!anyreq) | 1039 | if (!anyreq) |
999 | seq_printf(sf, " `- none\n"); | 1040 | seq_printf(sf, " `- none\n"); |
1000 | else | 1041 | else |
1001 | seq_printf(sf, " X X\n"); | 1042 | seq_printf(sf, " X X\n"); |
1002 | 1043 | ||
1003 | mutex_unlock(&omap_dvfs_lock); | 1044 | mutex_unlock(&omap_dvfs_lock); |
1004 | return 0; | 1045 | return 0; |
1005 | } | 1046 | } |
1006 | 1047 | ||
1007 | static int dvfs_dbg_open(struct inode *inode, struct file *file) | 1048 | static int dvfs_dbg_open(struct inode *inode, struct file *file) |
1008 | { | 1049 | { |
1009 | return single_open(file, dvfs_dump_vdd, inode->i_private); | 1050 | return single_open(file, dvfs_dump_vdd, inode->i_private); |
1010 | } | 1051 | } |
1011 | 1052 | ||
1012 | static struct file_operations debugdvfs_fops = { | 1053 | static struct file_operations debugdvfs_fops = { |
1013 | .open = dvfs_dbg_open, | 1054 | .open = dvfs_dbg_open, |
1014 | .read = seq_read, | 1055 | .read = seq_read, |
1015 | .llseek = seq_lseek, | 1056 | .llseek = seq_lseek, |
1016 | .release = single_release, | 1057 | .release = single_release, |
1017 | }; | 1058 | }; |
1018 | 1059 | ||
1019 | static struct dentry __initdata *dvfsdebugfs_dir; | 1060 | static struct dentry __initdata *dvfsdebugfs_dir; |
1020 | 1061 | ||
1021 | static void __init dvfs_dbg_init(struct omap_vdd_dvfs_info *dvfs_info) | 1062 | static void __init dvfs_dbg_init(struct omap_vdd_dvfs_info *dvfs_info) |
1022 | { | 1063 | { |
1023 | struct dentry *ddir; | 1064 | struct dentry *ddir; |
1024 | 1065 | ||
1025 | /* create a base dir */ | 1066 | /* create a base dir */ |
1026 | if (!dvfsdebugfs_dir) | 1067 | if (!dvfsdebugfs_dir) |
1027 | dvfsdebugfs_dir = debugfs_create_dir("dvfs", NULL); | 1068 | dvfsdebugfs_dir = debugfs_create_dir("dvfs", NULL); |
1028 | if (IS_ERR_OR_NULL(dvfsdebugfs_dir)) { | 1069 | if (IS_ERR_OR_NULL(dvfsdebugfs_dir)) { |
1029 | WARN_ONCE("%s: Unable to create base DVFS dir\n", __func__); | 1070 | WARN_ONCE("%s: Unable to create base DVFS dir\n", __func__); |
1030 | return; | 1071 | return; |
1031 | } | 1072 | } |
1032 | 1073 | ||
1033 | if (IS_ERR_OR_NULL(dvfs_info->voltdm)) { | 1074 | if (IS_ERR_OR_NULL(dvfs_info->voltdm)) { |
1034 | pr_err("%s: no voltdm\n", __func__); | 1075 | pr_err("%s: no voltdm\n", __func__); |
1035 | return; | 1076 | return; |
1036 | } | 1077 | } |
1037 | 1078 | ||
1038 | ddir = debugfs_create_dir(dvfs_info->voltdm->name, dvfsdebugfs_dir); | 1079 | ddir = debugfs_create_dir(dvfs_info->voltdm->name, dvfsdebugfs_dir); |
1039 | if (IS_ERR_OR_NULL(ddir)) { | 1080 | if (IS_ERR_OR_NULL(ddir)) { |
1040 | pr_warning("%s: unable to create subdir %s\n", __func__, | 1081 | pr_warning("%s: unable to create subdir %s\n", __func__, |
1041 | dvfs_info->voltdm->name); | 1082 | dvfs_info->voltdm->name); |
1042 | return; | 1083 | return; |
1043 | } | 1084 | } |
1044 | 1085 | ||
1045 | debugfs_create_file("info", S_IRUGO, ddir, | 1086 | debugfs_create_file("info", S_IRUGO, ddir, |
1046 | (void *)dvfs_info, &debugdvfs_fops); | 1087 | (void *)dvfs_info, &debugdvfs_fops); |
1047 | } | 1088 | } |
1048 | #else /* CONFIG_PM_DEBUG */ | 1089 | #else /* CONFIG_PM_DEBUG */ |
1049 | static inline void dvfs_dbg_init(struct omap_vdd_dvfs_info *dvfs_info) | 1090 | static inline void dvfs_dbg_init(struct omap_vdd_dvfs_info *dvfs_info) |
1050 | { | 1091 | { |
1051 | return; | 1092 | return; |
1052 | } | 1093 | } |
1053 | #endif /* CONFIG_PM_DEBUG */ | 1094 | #endif /* CONFIG_PM_DEBUG */ |
1054 | 1095 | ||
1055 | /** | 1096 | /** |
1056 | * omap_dvfs_register_device - Add a parent device into dvfs managed list | 1097 | * omap_dvfs_register_device - Add a parent device into dvfs managed list |
1057 | * @dev: Device to be added | 1098 | * @dev: Device to be added |
1058 | * @voltdm_name: Name of the voltage domain for the device | 1099 | * @voltdm_name: Name of the voltage domain for the device |
1059 | * @clk_name: Name of the clock for the device | 1100 | * @clk_name: Name of the clock for the device |
1060 | * | 1101 | * |
1061 | * This function adds a given device into user_list of corresponding | 1102 | * This function adds a given device into user_list of corresponding |
1062 | * vdd's omap_vdd_dvfs_info strucure. This list is traversed to scale | 1103 | * vdd's omap_vdd_dvfs_info strucure. This list is traversed to scale |
1063 | * frequencies of all the devices on a given vdd. | 1104 | * frequencies of all the devices on a given vdd. |
1064 | * | 1105 | * |
1065 | * Returns 0 on success. | 1106 | * Returns 0 on success. |
1066 | */ | 1107 | */ |
1067 | int __init omap_dvfs_register_device(struct device *dev, char *voltdm_name, | 1108 | int __init omap_dvfs_register_device(struct device *dev, char *voltdm_name, |
1068 | char *clk_name) | 1109 | char *clk_name) |
1069 | { | 1110 | { |
1070 | struct omap_vdd_dev_list *temp_dev; | 1111 | struct omap_vdd_dev_list *temp_dev; |
1071 | struct omap_vdd_dvfs_info *dvfs_info; | 1112 | struct omap_vdd_dvfs_info *dvfs_info; |
1072 | struct clk *clk = NULL; | 1113 | struct clk *clk = NULL; |
1073 | struct voltagedomain *voltdm; | 1114 | struct voltagedomain *voltdm; |
1074 | int ret = 0; | 1115 | int ret = 0; |
1075 | 1116 | ||
1076 | if (!voltdm_name) { | 1117 | if (!voltdm_name) { |
1077 | dev_err(dev, "%s: Bad voltdm name!\n", __func__); | 1118 | dev_err(dev, "%s: Bad voltdm name!\n", __func__); |
1078 | return -EINVAL; | 1119 | return -EINVAL; |
1079 | } | 1120 | } |
1080 | if (!clk_name) { | 1121 | if (!clk_name) { |
1081 | dev_err(dev, "%s: Bad clk name!\n", __func__); | 1122 | dev_err(dev, "%s: Bad clk name!\n", __func__); |
1082 | return -EINVAL; | 1123 | return -EINVAL; |
1083 | } | 1124 | } |
1084 | 1125 | ||
1085 | /* Lock me to secure structure changes */ | 1126 | /* Lock me to secure structure changes */ |
1086 | mutex_lock(&omap_dvfs_lock); | 1127 | mutex_lock(&omap_dvfs_lock); |
1087 | 1128 | ||
1088 | voltdm = voltdm_lookup(voltdm_name); | 1129 | voltdm = voltdm_lookup(voltdm_name); |
1089 | if (!voltdm) { | 1130 | if (!voltdm) { |
1090 | dev_warn(dev, "%s: unable to find voltdm %s!\n", | 1131 | dev_warn(dev, "%s: unable to find voltdm %s!\n", |
1091 | __func__, voltdm_name); | 1132 | __func__, voltdm_name); |
1092 | ret = -EINVAL; | 1133 | ret = -EINVAL; |
1093 | goto out; | 1134 | goto out; |
1094 | } | 1135 | } |
1095 | dvfs_info = _voltdm_to_dvfs_info(voltdm); | 1136 | dvfs_info = _voltdm_to_dvfs_info(voltdm); |
1096 | if (!dvfs_info) { | 1137 | if (!dvfs_info) { |
1097 | dvfs_info = kzalloc(sizeof(struct omap_vdd_dvfs_info), | 1138 | dvfs_info = kzalloc(sizeof(struct omap_vdd_dvfs_info), |
1098 | GFP_KERNEL); | 1139 | GFP_KERNEL); |
1099 | if (!dvfs_info) { | 1140 | if (!dvfs_info) { |
1100 | dev_warn(dev, "%s: unable to alloc memory!\n", | 1141 | dev_warn(dev, "%s: unable to alloc memory!\n", |
1101 | __func__); | 1142 | __func__); |
1102 | ret = -ENOMEM; | 1143 | ret = -ENOMEM; |
1103 | goto out; | 1144 | goto out; |
1104 | } | 1145 | } |
1105 | dvfs_info->voltdm = voltdm; | 1146 | dvfs_info->voltdm = voltdm; |
1106 | 1147 | ||
1107 | /* Init the plist */ | 1148 | /* Init the plist */ |
1108 | spin_lock_init(&dvfs_info->user_lock); | 1149 | spin_lock_init(&dvfs_info->user_lock); |
1109 | plist_head_init(&dvfs_info->vdd_user_list); | 1150 | plist_head_init(&dvfs_info->vdd_user_list); |
1110 | 1151 | ||
1111 | /* Init the device list */ | 1152 | /* Init the device list */ |
1112 | INIT_LIST_HEAD(&dvfs_info->dev_list); | 1153 | INIT_LIST_HEAD(&dvfs_info->dev_list); |
1113 | 1154 | ||
1114 | list_add(&dvfs_info->node, &omap_dvfs_info_list); | 1155 | list_add(&dvfs_info->node, &omap_dvfs_info_list); |
1115 | 1156 | ||
1116 | dvfs_dbg_init(dvfs_info); | 1157 | dvfs_dbg_init(dvfs_info); |
1117 | } | 1158 | } |
1118 | 1159 | ||
1119 | /* If device already added, we dont need to do more.. */ | 1160 | /* If device already added, we dont need to do more.. */ |
1120 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { | 1161 | list_for_each_entry(temp_dev, &dvfs_info->dev_list, node) { |
1121 | if (temp_dev->dev == dev) | 1162 | if (temp_dev->dev == dev) |
1122 | goto out; | 1163 | goto out; |
1123 | } | 1164 | } |
1124 | 1165 | ||
1125 | temp_dev = kzalloc(sizeof(struct omap_vdd_dev_list), GFP_KERNEL); | 1166 | temp_dev = kzalloc(sizeof(struct omap_vdd_dev_list), GFP_KERNEL); |
1126 | if (!temp_dev) { | 1167 | if (!temp_dev) { |
1127 | dev_err(dev, "%s: Unable to creat a new device for vdd_%s\n", | 1168 | dev_err(dev, "%s: Unable to creat a new device for vdd_%s\n", |
1128 | __func__, dvfs_info->voltdm->name); | 1169 | __func__, dvfs_info->voltdm->name); |
1129 | ret = -ENOMEM; | 1170 | ret = -ENOMEM; |
1130 | goto out; | 1171 | goto out; |
1131 | } | 1172 | } |
1132 | 1173 | ||
1133 | clk = clk_get(dev, clk_name); | 1174 | clk = clk_get(dev, clk_name); |
1134 | if (IS_ERR_OR_NULL(clk)) { | 1175 | if (IS_ERR_OR_NULL(clk)) { |
1135 | dev_warn(dev, "%s: Bad clk pointer!\n", __func__); | 1176 | dev_warn(dev, "%s: Bad clk pointer!\n", __func__); |
1136 | kfree(temp_dev); | 1177 | kfree(temp_dev); |
1137 | ret = -EINVAL; | 1178 | ret = -EINVAL; |
1138 | goto out; | 1179 | goto out; |
1139 | } | 1180 | } |
1140 | 1181 | ||
1141 | /* Initialize priority ordered list */ | 1182 | /* Initialize priority ordered list */ |
1142 | spin_lock_init(&temp_dev->user_lock); | 1183 | spin_lock_init(&temp_dev->user_lock); |
1143 | plist_head_init(&temp_dev->freq_user_list); | 1184 | plist_head_init(&temp_dev->freq_user_list); |
1144 | 1185 | ||
1145 | temp_dev->dev = dev; | 1186 | temp_dev->dev = dev; |
1146 | temp_dev->clk = clk; | 1187 | temp_dev->clk = clk; |
1147 | list_add_tail(&temp_dev->node, &dvfs_info->dev_list); | 1188 | list_add_tail(&temp_dev->node, &dvfs_info->dev_list); |
1148 | 1189 | ||
1149 | /* Fall through */ | 1190 | /* Fall through */ |
1150 | out: | 1191 | out: |
1151 | mutex_unlock(&omap_dvfs_lock); | 1192 | mutex_unlock(&omap_dvfs_lock); |
1152 | return ret; | 1193 | return ret; |
1153 | } | 1194 | } |
1154 | 1195 |
arch/arm/plat-omap/include/plat/dvfs.h
1 | /* | 1 | /* |
2 | * OMAP3/OMAP4 DVFS Management Routines | 2 | * OMAP3/OMAP4 DVFS Management Routines |
3 | * | 3 | * |
4 | * Author: Vishwanath BS <vishwanath.bs@ti.com> | 4 | * Author: Vishwanath BS <vishwanath.bs@ti.com> |
5 | * | 5 | * |
6 | * Copyright (C) 2011 Texas Instruments, Inc. | 6 | * Copyright (C) 2011 Texas Instruments, Inc. |
7 | * Vishwanath BS <vishwanath.bs@ti.com> | 7 | * Vishwanath BS <vishwanath.bs@ti.com> |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #ifndef __ARCH_ARM_MACH_OMAP2_DVFS_H | 14 | #ifndef __ARCH_ARM_MACH_OMAP2_DVFS_H |
15 | #define __ARCH_ARM_MACH_OMAP2_DVFS_H | 15 | #define __ARCH_ARM_MACH_OMAP2_DVFS_H |
16 | #include <plat/omap_hwmod.h> | 16 | #include <plat/omap_hwmod.h> |
17 | #include <plat/voltage.h> | 17 | #include <plat/voltage.h> |
18 | 18 | ||
19 | #ifdef CONFIG_PM | 19 | #ifdef CONFIG_PM |
20 | int omap_dvfs_register_device(struct device *dev, char *voltdm_name, | 20 | int omap_dvfs_register_device(struct device *dev, char *voltdm_name, |
21 | char *clk_name); | 21 | char *clk_name); |
22 | int omap_device_scale(struct device *req_dev, struct device *target_dev, | 22 | int omap_device_scale(struct device *req_dev, struct device *target_dev, |
23 | unsigned long rate); | 23 | unsigned long rate); |
24 | |||
25 | bool omap_dvfs_is_scaling(struct voltagedomain *voltdm); | ||
24 | #else | 26 | #else |
25 | static inline int omap_dvfs_register_device(struct omap_hwmod *oh, | 27 | static inline int omap_dvfs_register_device(struct omap_hwmod *oh, |
26 | struct device *dev) | 28 | struct device *dev) |
27 | static inline int omap_dvfs_register_device(struct device *dev, | 29 | static inline int omap_dvfs_register_device(struct device *dev, |
28 | char *voltdm_name, char *clk_name) | 30 | char *voltdm_name, char *clk_name) |
29 | { | 31 | { |
30 | return -EINVAL; | 32 | return -EINVAL; |
31 | } | 33 | } |
32 | static inline int omap_device_scale(struct device *req_dev, | 34 | static inline int omap_device_scale(struct device *req_dev, |
33 | struct device *target_dev, unsigned long rate) | 35 | struct device *target_dev, unsigned long rate) |
34 | { | 36 | { |
35 | return -EINVAL; | 37 | return -EINVAL; |
38 | } | ||
39 | static inline bool omap_dvfs_is_scaling(struct voltagedomain *voltdm) | ||
40 | { | ||
41 | return false; | ||
36 | } | 42 | } |
37 | #endif | 43 | #endif |
38 | #endif | 44 | #endif |
39 | 45 |