diff options
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/smartreflex.c | 983 | ||||
-rw-r--r-- | arch/arm/plat-omap/Kconfig | 22 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/smartreflex.h | 245 |
4 files changed, 1251 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index da7d53690d91..98c6309c2507 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile | |||
@@ -62,6 +62,7 @@ obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o voltage.o \ | |||
62 | cpuidle34xx.o pm_bus.o | 62 | cpuidle34xx.o pm_bus.o |
63 | obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o pm_bus.o | 63 | obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o pm_bus.o |
64 | obj-$(CONFIG_PM_DEBUG) += pm-debug.o | 64 | obj-$(CONFIG_PM_DEBUG) += pm-debug.o |
65 | obj-$(CONFIG_OMAP_SMARTREFLEX) += smartreflex.o | ||
65 | 66 | ||
66 | AFLAGS_sleep24xx.o :=-Wa,-march=armv6 | 67 | AFLAGS_sleep24xx.o :=-Wa,-march=armv6 |
67 | AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a | 68 | AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a |
diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c new file mode 100644 index 000000000000..eee23d0f50de --- /dev/null +++ b/arch/arm/mach-omap2/smartreflex.c | |||
@@ -0,0 +1,983 @@ | |||
1 | /* | ||
2 | * OMAP SmartReflex Voltage Control | ||
3 | * | ||
4 | * Author: Thara Gopinath <thara@ti.com> | ||
5 | * | ||
6 | * Copyright (C) 2010 Texas Instruments, Inc. | ||
7 | * Thara Gopinath <thara@ti.com> | ||
8 | * | ||
9 | * Copyright (C) 2008 Nokia Corporation | ||
10 | * Kalle Jokiniemi | ||
11 | * | ||
12 | * Copyright (C) 2007 Texas Instruments, Inc. | ||
13 | * Lesly A M <x0080970@ti.com> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License version 2 as | ||
17 | * published by the Free Software Foundation. | ||
18 | */ | ||
19 | |||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/debugfs.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | |||
28 | #include <plat/common.h> | ||
29 | #include <plat/smartreflex.h> | ||
30 | |||
31 | #include "pm.h" | ||
32 | |||
33 | #define SMARTREFLEX_NAME_LEN 16 | ||
34 | #define SR_DISABLE_TIMEOUT 200 | ||
35 | |||
36 | struct omap_sr { | ||
37 | int srid; | ||
38 | int ip_type; | ||
39 | int nvalue_count; | ||
40 | bool autocomp_active; | ||
41 | u32 clk_length; | ||
42 | u32 err_weight; | ||
43 | u32 err_minlimit; | ||
44 | u32 err_maxlimit; | ||
45 | u32 accum_data; | ||
46 | u32 senn_avgweight; | ||
47 | u32 senp_avgweight; | ||
48 | u32 senp_mod; | ||
49 | u32 senn_mod; | ||
50 | unsigned int irq; | ||
51 | void __iomem *base; | ||
52 | struct platform_device *pdev; | ||
53 | struct list_head node; | ||
54 | struct omap_sr_nvalue_table *nvalue_table; | ||
55 | struct voltagedomain *voltdm; | ||
56 | }; | ||
57 | |||
58 | /* sr_list contains all the instances of smartreflex module */ | ||
59 | static LIST_HEAD(sr_list); | ||
60 | |||
61 | static struct omap_sr_class_data *sr_class; | ||
62 | static struct omap_sr_pmic_data *sr_pmic_data; | ||
63 | |||
64 | static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value) | ||
65 | { | ||
66 | __raw_writel(value, (sr->base + offset)); | ||
67 | } | ||
68 | |||
69 | static inline void sr_modify_reg(struct omap_sr *sr, unsigned offset, u32 mask, | ||
70 | u32 value) | ||
71 | { | ||
72 | u32 reg_val; | ||
73 | u32 errconfig_offs = 0, errconfig_mask = 0; | ||
74 | |||
75 | reg_val = __raw_readl(sr->base + offset); | ||
76 | reg_val &= ~mask; | ||
77 | |||
78 | /* | ||
79 | * Smartreflex error config register is special as it contains | ||
80 | * certain status bits which if written a 1 into means a clear | ||
81 | * of those bits. So in order to make sure no accidental write of | ||
82 | * 1 happens to those status bits, do a clear of them in the read | ||
83 | * value. This mean this API doesn't rewrite values in these bits | ||
84 | * if they are currently set, but does allow the caller to write | ||
85 | * those bits. | ||
86 | */ | ||
87 | if (sr->ip_type == SR_TYPE_V1) { | ||
88 | errconfig_offs = ERRCONFIG_V1; | ||
89 | errconfig_mask = ERRCONFIG_STATUS_V1_MASK; | ||
90 | } else if (sr->ip_type == SR_TYPE_V2) { | ||
91 | errconfig_offs = ERRCONFIG_V2; | ||
92 | errconfig_mask = ERRCONFIG_VPBOUNDINTST_V2; | ||
93 | } | ||
94 | |||
95 | if (offset == errconfig_offs) | ||
96 | reg_val &= ~errconfig_mask; | ||
97 | |||
98 | reg_val |= value; | ||
99 | |||
100 | __raw_writel(reg_val, (sr->base + offset)); | ||
101 | } | ||
102 | |||
103 | static inline u32 sr_read_reg(struct omap_sr *sr, unsigned offset) | ||
104 | { | ||
105 | return __raw_readl(sr->base + offset); | ||
106 | } | ||
107 | |||
108 | static struct omap_sr *_sr_lookup(struct voltagedomain *voltdm) | ||
109 | { | ||
110 | struct omap_sr *sr_info; | ||
111 | |||
112 | if (!voltdm) { | ||
113 | pr_err("%s: Null voltage domain passed!\n", __func__); | ||
114 | return ERR_PTR(-EINVAL); | ||
115 | } | ||
116 | |||
117 | list_for_each_entry(sr_info, &sr_list, node) { | ||
118 | if (voltdm == sr_info->voltdm) | ||
119 | return sr_info; | ||
120 | } | ||
121 | |||
122 | return ERR_PTR(-ENODATA); | ||
123 | } | ||
124 | |||
125 | static irqreturn_t sr_interrupt(int irq, void *data) | ||
126 | { | ||
127 | struct omap_sr *sr_info = (struct omap_sr *)data; | ||
128 | u32 status = 0; | ||
129 | |||
130 | if (sr_info->ip_type == SR_TYPE_V1) { | ||
131 | /* Read the status bits */ | ||
132 | status = sr_read_reg(sr_info, ERRCONFIG_V1); | ||
133 | |||
134 | /* Clear them by writing back */ | ||
135 | sr_write_reg(sr_info, ERRCONFIG_V1, status); | ||
136 | } else if (sr_info->ip_type == SR_TYPE_V2) { | ||
137 | /* Read the status bits */ | ||
138 | sr_read_reg(sr_info, IRQSTATUS); | ||
139 | |||
140 | /* Clear them by writing back */ | ||
141 | sr_write_reg(sr_info, IRQSTATUS, status); | ||
142 | } | ||
143 | |||
144 | if (sr_class->class_type == SR_CLASS2 && sr_class->notify) | ||
145 | sr_class->notify(sr_info->voltdm, status); | ||
146 | |||
147 | return IRQ_HANDLED; | ||
148 | } | ||
149 | |||
150 | static void sr_set_clk_length(struct omap_sr *sr) | ||
151 | { | ||
152 | struct clk *sys_ck; | ||
153 | u32 sys_clk_speed; | ||
154 | |||
155 | sys_ck = clk_get(NULL, "sys_ck"); | ||
156 | if (IS_ERR(sys_ck)) { | ||
157 | dev_err(&sr->pdev->dev, "%s: unable to get sys clk\n", | ||
158 | __func__); | ||
159 | return; | ||
160 | } | ||
161 | sys_clk_speed = clk_get_rate(sys_ck); | ||
162 | clk_put(sys_ck); | ||
163 | |||
164 | switch (sys_clk_speed) { | ||
165 | case 12000000: | ||
166 | sr->clk_length = SRCLKLENGTH_12MHZ_SYSCLK; | ||
167 | break; | ||
168 | case 13000000: | ||
169 | sr->clk_length = SRCLKLENGTH_13MHZ_SYSCLK; | ||
170 | break; | ||
171 | case 19200000: | ||
172 | sr->clk_length = SRCLKLENGTH_19MHZ_SYSCLK; | ||
173 | break; | ||
174 | case 26000000: | ||
175 | sr->clk_length = SRCLKLENGTH_26MHZ_SYSCLK; | ||
176 | break; | ||
177 | case 38400000: | ||
178 | sr->clk_length = SRCLKLENGTH_38MHZ_SYSCLK; | ||
179 | break; | ||
180 | default: | ||
181 | dev_err(&sr->pdev->dev, "%s: Invalid sysclk value: %d\n", | ||
182 | __func__, sys_clk_speed); | ||
183 | break; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | static void sr_set_regfields(struct omap_sr *sr) | ||
188 | { | ||
189 | /* | ||
190 | * For time being these values are defined in smartreflex.h | ||
191 | * and populated during init. May be they can be moved to board | ||
192 | * file or pmic specific data structure. In that case these structure | ||
193 | * fields will have to be populated using the pdata or pmic structure. | ||
194 | */ | ||
195 | if (cpu_is_omap34xx()) { | ||
196 | sr->err_weight = OMAP3430_SR_ERRWEIGHT; | ||
197 | sr->err_maxlimit = OMAP3430_SR_ERRMAXLIMIT; | ||
198 | sr->accum_data = OMAP3430_SR_ACCUMDATA; | ||
199 | if (!(strcmp(sr->voltdm->name, "mpu"))) { | ||
200 | sr->senn_avgweight = OMAP3430_SR1_SENNAVGWEIGHT; | ||
201 | sr->senp_avgweight = OMAP3430_SR1_SENPAVGWEIGHT; | ||
202 | } else { | ||
203 | sr->senn_avgweight = OMAP3430_SR2_SENNAVGWEIGHT; | ||
204 | sr->senp_avgweight = OMAP3430_SR2_SENPAVGWEIGHT; | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static void sr_start_vddautocomp(struct omap_sr *sr) | ||
210 | { | ||
211 | if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) { | ||
212 | dev_warn(&sr->pdev->dev, | ||
213 | "%s: smartreflex class driver not registered\n", | ||
214 | __func__); | ||
215 | return; | ||
216 | } | ||
217 | |||
218 | if (!sr_class->enable(sr->voltdm)) | ||
219 | sr->autocomp_active = true; | ||
220 | } | ||
221 | |||
222 | static void sr_stop_vddautocomp(struct omap_sr *sr) | ||
223 | { | ||
224 | if (!sr_class || !(sr_class->disable)) { | ||
225 | dev_warn(&sr->pdev->dev, | ||
226 | "%s: smartreflex class driver not registered\n", | ||
227 | __func__); | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | if (sr->autocomp_active) { | ||
232 | sr_class->disable(sr->voltdm, 1); | ||
233 | sr->autocomp_active = false; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * This function handles the intializations which have to be done | ||
239 | * only when both sr device and class driver regiter has | ||
240 | * completed. This will be attempted to be called from both sr class | ||
241 | * driver register and sr device intializtion API's. Only one call | ||
242 | * will ultimately succeed. | ||
243 | * | ||
244 | * Currenly this function registers interrrupt handler for a particular SR | ||
245 | * if smartreflex class driver is already registered and has | ||
246 | * requested for interrupts and the SR interrupt line in present. | ||
247 | */ | ||
248 | static int sr_late_init(struct omap_sr *sr_info) | ||
249 | { | ||
250 | char *name; | ||
251 | struct omap_sr_data *pdata = sr_info->pdev->dev.platform_data; | ||
252 | struct resource *mem; | ||
253 | int ret = 0; | ||
254 | |||
255 | if (sr_class->class_type == SR_CLASS2 && | ||
256 | sr_class->notify_flags && sr_info->irq) { | ||
257 | |||
258 | name = kzalloc(SMARTREFLEX_NAME_LEN + 1, GFP_KERNEL); | ||
259 | strcpy(name, "sr_"); | ||
260 | strcat(name, sr_info->voltdm->name); | ||
261 | ret = request_irq(sr_info->irq, sr_interrupt, | ||
262 | 0, name, (void *)sr_info); | ||
263 | if (ret) | ||
264 | goto error; | ||
265 | } | ||
266 | |||
267 | if (pdata && pdata->enable_on_init) | ||
268 | sr_start_vddautocomp(sr_info); | ||
269 | |||
270 | return ret; | ||
271 | |||
272 | error: | ||
273 | iounmap(sr_info->base); | ||
274 | mem = platform_get_resource(sr_info->pdev, IORESOURCE_MEM, 0); | ||
275 | release_mem_region(mem->start, resource_size(mem)); | ||
276 | list_del(&sr_info->node); | ||
277 | dev_err(&sr_info->pdev->dev, "%s: ERROR in registering" | ||
278 | "interrupt handler. Smartreflex will" | ||
279 | "not function as desired\n", __func__); | ||
280 | kfree(sr_info); | ||
281 | return ret; | ||
282 | } | ||
283 | |||
284 | static void sr_v1_disable(struct omap_sr *sr) | ||
285 | { | ||
286 | int timeout = 0; | ||
287 | |||
288 | /* Enable MCUDisableAcknowledge interrupt */ | ||
289 | sr_modify_reg(sr, ERRCONFIG_V1, | ||
290 | ERRCONFIG_MCUDISACKINTEN, ERRCONFIG_MCUDISACKINTEN); | ||
291 | |||
292 | /* SRCONFIG - disable SR */ | ||
293 | sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0); | ||
294 | |||
295 | /* Disable all other SR interrupts and clear the status */ | ||
296 | sr_modify_reg(sr, ERRCONFIG_V1, | ||
297 | (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN | | ||
298 | ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_VPBOUNDINTEN_V1), | ||
299 | (ERRCONFIG_MCUACCUMINTST | ERRCONFIG_MCUVALIDINTST | | ||
300 | ERRCONFIG_MCUBOUNDINTST | | ||
301 | ERRCONFIG_VPBOUNDINTST_V1)); | ||
302 | |||
303 | /* | ||
304 | * Wait for SR to be disabled. | ||
305 | * wait until ERRCONFIG.MCUDISACKINTST = 1. Typical latency is 1us. | ||
306 | */ | ||
307 | omap_test_timeout((sr_read_reg(sr, ERRCONFIG_V1) & | ||
308 | ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT, | ||
309 | timeout); | ||
310 | |||
311 | if (timeout >= SR_DISABLE_TIMEOUT) | ||
312 | dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n", | ||
313 | __func__); | ||
314 | |||
315 | /* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */ | ||
316 | sr_modify_reg(sr, ERRCONFIG_V1, ERRCONFIG_MCUDISACKINTEN, | ||
317 | ERRCONFIG_MCUDISACKINTST); | ||
318 | } | ||
319 | |||
320 | static void sr_v2_disable(struct omap_sr *sr) | ||
321 | { | ||
322 | int timeout = 0; | ||
323 | |||
324 | /* Enable MCUDisableAcknowledge interrupt */ | ||
325 | sr_write_reg(sr, IRQENABLE_SET, IRQENABLE_MCUDISABLEACKINT); | ||
326 | |||
327 | /* SRCONFIG - disable SR */ | ||
328 | sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0); | ||
329 | |||
330 | /* Disable all other SR interrupts and clear the status */ | ||
331 | sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2, | ||
332 | ERRCONFIG_VPBOUNDINTST_V2); | ||
333 | sr_write_reg(sr, IRQENABLE_CLR, (IRQENABLE_MCUACCUMINT | | ||
334 | IRQENABLE_MCUVALIDINT | | ||
335 | IRQENABLE_MCUBOUNDSINT)); | ||
336 | sr_write_reg(sr, IRQSTATUS, (IRQSTATUS_MCUACCUMINT | | ||
337 | IRQSTATUS_MCVALIDINT | | ||
338 | IRQSTATUS_MCBOUNDSINT)); | ||
339 | |||
340 | /* | ||
341 | * Wait for SR to be disabled. | ||
342 | * wait until IRQSTATUS.MCUDISACKINTST = 1. Typical latency is 1us. | ||
343 | */ | ||
344 | omap_test_timeout((sr_read_reg(sr, IRQSTATUS) & | ||
345 | IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT, | ||
346 | timeout); | ||
347 | |||
348 | if (timeout >= SR_DISABLE_TIMEOUT) | ||
349 | dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n", | ||
350 | __func__); | ||
351 | |||
352 | /* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */ | ||
353 | sr_write_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUDISABLEACKINT); | ||
354 | sr_write_reg(sr, IRQSTATUS, IRQSTATUS_MCUDISABLEACKINT); | ||
355 | } | ||
356 | |||
357 | static u32 sr_retrieve_nvalue(struct omap_sr *sr, u32 efuse_offs) | ||
358 | { | ||
359 | int i; | ||
360 | |||
361 | if (!sr->nvalue_table) { | ||
362 | dev_warn(&sr->pdev->dev, "%s: Missing ntarget value table\n", | ||
363 | __func__); | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | for (i = 0; i < sr->nvalue_count; i++) { | ||
368 | if (sr->nvalue_table[i].efuse_offs == efuse_offs) | ||
369 | return sr->nvalue_table[i].nvalue; | ||
370 | } | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | /* Public Functions */ | ||
376 | |||
377 | /** | ||
378 | * sr_configure_errgen() - Configures the smrtreflex to perform AVS using the | ||
379 | * error generator module. | ||
380 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. | ||
381 | * | ||
382 | * This API is to be called from the smartreflex class driver to | ||
383 | * configure the error generator module inside the smartreflex module. | ||
384 | * SR settings if using the ERROR module inside Smartreflex. | ||
385 | * SR CLASS 3 by default uses only the ERROR module where as | ||
386 | * SR CLASS 2 can choose between ERROR module and MINMAXAVG | ||
387 | * module. Returns 0 on success and error value in case of failure. | ||
388 | */ | ||
389 | int sr_configure_errgen(struct voltagedomain *voltdm) | ||
390 | { | ||
391 | u32 sr_config, sr_errconfig, errconfig_offs, vpboundint_en; | ||
392 | u32 vpboundint_st, senp_en = 0, senn_en = 0; | ||
393 | u8 senp_shift, senn_shift; | ||
394 | struct omap_sr *sr = _sr_lookup(voltdm); | ||
395 | |||
396 | if (IS_ERR(sr)) { | ||
397 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
398 | __func__, voltdm->name); | ||
399 | return -EINVAL; | ||
400 | } | ||
401 | |||
402 | if (!sr->clk_length) | ||
403 | sr_set_clk_length(sr); | ||
404 | |||
405 | senp_en = sr->senp_mod; | ||
406 | senn_en = sr->senn_mod; | ||
407 | |||
408 | sr_config = (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) | | ||
409 | SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN; | ||
410 | |||
411 | if (sr->ip_type == SR_TYPE_V1) { | ||
412 | sr_config |= SRCONFIG_DELAYCTRL; | ||
413 | senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT; | ||
414 | senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT; | ||
415 | errconfig_offs = ERRCONFIG_V1; | ||
416 | vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1; | ||
417 | vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1; | ||
418 | } else if (sr->ip_type == SR_TYPE_V2) { | ||
419 | senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT; | ||
420 | senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT; | ||
421 | errconfig_offs = ERRCONFIG_V2; | ||
422 | vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2; | ||
423 | vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2; | ||
424 | } else { | ||
425 | dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex" | ||
426 | "module without specifying the ip\n", __func__); | ||
427 | return -EINVAL; | ||
428 | } | ||
429 | |||
430 | sr_config |= ((senn_en << senn_shift) | (senp_en << senp_shift)); | ||
431 | sr_write_reg(sr, SRCONFIG, sr_config); | ||
432 | sr_errconfig = (sr->err_weight << ERRCONFIG_ERRWEIGHT_SHIFT) | | ||
433 | (sr->err_maxlimit << ERRCONFIG_ERRMAXLIMIT_SHIFT) | | ||
434 | (sr->err_minlimit << ERRCONFIG_ERRMINLIMIT_SHIFT); | ||
435 | sr_modify_reg(sr, errconfig_offs, (SR_ERRWEIGHT_MASK | | ||
436 | SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK), | ||
437 | sr_errconfig); | ||
438 | |||
439 | /* Enabling the interrupts if the ERROR module is used */ | ||
440 | sr_modify_reg(sr, errconfig_offs, | ||
441 | vpboundint_en, (vpboundint_en | vpboundint_st)); | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | /** | ||
447 | * sr_configure_minmax() - Configures the smrtreflex to perform AVS using the | ||
448 | * minmaxavg module. | ||
449 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. | ||
450 | * | ||
451 | * This API is to be called from the smartreflex class driver to | ||
452 | * configure the minmaxavg module inside the smartreflex module. | ||
453 | * SR settings if using the ERROR module inside Smartreflex. | ||
454 | * SR CLASS 3 by default uses only the ERROR module where as | ||
455 | * SR CLASS 2 can choose between ERROR module and MINMAXAVG | ||
456 | * module. Returns 0 on success and error value in case of failure. | ||
457 | */ | ||
458 | int sr_configure_minmax(struct voltagedomain *voltdm) | ||
459 | { | ||
460 | u32 sr_config, sr_avgwt; | ||
461 | u32 senp_en = 0, senn_en = 0; | ||
462 | u8 senp_shift, senn_shift; | ||
463 | struct omap_sr *sr = _sr_lookup(voltdm); | ||
464 | |||
465 | if (IS_ERR(sr)) { | ||
466 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
467 | __func__, voltdm->name); | ||
468 | return -EINVAL; | ||
469 | } | ||
470 | |||
471 | if (!sr->clk_length) | ||
472 | sr_set_clk_length(sr); | ||
473 | |||
474 | senp_en = sr->senp_mod; | ||
475 | senn_en = sr->senn_mod; | ||
476 | |||
477 | sr_config = (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) | | ||
478 | SRCONFIG_SENENABLE | | ||
479 | (sr->accum_data << SRCONFIG_ACCUMDATA_SHIFT); | ||
480 | |||
481 | if (sr->ip_type == SR_TYPE_V1) { | ||
482 | sr_config |= SRCONFIG_DELAYCTRL; | ||
483 | senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT; | ||
484 | senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT; | ||
485 | } else if (sr->ip_type == SR_TYPE_V2) { | ||
486 | senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT; | ||
487 | senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT; | ||
488 | } else { | ||
489 | dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex" | ||
490 | "module without specifying the ip\n", __func__); | ||
491 | return -EINVAL; | ||
492 | } | ||
493 | |||
494 | sr_config |= ((senn_en << senn_shift) | (senp_en << senp_shift)); | ||
495 | sr_write_reg(sr, SRCONFIG, sr_config); | ||
496 | sr_avgwt = (sr->senp_avgweight << AVGWEIGHT_SENPAVGWEIGHT_SHIFT) | | ||
497 | (sr->senn_avgweight << AVGWEIGHT_SENNAVGWEIGHT_SHIFT); | ||
498 | sr_write_reg(sr, AVGWEIGHT, sr_avgwt); | ||
499 | |||
500 | /* | ||
501 | * Enabling the interrupts if MINMAXAVG module is used. | ||
502 | * TODO: check if all the interrupts are mandatory | ||
503 | */ | ||
504 | if (sr->ip_type == SR_TYPE_V1) { | ||
505 | sr_modify_reg(sr, ERRCONFIG_V1, | ||
506 | (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN | | ||
507 | ERRCONFIG_MCUBOUNDINTEN), | ||
508 | (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUACCUMINTST | | ||
509 | ERRCONFIG_MCUVALIDINTEN | ERRCONFIG_MCUVALIDINTST | | ||
510 | ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_MCUBOUNDINTST)); | ||
511 | } else if (sr->ip_type == SR_TYPE_V2) { | ||
512 | sr_write_reg(sr, IRQSTATUS, | ||
513 | IRQSTATUS_MCUACCUMINT | IRQSTATUS_MCVALIDINT | | ||
514 | IRQSTATUS_MCBOUNDSINT | IRQSTATUS_MCUDISABLEACKINT); | ||
515 | sr_write_reg(sr, IRQENABLE_SET, | ||
516 | IRQENABLE_MCUACCUMINT | IRQENABLE_MCUVALIDINT | | ||
517 | IRQENABLE_MCUBOUNDSINT | IRQENABLE_MCUDISABLEACKINT); | ||
518 | } | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * sr_enable() - Enables the smartreflex module. | ||
525 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. | ||
526 | * @volt: The voltage at which the Voltage domain associated with | ||
527 | * the smartreflex module is operating at. | ||
528 | * This is required only to program the correct Ntarget value. | ||
529 | * | ||
530 | * This API is to be called from the smartreflex class driver to | ||
531 | * enable a smartreflex module. Returns 0 on success. Returns error | ||
532 | * value if the voltage passed is wrong or if ntarget value is wrong. | ||
533 | */ | ||
534 | int sr_enable(struct voltagedomain *voltdm, unsigned long volt) | ||
535 | { | ||
536 | u32 nvalue_reciprocal; | ||
537 | struct omap_volt_data *volt_data; | ||
538 | struct omap_sr *sr = _sr_lookup(voltdm); | ||
539 | int ret; | ||
540 | |||
541 | if (IS_ERR(sr)) { | ||
542 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
543 | __func__, voltdm->name); | ||
544 | return -EINVAL; | ||
545 | } | ||
546 | |||
547 | volt_data = omap_voltage_get_voltdata(sr->voltdm, volt); | ||
548 | |||
549 | if (IS_ERR(volt_data)) { | ||
550 | dev_warn(&sr->pdev->dev, "%s: Unable to get voltage table" | ||
551 | "for nominal voltage %ld\n", __func__, volt); | ||
552 | return -ENODATA; | ||
553 | } | ||
554 | |||
555 | nvalue_reciprocal = sr_retrieve_nvalue(sr, volt_data->sr_efuse_offs); | ||
556 | |||
557 | if (!nvalue_reciprocal) { | ||
558 | dev_warn(&sr->pdev->dev, "%s: NVALUE = 0 at voltage %ld\n", | ||
559 | __func__, volt); | ||
560 | return -ENODATA; | ||
561 | } | ||
562 | |||
563 | /* errminlimit is opp dependent and hence linked to voltage */ | ||
564 | sr->err_minlimit = volt_data->sr_errminlimit; | ||
565 | |||
566 | pm_runtime_get_sync(&sr->pdev->dev); | ||
567 | |||
568 | /* Check if SR is already enabled. If yes do nothing */ | ||
569 | if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) | ||
570 | return 0; | ||
571 | |||
572 | /* Configure SR */ | ||
573 | ret = sr_class->configure(voltdm); | ||
574 | if (ret) | ||
575 | return ret; | ||
576 | |||
577 | sr_write_reg(sr, NVALUERECIPROCAL, nvalue_reciprocal); | ||
578 | |||
579 | /* SRCONFIG - enable SR */ | ||
580 | sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE); | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | /** | ||
585 | * sr_disable() - Disables the smartreflex module. | ||
586 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. | ||
587 | * | ||
588 | * This API is to be called from the smartreflex class driver to | ||
589 | * disable a smartreflex module. | ||
590 | */ | ||
591 | void sr_disable(struct voltagedomain *voltdm) | ||
592 | { | ||
593 | struct omap_sr *sr = _sr_lookup(voltdm); | ||
594 | |||
595 | if (IS_ERR(sr)) { | ||
596 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
597 | __func__, voltdm->name); | ||
598 | return; | ||
599 | } | ||
600 | |||
601 | /* Check if SR clocks are already disabled. If yes do nothing */ | ||
602 | if (pm_runtime_suspended(&sr->pdev->dev)) | ||
603 | return; | ||
604 | |||
605 | /* | ||
606 | * Disable SR if only it is indeed enabled. Else just | ||
607 | * disable the clocks. | ||
608 | */ | ||
609 | if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) { | ||
610 | if (sr->ip_type == SR_TYPE_V1) | ||
611 | sr_v1_disable(sr); | ||
612 | else if (sr->ip_type == SR_TYPE_V2) | ||
613 | sr_v2_disable(sr); | ||
614 | } | ||
615 | |||
616 | pm_runtime_put_sync(&sr->pdev->dev); | ||
617 | } | ||
618 | |||
619 | /** | ||
620 | * sr_register_class() - API to register a smartreflex class parameters. | ||
621 | * @class_data: The structure containing various sr class specific data. | ||
622 | * | ||
623 | * This API is to be called by the smartreflex class driver to register itself | ||
624 | * with the smartreflex driver during init. Returns 0 on success else the | ||
625 | * error value. | ||
626 | */ | ||
627 | int sr_register_class(struct omap_sr_class_data *class_data) | ||
628 | { | ||
629 | struct omap_sr *sr_info; | ||
630 | |||
631 | if (!class_data) { | ||
632 | pr_warning("%s:, Smartreflex class data passed is NULL\n", | ||
633 | __func__); | ||
634 | return -EINVAL; | ||
635 | } | ||
636 | |||
637 | if (sr_class) { | ||
638 | pr_warning("%s: Smartreflex class driver already registered\n", | ||
639 | __func__); | ||
640 | return -EBUSY; | ||
641 | } | ||
642 | |||
643 | sr_class = class_data; | ||
644 | |||
645 | /* | ||
646 | * Call into late init to do intializations that require | ||
647 | * both sr driver and sr class driver to be initiallized. | ||
648 | */ | ||
649 | list_for_each_entry(sr_info, &sr_list, node) | ||
650 | sr_late_init(sr_info); | ||
651 | |||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | /** | ||
656 | * omap_sr_enable() - API to enable SR clocks and to call into the | ||
657 | * registered smartreflex class enable API. | ||
658 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. | ||
659 | * | ||
660 | * This API is to be called from the kernel in order to enable | ||
661 | * a particular smartreflex module. This API will do the initial | ||
662 | * configurations to turn on the smartreflex module and in turn call | ||
663 | * into the registered smartreflex class enable API. | ||
664 | */ | ||
665 | void omap_sr_enable(struct voltagedomain *voltdm) | ||
666 | { | ||
667 | struct omap_sr *sr = _sr_lookup(voltdm); | ||
668 | |||
669 | if (IS_ERR(sr)) { | ||
670 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
671 | __func__, voltdm->name); | ||
672 | return; | ||
673 | } | ||
674 | |||
675 | if (!sr->autocomp_active) | ||
676 | return; | ||
677 | |||
678 | if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) { | ||
679 | dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not" | ||
680 | "registered\n", __func__); | ||
681 | return; | ||
682 | } | ||
683 | |||
684 | sr_class->enable(voltdm); | ||
685 | } | ||
686 | |||
687 | /** | ||
688 | * omap_sr_disable() - API to disable SR without resetting the voltage | ||
689 | * processor voltage | ||
690 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. | ||
691 | * | ||
692 | * This API is to be called from the kernel in order to disable | ||
693 | * a particular smartreflex module. This API will in turn call | ||
694 | * into the registered smartreflex class disable API. This API will tell | ||
695 | * the smartreflex class disable not to reset the VP voltage after | ||
696 | * disabling smartreflex. | ||
697 | */ | ||
698 | void omap_sr_disable(struct voltagedomain *voltdm) | ||
699 | { | ||
700 | struct omap_sr *sr = _sr_lookup(voltdm); | ||
701 | |||
702 | if (IS_ERR(sr)) { | ||
703 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
704 | __func__, voltdm->name); | ||
705 | return; | ||
706 | } | ||
707 | |||
708 | if (!sr->autocomp_active) | ||
709 | return; | ||
710 | |||
711 | if (!sr_class || !(sr_class->disable)) { | ||
712 | dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not" | ||
713 | "registered\n", __func__); | ||
714 | return; | ||
715 | } | ||
716 | |||
717 | sr_class->disable(voltdm, 0); | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * omap_sr_disable_reset_volt() - API to disable SR and reset the | ||
722 | * voltage processor voltage | ||
723 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. | ||
724 | * | ||
725 | * This API is to be called from the kernel in order to disable | ||
726 | * a particular smartreflex module. This API will in turn call | ||
727 | * into the registered smartreflex class disable API. This API will tell | ||
728 | * the smartreflex class disable to reset the VP voltage after | ||
729 | * disabling smartreflex. | ||
730 | */ | ||
731 | void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) | ||
732 | { | ||
733 | struct omap_sr *sr = _sr_lookup(voltdm); | ||
734 | |||
735 | if (IS_ERR(sr)) { | ||
736 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
737 | __func__, voltdm->name); | ||
738 | return; | ||
739 | } | ||
740 | |||
741 | if (!sr->autocomp_active) | ||
742 | return; | ||
743 | |||
744 | if (!sr_class || !(sr_class->disable)) { | ||
745 | dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not" | ||
746 | "registered\n", __func__); | ||
747 | return; | ||
748 | } | ||
749 | |||
750 | sr_class->disable(voltdm, 1); | ||
751 | } | ||
752 | |||
753 | /** | ||
754 | * omap_sr_register_pmic() - API to register pmic specific info. | ||
755 | * @pmic_data: The structure containing pmic specific data. | ||
756 | * | ||
757 | * This API is to be called from the PMIC specific code to register with | ||
758 | * smartreflex driver pmic specific info. Currently the only info required | ||
759 | * is the smartreflex init on the PMIC side. | ||
760 | */ | ||
761 | void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data) | ||
762 | { | ||
763 | if (!pmic_data) { | ||
764 | pr_warning("%s: Trying to register NULL PMIC data structure" | ||
765 | "with smartreflex\n", __func__); | ||
766 | return; | ||
767 | } | ||
768 | |||
769 | sr_pmic_data = pmic_data; | ||
770 | } | ||
771 | |||
772 | /* PM Debug Fs enteries to enable disable smartreflex. */ | ||
773 | static int omap_sr_autocomp_show(void *data, u64 *val) | ||
774 | { | ||
775 | struct omap_sr *sr_info = (struct omap_sr *) data; | ||
776 | |||
777 | if (!sr_info) { | ||
778 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
779 | __func__, sr_info->voltdm->name); | ||
780 | return -EINVAL; | ||
781 | } | ||
782 | |||
783 | *val = sr_info->autocomp_active; | ||
784 | |||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | static int omap_sr_autocomp_store(void *data, u64 val) | ||
789 | { | ||
790 | struct omap_sr *sr_info = (struct omap_sr *) data; | ||
791 | |||
792 | if (!sr_info) { | ||
793 | pr_warning("%s: omap_sr struct for sr_%s not found\n", | ||
794 | __func__, sr_info->voltdm->name); | ||
795 | return -EINVAL; | ||
796 | } | ||
797 | |||
798 | /* Sanity check */ | ||
799 | if (val && (val != 1)) { | ||
800 | pr_warning("%s: Invalid argument %lld\n", __func__, val); | ||
801 | return -EINVAL; | ||
802 | } | ||
803 | |||
804 | if (!val) | ||
805 | sr_stop_vddautocomp(sr_info); | ||
806 | else | ||
807 | sr_start_vddautocomp(sr_info); | ||
808 | |||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | DEFINE_SIMPLE_ATTRIBUTE(pm_sr_fops, omap_sr_autocomp_show, | ||
813 | omap_sr_autocomp_store, "%llu\n"); | ||
814 | |||
815 | static int __init omap_sr_probe(struct platform_device *pdev) | ||
816 | { | ||
817 | struct omap_sr *sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL); | ||
818 | struct omap_sr_data *pdata = pdev->dev.platform_data; | ||
819 | struct resource *mem, *irq; | ||
820 | struct dentry *vdd_dbg_dir, *dbg_dir; | ||
821 | int ret = 0; | ||
822 | |||
823 | if (!sr_info) { | ||
824 | dev_err(&pdev->dev, "%s: unable to allocate sr_info\n", | ||
825 | __func__); | ||
826 | return -ENOMEM; | ||
827 | } | ||
828 | |||
829 | if (!pdata) { | ||
830 | dev_err(&pdev->dev, "%s: platform data missing\n", __func__); | ||
831 | return -EINVAL; | ||
832 | } | ||
833 | |||
834 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
835 | if (!mem) { | ||
836 | dev_err(&pdev->dev, "%s: no mem resource\n", __func__); | ||
837 | ret = -ENODEV; | ||
838 | goto err_free_devinfo; | ||
839 | } | ||
840 | |||
841 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
842 | |||
843 | pm_runtime_enable(&pdev->dev); | ||
844 | |||
845 | sr_info->pdev = pdev; | ||
846 | sr_info->srid = pdev->id; | ||
847 | sr_info->voltdm = pdata->voltdm; | ||
848 | sr_info->nvalue_table = pdata->nvalue_table; | ||
849 | sr_info->nvalue_count = pdata->nvalue_count; | ||
850 | sr_info->senn_mod = pdata->senn_mod; | ||
851 | sr_info->senp_mod = pdata->senp_mod; | ||
852 | sr_info->autocomp_active = false; | ||
853 | sr_info->ip_type = pdata->ip_type; | ||
854 | sr_info->base = ioremap(mem->start, resource_size(mem)); | ||
855 | if (!sr_info->base) { | ||
856 | dev_err(&pdev->dev, "%s: ioremap fail\n", __func__); | ||
857 | ret = -ENOMEM; | ||
858 | goto err_release_region; | ||
859 | } | ||
860 | |||
861 | if (irq) | ||
862 | sr_info->irq = irq->start; | ||
863 | |||
864 | sr_set_clk_length(sr_info); | ||
865 | sr_set_regfields(sr_info); | ||
866 | |||
867 | list_add(&sr_info->node, &sr_list); | ||
868 | |||
869 | /* | ||
870 | * Call into late init to do intializations that require | ||
871 | * both sr driver and sr class driver to be initiallized. | ||
872 | */ | ||
873 | if (sr_class) { | ||
874 | ret = sr_late_init(sr_info); | ||
875 | if (ret) { | ||
876 | pr_warning("%s: Error in SR late init\n", __func__); | ||
877 | return ret; | ||
878 | } | ||
879 | } | ||
880 | |||
881 | dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__); | ||
882 | |||
883 | /* | ||
884 | * If the voltage domain debugfs directory is not created, do | ||
885 | * not try to create rest of the debugfs entries. | ||
886 | */ | ||
887 | vdd_dbg_dir = omap_voltage_get_dbgdir(sr_info->voltdm); | ||
888 | if (!vdd_dbg_dir) | ||
889 | return -EINVAL; | ||
890 | |||
891 | dbg_dir = debugfs_create_dir("smartreflex", vdd_dbg_dir); | ||
892 | if (IS_ERR(dbg_dir)) { | ||
893 | dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n", | ||
894 | __func__); | ||
895 | return PTR_ERR(dbg_dir); | ||
896 | } | ||
897 | |||
898 | (void) debugfs_create_file("autocomp", S_IRUGO | S_IWUGO, dbg_dir, | ||
899 | (void *)sr_info, &pm_sr_fops); | ||
900 | |||
901 | return ret; | ||
902 | |||
903 | err_release_region: | ||
904 | release_mem_region(mem->start, resource_size(mem)); | ||
905 | err_free_devinfo: | ||
906 | kfree(sr_info); | ||
907 | |||
908 | return ret; | ||
909 | } | ||
910 | |||
911 | static int __devexit omap_sr_remove(struct platform_device *pdev) | ||
912 | { | ||
913 | struct omap_sr_data *pdata = pdev->dev.platform_data; | ||
914 | struct omap_sr *sr_info; | ||
915 | struct resource *mem; | ||
916 | |||
917 | if (!pdata) { | ||
918 | dev_err(&pdev->dev, "%s: platform data missing\n", __func__); | ||
919 | return -EINVAL; | ||
920 | } | ||
921 | |||
922 | sr_info = _sr_lookup(pdata->voltdm); | ||
923 | if (!sr_info) { | ||
924 | dev_warn(&pdev->dev, "%s: omap_sr struct not found\n", | ||
925 | __func__); | ||
926 | return -EINVAL; | ||
927 | } | ||
928 | |||
929 | if (sr_info->autocomp_active) | ||
930 | sr_stop_vddautocomp(sr_info); | ||
931 | |||
932 | list_del(&sr_info->node); | ||
933 | iounmap(sr_info->base); | ||
934 | kfree(sr_info); | ||
935 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
936 | release_mem_region(mem->start, resource_size(mem)); | ||
937 | |||
938 | return 0; | ||
939 | } | ||
940 | |||
941 | static struct platform_driver smartreflex_driver = { | ||
942 | .remove = omap_sr_remove, | ||
943 | .driver = { | ||
944 | .name = "smartreflex", | ||
945 | }, | ||
946 | }; | ||
947 | |||
948 | static int __init sr_init(void) | ||
949 | { | ||
950 | int ret = 0; | ||
951 | |||
952 | /* | ||
953 | * sr_init is a late init. If by then a pmic specific API is not | ||
954 | * registered either there is no need for anything to be done on | ||
955 | * the PMIC side or somebody has forgotten to register a PMIC | ||
956 | * handler. Warn for the second condition. | ||
957 | */ | ||
958 | if (sr_pmic_data && sr_pmic_data->sr_pmic_init) | ||
959 | sr_pmic_data->sr_pmic_init(); | ||
960 | else | ||
961 | pr_warning("%s: No PMIC hook to init smartreflex\n", __func__); | ||
962 | |||
963 | ret = platform_driver_probe(&smartreflex_driver, omap_sr_probe); | ||
964 | if (ret) { | ||
965 | pr_err("%s: platform driver register failed for SR\n", | ||
966 | __func__); | ||
967 | return ret; | ||
968 | } | ||
969 | |||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | static void __exit sr_exit(void) | ||
974 | { | ||
975 | platform_driver_unregister(&smartreflex_driver); | ||
976 | } | ||
977 | late_initcall(sr_init); | ||
978 | module_exit(sr_exit); | ||
979 | |||
980 | MODULE_DESCRIPTION("OMAP Smartreflex Driver"); | ||
981 | MODULE_LICENSE("GPL"); | ||
982 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
983 | MODULE_AUTHOR("Texas Instruments Inc"); | ||
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 5e63e5069e0d..f1673fb96fe9 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig | |||
@@ -35,6 +35,28 @@ config OMAP_DEBUG_LEDS | |||
35 | depends on OMAP_DEBUG_DEVICES | 35 | depends on OMAP_DEBUG_DEVICES |
36 | default y if LEDS_CLASS | 36 | default y if LEDS_CLASS |
37 | 37 | ||
38 | config OMAP_SMARTREFLEX | ||
39 | bool "SmartReflex support" | ||
40 | depends on ARCH_OMAP3 && PM | ||
41 | help | ||
42 | Say Y if you want to enable SmartReflex. | ||
43 | |||
44 | SmartReflex can perform continuous dynamic voltage | ||
45 | scaling around the nominal operating point voltage | ||
46 | according to silicon characteristics and operating | ||
47 | conditions. Enabling SmartReflex reduces power | ||
48 | consumption. | ||
49 | |||
50 | Please note, that by default SmartReflex is only | ||
51 | initialized. To enable the automatic voltage | ||
52 | compensation for vdd mpu and vdd core from user space, | ||
53 | user must write 1 to | ||
54 | /debug/voltage/vdd_<X>/smartreflex/autocomp, | ||
55 | where X is mpu or core for OMAP3. | ||
56 | Optionallly autocompensation can be enabled in the kernel | ||
57 | by default during system init via the enable_on_init flag | ||
58 | which an be passed as platform data to the smartreflex driver. | ||
59 | |||
38 | config OMAP_RESET_CLOCKS | 60 | config OMAP_RESET_CLOCKS |
39 | bool "Reset unused clocks during boot" | 61 | bool "Reset unused clocks during boot" |
40 | depends on ARCH_OMAP | 62 | depends on ARCH_OMAP |
diff --git a/arch/arm/plat-omap/include/plat/smartreflex.h b/arch/arm/plat-omap/include/plat/smartreflex.h new file mode 100644 index 000000000000..6568c885f37a --- /dev/null +++ b/arch/arm/plat-omap/include/plat/smartreflex.h | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | * OMAP Smartreflex Defines and Routines | ||
3 | * | ||
4 | * Author: Thara Gopinath <thara@ti.com> | ||
5 | * | ||
6 | * Copyright (C) 2010 Texas Instruments, Inc. | ||
7 | * Thara Gopinath <thara@ti.com> | ||
8 | * | ||
9 | * Copyright (C) 2008 Nokia Corporation | ||
10 | * Kalle Jokiniemi | ||
11 | * | ||
12 | * Copyright (C) 2007 Texas Instruments, Inc. | ||
13 | * Lesly A M <x0080970@ti.com> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License version 2 as | ||
17 | * published by the Free Software Foundation. | ||
18 | */ | ||
19 | |||
20 | #ifndef __ASM_ARM_OMAP_SMARTREFLEX_H | ||
21 | #define __ASM_ARM_OMAP_SMARTREFLEX_H | ||
22 | |||
23 | #include <linux/platform_device.h> | ||
24 | #include <plat/voltage.h> | ||
25 | |||
26 | /* | ||
27 | * Different Smartreflex IPs version. The v1 is the 65nm version used in | ||
28 | * OMAP3430. The v2 is the update for the 45nm version of the IP | ||
29 | * used in OMAP3630 and OMAP4430 | ||
30 | */ | ||
31 | #define SR_TYPE_V1 1 | ||
32 | #define SR_TYPE_V2 2 | ||
33 | |||
34 | /* SMART REFLEX REG ADDRESS OFFSET */ | ||
35 | #define SRCONFIG 0x00 | ||
36 | #define SRSTATUS 0x04 | ||
37 | #define SENVAL 0x08 | ||
38 | #define SENMIN 0x0C | ||
39 | #define SENMAX 0x10 | ||
40 | #define SENAVG 0x14 | ||
41 | #define AVGWEIGHT 0x18 | ||
42 | #define NVALUERECIPROCAL 0x1c | ||
43 | #define SENERROR_V1 0x20 | ||
44 | #define ERRCONFIG_V1 0x24 | ||
45 | #define IRQ_EOI 0x20 | ||
46 | #define IRQSTATUS_RAW 0x24 | ||
47 | #define IRQSTATUS 0x28 | ||
48 | #define IRQENABLE_SET 0x2C | ||
49 | #define IRQENABLE_CLR 0x30 | ||
50 | #define SENERROR_V2 0x34 | ||
51 | #define ERRCONFIG_V2 0x38 | ||
52 | |||
53 | /* Bit/Shift Positions */ | ||
54 | |||
55 | /* SRCONFIG */ | ||
56 | #define SRCONFIG_ACCUMDATA_SHIFT 22 | ||
57 | #define SRCONFIG_SRCLKLENGTH_SHIFT 12 | ||
58 | #define SRCONFIG_SENNENABLE_V1_SHIFT 5 | ||
59 | #define SRCONFIG_SENPENABLE_V1_SHIFT 3 | ||
60 | #define SRCONFIG_SENNENABLE_V2_SHIFT 1 | ||
61 | #define SRCONFIG_SENPENABLE_V2_SHIFT 0 | ||
62 | #define SRCONFIG_CLKCTRL_SHIFT 0 | ||
63 | |||
64 | #define SRCONFIG_ACCUMDATA_MASK (0x3ff << 22) | ||
65 | |||
66 | #define SRCONFIG_SRENABLE BIT(11) | ||
67 | #define SRCONFIG_SENENABLE BIT(10) | ||
68 | #define SRCONFIG_ERRGEN_EN BIT(9) | ||
69 | #define SRCONFIG_MINMAXAVG_EN BIT(8) | ||
70 | #define SRCONFIG_DELAYCTRL BIT(2) | ||
71 | |||
72 | /* AVGWEIGHT */ | ||
73 | #define AVGWEIGHT_SENPAVGWEIGHT_SHIFT 2 | ||
74 | #define AVGWEIGHT_SENNAVGWEIGHT_SHIFT 0 | ||
75 | |||
76 | /* NVALUERECIPROCAL */ | ||
77 | #define NVALUERECIPROCAL_SENPGAIN_SHIFT 20 | ||
78 | #define NVALUERECIPROCAL_SENNGAIN_SHIFT 16 | ||
79 | #define NVALUERECIPROCAL_RNSENP_SHIFT 8 | ||
80 | #define NVALUERECIPROCAL_RNSENN_SHIFT 0 | ||
81 | |||
82 | /* ERRCONFIG */ | ||
83 | #define ERRCONFIG_ERRWEIGHT_SHIFT 16 | ||
84 | #define ERRCONFIG_ERRMAXLIMIT_SHIFT 8 | ||
85 | #define ERRCONFIG_ERRMINLIMIT_SHIFT 0 | ||
86 | |||
87 | #define SR_ERRWEIGHT_MASK (0x07 << 16) | ||
88 | #define SR_ERRMAXLIMIT_MASK (0xff << 8) | ||
89 | #define SR_ERRMINLIMIT_MASK (0xff << 0) | ||
90 | |||
91 | #define ERRCONFIG_VPBOUNDINTEN_V1 BIT(31) | ||
92 | #define ERRCONFIG_VPBOUNDINTST_V1 BIT(30) | ||
93 | #define ERRCONFIG_MCUACCUMINTEN BIT(29) | ||
94 | #define ERRCONFIG_MCUACCUMINTST BIT(28) | ||
95 | #define ERRCONFIG_MCUVALIDINTEN BIT(27) | ||
96 | #define ERRCONFIG_MCUVALIDINTST BIT(26) | ||
97 | #define ERRCONFIG_MCUBOUNDINTEN BIT(25) | ||
98 | #define ERRCONFIG_MCUBOUNDINTST BIT(24) | ||
99 | #define ERRCONFIG_MCUDISACKINTEN BIT(23) | ||
100 | #define ERRCONFIG_VPBOUNDINTST_V2 BIT(23) | ||
101 | #define ERRCONFIG_MCUDISACKINTST BIT(22) | ||
102 | #define ERRCONFIG_VPBOUNDINTEN_V2 BIT(22) | ||
103 | |||
104 | #define ERRCONFIG_STATUS_V1_MASK (ERRCONFIG_VPBOUNDINTST_V1 | \ | ||
105 | ERRCONFIG_MCUACCUMINTST | \ | ||
106 | ERRCONFIG_MCUVALIDINTST | \ | ||
107 | ERRCONFIG_MCUBOUNDINTST | \ | ||
108 | ERRCONFIG_MCUDISACKINTST) | ||
109 | /* IRQSTATUS */ | ||
110 | #define IRQSTATUS_MCUACCUMINT BIT(3) | ||
111 | #define IRQSTATUS_MCVALIDINT BIT(2) | ||
112 | #define IRQSTATUS_MCBOUNDSINT BIT(1) | ||
113 | #define IRQSTATUS_MCUDISABLEACKINT BIT(0) | ||
114 | |||
115 | /* IRQENABLE_SET and IRQENABLE_CLEAR */ | ||
116 | #define IRQENABLE_MCUACCUMINT BIT(3) | ||
117 | #define IRQENABLE_MCUVALIDINT BIT(2) | ||
118 | #define IRQENABLE_MCUBOUNDSINT BIT(1) | ||
119 | #define IRQENABLE_MCUDISABLEACKINT BIT(0) | ||
120 | |||
121 | /* Common Bit values */ | ||
122 | |||
123 | #define SRCLKLENGTH_12MHZ_SYSCLK 0x3c | ||
124 | #define SRCLKLENGTH_13MHZ_SYSCLK 0x41 | ||
125 | #define SRCLKLENGTH_19MHZ_SYSCLK 0x60 | ||
126 | #define SRCLKLENGTH_26MHZ_SYSCLK 0x82 | ||
127 | #define SRCLKLENGTH_38MHZ_SYSCLK 0xC0 | ||
128 | |||
129 | /* | ||
130 | * 3430 specific values. Maybe these should be passed from board file or | ||
131 | * pmic structures. | ||
132 | */ | ||
133 | #define OMAP3430_SR_ACCUMDATA 0x1f4 | ||
134 | |||
135 | #define OMAP3430_SR1_SENPAVGWEIGHT 0x03 | ||
136 | #define OMAP3430_SR1_SENNAVGWEIGHT 0x03 | ||
137 | |||
138 | #define OMAP3430_SR2_SENPAVGWEIGHT 0x01 | ||
139 | #define OMAP3430_SR2_SENNAVGWEIGHT 0x01 | ||
140 | |||
141 | #define OMAP3430_SR_ERRWEIGHT 0x04 | ||
142 | #define OMAP3430_SR_ERRMAXLIMIT 0x02 | ||
143 | |||
144 | /** | ||
145 | * struct omap_sr_pmic_data - Strucutre to be populated by pmic code to pass | ||
146 | * pmic specific info to smartreflex driver | ||
147 | * | ||
148 | * @sr_pmic_init: API to initialize smartreflex on the PMIC side. | ||
149 | */ | ||
150 | struct omap_sr_pmic_data { | ||
151 | void (*sr_pmic_init) (void); | ||
152 | }; | ||
153 | |||
154 | #ifdef CONFIG_OMAP_SMARTREFLEX | ||
155 | /* | ||
156 | * The smart reflex driver supports CLASS1 CLASS2 and CLASS3 SR. | ||
157 | * The smartreflex class driver should pass the class type. | ||
158 | * Should be used to populate the class_type field of the | ||
159 | * omap_smartreflex_class_data structure. | ||
160 | */ | ||
161 | #define SR_CLASS1 0x1 | ||
162 | #define SR_CLASS2 0x2 | ||
163 | #define SR_CLASS3 0x3 | ||
164 | |||
165 | /** | ||
166 | * struct omap_sr_class_data - Smartreflex class driver info | ||
167 | * | ||
168 | * @enable: API to enable a particular class smaartreflex. | ||
169 | * @disable: API to disable a particular class smartreflex. | ||
170 | * @configure: API to configure a particular class smartreflex. | ||
171 | * @notify: API to notify the class driver about an event in SR. | ||
172 | * Not needed for class3. | ||
173 | * @notify_flags: specify the events to be notified to the class driver | ||
174 | * @class_type: specify which smartreflex class. | ||
175 | * Can be used by the SR driver to take any class | ||
176 | * based decisions. | ||
177 | */ | ||
178 | struct omap_sr_class_data { | ||
179 | int (*enable)(struct voltagedomain *voltdm); | ||
180 | int (*disable)(struct voltagedomain *voltdm, int is_volt_reset); | ||
181 | int (*configure)(struct voltagedomain *voltdm); | ||
182 | int (*notify)(struct voltagedomain *voltdm, u32 status); | ||
183 | u8 notify_flags; | ||
184 | u8 class_type; | ||
185 | }; | ||
186 | |||
187 | /** | ||
188 | * struct omap_sr_nvalue_table - Smartreflex n-target value info | ||
189 | * | ||
190 | * @efuse_offs: The offset of the efuse where n-target values are stored. | ||
191 | * @nvalue: The n-target value. | ||
192 | */ | ||
193 | struct omap_sr_nvalue_table { | ||
194 | u32 efuse_offs; | ||
195 | u32 nvalue; | ||
196 | }; | ||
197 | |||
198 | /** | ||
199 | * struct omap_sr_data - Smartreflex platform data. | ||
200 | * | ||
201 | * @ip_type: Smartreflex IP type. | ||
202 | * @senp_mod: SENPENABLE value for the sr | ||
203 | * @senn_mod: SENNENABLE value for sr | ||
204 | * @nvalue_count: Number of distinct nvalues in the nvalue table | ||
205 | * @enable_on_init: whether this sr module needs to enabled at | ||
206 | * boot up or not. | ||
207 | * @nvalue_table: table containing the efuse offsets and nvalues | ||
208 | * corresponding to them. | ||
209 | * @voltdm: Pointer to the voltage domain associated with the SR | ||
210 | */ | ||
211 | struct omap_sr_data { | ||
212 | int ip_type; | ||
213 | u32 senp_mod; | ||
214 | u32 senn_mod; | ||
215 | int nvalue_count; | ||
216 | bool enable_on_init; | ||
217 | struct omap_sr_nvalue_table *nvalue_table; | ||
218 | struct voltagedomain *voltdm; | ||
219 | }; | ||
220 | |||
221 | /* Smartreflex module enable/disable interface */ | ||
222 | void omap_sr_enable(struct voltagedomain *voltdm); | ||
223 | void omap_sr_disable(struct voltagedomain *voltdm); | ||
224 | void omap_sr_disable_reset_volt(struct voltagedomain *voltdm); | ||
225 | |||
226 | /* API to register the pmic specific data with the smartreflex driver. */ | ||
227 | void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data); | ||
228 | |||
229 | /* Smartreflex driver hooks to be called from Smartreflex class driver */ | ||
230 | int sr_enable(struct voltagedomain *voltdm, unsigned long volt); | ||
231 | void sr_disable(struct voltagedomain *voltdm); | ||
232 | int sr_configure_errgen(struct voltagedomain *voltdm); | ||
233 | int sr_configure_minmax(struct voltagedomain *voltdm); | ||
234 | |||
235 | /* API to register the smartreflex class driver with the smartreflex driver */ | ||
236 | int sr_register_class(struct omap_sr_class_data *class_data); | ||
237 | #else | ||
238 | static inline void omap_sr_enable(struct voltagedomain *voltdm) {} | ||
239 | static inline void omap_sr_disable(struct voltagedomain *voltdm) {} | ||
240 | static inline void omap_sr_disable_reset_volt( | ||
241 | struct voltagedomain *voltdm) {} | ||
242 | static inline void omap_sr_register_pmic( | ||
243 | struct omap_sr_pmic_data *pmic_data) {} | ||
244 | #endif | ||
245 | #endif | ||