1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * OMAP SmartReflex Voltage Control |
4 | * |
5 | * Author: Thara Gopinath <thara@ti.com> |
6 | * |
7 | * Copyright (C) 2012 Texas Instruments, Inc. |
8 | * Thara Gopinath <thara@ti.com> |
9 | * |
10 | * Copyright (C) 2008 Nokia Corporation |
11 | * Kalle Jokiniemi |
12 | * |
13 | * Copyright (C) 2007 Texas Instruments, Inc. |
14 | * Lesly A M <x0080970@ti.com> |
15 | */ |
16 | |
17 | #include <linux/module.h> |
18 | #include <linux/mod_devicetable.h> |
19 | #include <linux/interrupt.h> |
20 | #include <linux/clk.h> |
21 | #include <linux/io.h> |
22 | #include <linux/debugfs.h> |
23 | #include <linux/delay.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/pm_runtime.h> |
26 | #include <linux/power/smartreflex.h> |
27 | |
28 | #define DRIVER_NAME "smartreflex" |
29 | #define SMARTREFLEX_NAME_LEN 32 |
30 | #define NVALUE_NAME_LEN 40 |
31 | #define SR_DISABLE_TIMEOUT 200 |
32 | |
33 | /* sr_list contains all the instances of smartreflex module */ |
34 | static LIST_HEAD(sr_list); |
35 | |
36 | static struct omap_sr_class_data *sr_class; |
37 | static struct dentry *sr_dbg_dir; |
38 | |
39 | static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value) |
40 | { |
41 | __raw_writel(val: value, addr: (sr->base + offset)); |
42 | } |
43 | |
44 | static inline void sr_modify_reg(struct omap_sr *sr, unsigned offset, u32 mask, |
45 | u32 value) |
46 | { |
47 | u32 reg_val; |
48 | |
49 | /* |
50 | * Smartreflex error config register is special as it contains |
51 | * certain status bits which if written a 1 into means a clear |
52 | * of those bits. So in order to make sure no accidental write of |
53 | * 1 happens to those status bits, do a clear of them in the read |
54 | * value. This mean this API doesn't rewrite values in these bits |
55 | * if they are currently set, but does allow the caller to write |
56 | * those bits. |
57 | */ |
58 | if (sr->ip_type == SR_TYPE_V1 && offset == ERRCONFIG_V1) |
59 | mask |= ERRCONFIG_STATUS_V1_MASK; |
60 | else if (sr->ip_type == SR_TYPE_V2 && offset == ERRCONFIG_V2) |
61 | mask |= ERRCONFIG_VPBOUNDINTST_V2; |
62 | |
63 | reg_val = __raw_readl(addr: sr->base + offset); |
64 | reg_val &= ~mask; |
65 | |
66 | value &= mask; |
67 | |
68 | reg_val |= value; |
69 | |
70 | __raw_writel(val: reg_val, addr: (sr->base + offset)); |
71 | } |
72 | |
73 | static inline u32 sr_read_reg(struct omap_sr *sr, unsigned offset) |
74 | { |
75 | return __raw_readl(addr: sr->base + offset); |
76 | } |
77 | |
78 | static struct omap_sr *_sr_lookup(struct voltagedomain *voltdm) |
79 | { |
80 | struct omap_sr *sr_info; |
81 | |
82 | if (!voltdm) { |
83 | pr_err("%s: Null voltage domain passed!\n" , __func__); |
84 | return ERR_PTR(error: -EINVAL); |
85 | } |
86 | |
87 | list_for_each_entry(sr_info, &sr_list, node) { |
88 | if (voltdm == sr_info->voltdm) |
89 | return sr_info; |
90 | } |
91 | |
92 | return ERR_PTR(error: -ENODATA); |
93 | } |
94 | |
95 | static irqreturn_t sr_interrupt(int irq, void *data) |
96 | { |
97 | struct omap_sr *sr_info = data; |
98 | u32 status = 0; |
99 | |
100 | switch (sr_info->ip_type) { |
101 | case SR_TYPE_V1: |
102 | /* Read the status bits */ |
103 | status = sr_read_reg(sr: sr_info, ERRCONFIG_V1); |
104 | |
105 | /* Clear them by writing back */ |
106 | sr_write_reg(sr: sr_info, ERRCONFIG_V1, value: status); |
107 | break; |
108 | case SR_TYPE_V2: |
109 | /* Read the status bits */ |
110 | status = sr_read_reg(sr: sr_info, IRQSTATUS); |
111 | |
112 | /* Clear them by writing back */ |
113 | sr_write_reg(sr: sr_info, IRQSTATUS, value: status); |
114 | break; |
115 | default: |
116 | dev_err(&sr_info->pdev->dev, "UNKNOWN IP type %d\n" , |
117 | sr_info->ip_type); |
118 | return IRQ_NONE; |
119 | } |
120 | |
121 | if (sr_class->notify) |
122 | sr_class->notify(sr_info, status); |
123 | |
124 | return IRQ_HANDLED; |
125 | } |
126 | |
127 | static void sr_set_clk_length(struct omap_sr *sr) |
128 | { |
129 | u32 fclk_speed; |
130 | |
131 | /* Try interconnect target module fck first if it already exists */ |
132 | if (IS_ERR(ptr: sr->fck)) |
133 | return; |
134 | |
135 | fclk_speed = clk_get_rate(clk: sr->fck); |
136 | |
137 | switch (fclk_speed) { |
138 | case 12000000: |
139 | sr->clk_length = SRCLKLENGTH_12MHZ_SYSCLK; |
140 | break; |
141 | case 13000000: |
142 | sr->clk_length = SRCLKLENGTH_13MHZ_SYSCLK; |
143 | break; |
144 | case 19200000: |
145 | sr->clk_length = SRCLKLENGTH_19MHZ_SYSCLK; |
146 | break; |
147 | case 26000000: |
148 | sr->clk_length = SRCLKLENGTH_26MHZ_SYSCLK; |
149 | break; |
150 | case 38400000: |
151 | sr->clk_length = SRCLKLENGTH_38MHZ_SYSCLK; |
152 | break; |
153 | default: |
154 | dev_err(&sr->pdev->dev, "%s: Invalid fclk rate: %d\n" , |
155 | __func__, fclk_speed); |
156 | break; |
157 | } |
158 | } |
159 | |
160 | static void sr_start_vddautocomp(struct omap_sr *sr) |
161 | { |
162 | if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) { |
163 | dev_warn(&sr->pdev->dev, |
164 | "%s: smartreflex class driver not registered\n" , |
165 | __func__); |
166 | return; |
167 | } |
168 | |
169 | if (!sr_class->enable(sr)) |
170 | sr->autocomp_active = true; |
171 | } |
172 | |
173 | static void sr_stop_vddautocomp(struct omap_sr *sr) |
174 | { |
175 | if (!sr_class || !(sr_class->disable)) { |
176 | dev_warn(&sr->pdev->dev, |
177 | "%s: smartreflex class driver not registered\n" , |
178 | __func__); |
179 | return; |
180 | } |
181 | |
182 | if (sr->autocomp_active) { |
183 | sr_class->disable(sr, 1); |
184 | sr->autocomp_active = false; |
185 | } |
186 | } |
187 | |
188 | /* |
189 | * This function handles the initializations which have to be done |
190 | * only when both sr device and class driver regiter has |
191 | * completed. This will be attempted to be called from both sr class |
192 | * driver register and sr device intializtion API's. Only one call |
193 | * will ultimately succeed. |
194 | * |
195 | * Currently this function registers interrupt handler for a particular SR |
196 | * if smartreflex class driver is already registered and has |
197 | * requested for interrupts and the SR interrupt line in present. |
198 | */ |
199 | static int sr_late_init(struct omap_sr *sr_info) |
200 | { |
201 | int ret = 0; |
202 | |
203 | if (sr_class->notify && sr_class->notify_flags && sr_info->irq) { |
204 | ret = devm_request_irq(dev: &sr_info->pdev->dev, irq: sr_info->irq, |
205 | handler: sr_interrupt, irqflags: 0, devname: sr_info->name, dev_id: sr_info); |
206 | if (ret) |
207 | goto error; |
208 | disable_irq(irq: sr_info->irq); |
209 | } |
210 | |
211 | return ret; |
212 | |
213 | error: |
214 | list_del(entry: &sr_info->node); |
215 | dev_err(&sr_info->pdev->dev, "%s: ERROR in registering interrupt handler. Smartreflex will not function as desired\n" , |
216 | __func__); |
217 | |
218 | return ret; |
219 | } |
220 | |
221 | static void sr_v1_disable(struct omap_sr *sr) |
222 | { |
223 | int timeout = 0; |
224 | int errconf_val = ERRCONFIG_MCUACCUMINTST | ERRCONFIG_MCUVALIDINTST | |
225 | ERRCONFIG_MCUBOUNDINTST; |
226 | |
227 | /* Enable MCUDisableAcknowledge interrupt */ |
228 | sr_modify_reg(sr, ERRCONFIG_V1, |
229 | ERRCONFIG_MCUDISACKINTEN, ERRCONFIG_MCUDISACKINTEN); |
230 | |
231 | /* SRCONFIG - disable SR */ |
232 | sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, value: 0x0); |
233 | |
234 | /* Disable all other SR interrupts and clear the status as needed */ |
235 | if (sr_read_reg(sr, ERRCONFIG_V1) & ERRCONFIG_VPBOUNDINTST_V1) |
236 | errconf_val |= ERRCONFIG_VPBOUNDINTST_V1; |
237 | sr_modify_reg(sr, ERRCONFIG_V1, |
238 | mask: (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN | |
239 | ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_VPBOUNDINTEN_V1), |
240 | value: errconf_val); |
241 | |
242 | /* |
243 | * Wait for SR to be disabled. |
244 | * wait until ERRCONFIG.MCUDISACKINTST = 1. Typical latency is 1us. |
245 | */ |
246 | sr_test_cond_timeout((sr_read_reg(sr, ERRCONFIG_V1) & |
247 | ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT, |
248 | timeout); |
249 | |
250 | if (timeout >= SR_DISABLE_TIMEOUT) |
251 | dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n" , |
252 | __func__); |
253 | |
254 | /* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */ |
255 | sr_modify_reg(sr, ERRCONFIG_V1, ERRCONFIG_MCUDISACKINTEN, |
256 | ERRCONFIG_MCUDISACKINTST); |
257 | } |
258 | |
259 | static void sr_v2_disable(struct omap_sr *sr) |
260 | { |
261 | int timeout = 0; |
262 | |
263 | /* Enable MCUDisableAcknowledge interrupt */ |
264 | sr_write_reg(sr, IRQENABLE_SET, IRQENABLE_MCUDISABLEACKINT); |
265 | |
266 | /* SRCONFIG - disable SR */ |
267 | sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, value: 0x0); |
268 | |
269 | /* |
270 | * Disable all other SR interrupts and clear the status |
271 | * write to status register ONLY on need basis - only if status |
272 | * is set. |
273 | */ |
274 | if (sr_read_reg(sr, ERRCONFIG_V2) & ERRCONFIG_VPBOUNDINTST_V2) |
275 | sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2, |
276 | ERRCONFIG_VPBOUNDINTST_V2); |
277 | else |
278 | sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2, |
279 | value: 0x0); |
280 | sr_write_reg(sr, IRQENABLE_CLR, value: (IRQENABLE_MCUACCUMINT | |
281 | IRQENABLE_MCUVALIDINT | |
282 | IRQENABLE_MCUBOUNDSINT)); |
283 | sr_write_reg(sr, IRQSTATUS, value: (IRQSTATUS_MCUACCUMINT | |
284 | IRQSTATUS_MCVALIDINT | |
285 | IRQSTATUS_MCBOUNDSINT)); |
286 | |
287 | /* |
288 | * Wait for SR to be disabled. |
289 | * wait until IRQSTATUS.MCUDISACKINTST = 1. Typical latency is 1us. |
290 | */ |
291 | sr_test_cond_timeout((sr_read_reg(sr, IRQSTATUS) & |
292 | IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT, |
293 | timeout); |
294 | |
295 | if (timeout >= SR_DISABLE_TIMEOUT) |
296 | dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n" , |
297 | __func__); |
298 | |
299 | /* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */ |
300 | sr_write_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUDISABLEACKINT); |
301 | sr_write_reg(sr, IRQSTATUS, IRQSTATUS_MCUDISABLEACKINT); |
302 | } |
303 | |
304 | static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row( |
305 | struct omap_sr *sr, u32 efuse_offs) |
306 | { |
307 | int i; |
308 | |
309 | if (!sr->nvalue_table) { |
310 | dev_warn(&sr->pdev->dev, "%s: Missing ntarget value table\n" , |
311 | __func__); |
312 | return NULL; |
313 | } |
314 | |
315 | for (i = 0; i < sr->nvalue_count; i++) { |
316 | if (sr->nvalue_table[i].efuse_offs == efuse_offs) |
317 | return &sr->nvalue_table[i]; |
318 | } |
319 | |
320 | return NULL; |
321 | } |
322 | |
323 | /* Public Functions */ |
324 | |
325 | /** |
326 | * sr_configure_errgen() - Configures the SmartReflex to perform AVS using the |
327 | * error generator module. |
328 | * @sr: SR module to be configured. |
329 | * |
330 | * This API is to be called from the smartreflex class driver to |
331 | * configure the error generator module inside the smartreflex module. |
332 | * SR settings if using the ERROR module inside Smartreflex. |
333 | * SR CLASS 3 by default uses only the ERROR module where as |
334 | * SR CLASS 2 can choose between ERROR module and MINMAXAVG |
335 | * module. Returns 0 on success and error value in case of failure. |
336 | */ |
337 | int sr_configure_errgen(struct omap_sr *sr) |
338 | { |
339 | u32 sr_config, sr_errconfig, errconfig_offs; |
340 | u32 vpboundint_en, vpboundint_st; |
341 | u32 senp_en = 0, senn_en = 0; |
342 | u8 senp_shift, senn_shift; |
343 | |
344 | if (!sr) { |
345 | pr_warn("%s: NULL omap_sr from %pS\n" , |
346 | __func__, (void *)_RET_IP_); |
347 | return -EINVAL; |
348 | } |
349 | |
350 | if (!sr->clk_length) |
351 | sr_set_clk_length(sr); |
352 | |
353 | senp_en = sr->senp_mod; |
354 | senn_en = sr->senn_mod; |
355 | |
356 | sr_config = (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) | |
357 | SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN; |
358 | |
359 | switch (sr->ip_type) { |
360 | case SR_TYPE_V1: |
361 | sr_config |= SRCONFIG_DELAYCTRL; |
362 | senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT; |
363 | senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT; |
364 | errconfig_offs = ERRCONFIG_V1; |
365 | vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1; |
366 | vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1; |
367 | break; |
368 | case SR_TYPE_V2: |
369 | senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT; |
370 | senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT; |
371 | errconfig_offs = ERRCONFIG_V2; |
372 | vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2; |
373 | vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2; |
374 | break; |
375 | default: |
376 | dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n" , |
377 | __func__); |
378 | return -EINVAL; |
379 | } |
380 | |
381 | sr_config |= ((senn_en << senn_shift) | (senp_en << senp_shift)); |
382 | sr_write_reg(sr, SRCONFIG, value: sr_config); |
383 | sr_errconfig = (sr->err_weight << ERRCONFIG_ERRWEIGHT_SHIFT) | |
384 | (sr->err_maxlimit << ERRCONFIG_ERRMAXLIMIT_SHIFT) | |
385 | (sr->err_minlimit << ERRCONFIG_ERRMINLIMIT_SHIFT); |
386 | sr_modify_reg(sr, offset: errconfig_offs, mask: (SR_ERRWEIGHT_MASK | |
387 | SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK), |
388 | value: sr_errconfig); |
389 | |
390 | /* Enabling the interrupts if the ERROR module is used */ |
391 | sr_modify_reg(sr, offset: errconfig_offs, mask: (vpboundint_en | vpboundint_st), |
392 | value: vpboundint_en); |
393 | |
394 | return 0; |
395 | } |
396 | |
397 | /** |
398 | * sr_disable_errgen() - Disables SmartReflex AVS module's errgen component |
399 | * @sr: SR module to be configured. |
400 | * |
401 | * This API is to be called from the smartreflex class driver to |
402 | * disable the error generator module inside the smartreflex module. |
403 | * |
404 | * Returns 0 on success and error value in case of failure. |
405 | */ |
406 | int sr_disable_errgen(struct omap_sr *sr) |
407 | { |
408 | u32 errconfig_offs; |
409 | u32 vpboundint_en, vpboundint_st; |
410 | |
411 | if (!sr) { |
412 | pr_warn("%s: NULL omap_sr from %pS\n" , |
413 | __func__, (void *)_RET_IP_); |
414 | return -EINVAL; |
415 | } |
416 | |
417 | switch (sr->ip_type) { |
418 | case SR_TYPE_V1: |
419 | errconfig_offs = ERRCONFIG_V1; |
420 | vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1; |
421 | vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1; |
422 | break; |
423 | case SR_TYPE_V2: |
424 | errconfig_offs = ERRCONFIG_V2; |
425 | vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2; |
426 | vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2; |
427 | break; |
428 | default: |
429 | dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n" , |
430 | __func__); |
431 | return -EINVAL; |
432 | } |
433 | |
434 | /* Disable the Sensor and errorgen */ |
435 | sr_modify_reg(sr, SRCONFIG, SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN, value: 0); |
436 | |
437 | /* |
438 | * Disable the interrupts of ERROR module |
439 | * NOTE: modify is a read, modify,write - an implicit OCP barrier |
440 | * which is required is present here - sequencing is critical |
441 | * at this point (after errgen is disabled, vpboundint disable) |
442 | */ |
443 | sr_modify_reg(sr, offset: errconfig_offs, mask: vpboundint_en | vpboundint_st, value: 0); |
444 | |
445 | return 0; |
446 | } |
447 | |
448 | /** |
449 | * sr_configure_minmax() - Configures the SmartReflex to perform AVS using the |
450 | * minmaxavg module. |
451 | * @sr: SR module to be configured. |
452 | * |
453 | * This API is to be called from the smartreflex class driver to |
454 | * configure the minmaxavg module inside the smartreflex module. |
455 | * SR settings if using the ERROR module inside Smartreflex. |
456 | * SR CLASS 3 by default uses only the ERROR module where as |
457 | * SR CLASS 2 can choose between ERROR module and MINMAXAVG |
458 | * module. Returns 0 on success and error value in case of failure. |
459 | */ |
460 | int sr_configure_minmax(struct omap_sr *sr) |
461 | { |
462 | u32 sr_config, sr_avgwt; |
463 | u32 senp_en = 0, senn_en = 0; |
464 | u8 senp_shift, senn_shift; |
465 | |
466 | if (!sr) { |
467 | pr_warn("%s: NULL omap_sr from %pS\n" , |
468 | __func__, (void *)_RET_IP_); |
469 | return -EINVAL; |
470 | } |
471 | |
472 | if (!sr->clk_length) |
473 | sr_set_clk_length(sr); |
474 | |
475 | senp_en = sr->senp_mod; |
476 | senn_en = sr->senn_mod; |
477 | |
478 | sr_config = (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) | |
479 | SRCONFIG_SENENABLE | |
480 | (sr->accum_data << SRCONFIG_ACCUMDATA_SHIFT); |
481 | |
482 | switch (sr->ip_type) { |
483 | case SR_TYPE_V1: |
484 | sr_config |= SRCONFIG_DELAYCTRL; |
485 | senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT; |
486 | senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT; |
487 | break; |
488 | case SR_TYPE_V2: |
489 | senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT; |
490 | senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT; |
491 | break; |
492 | default: |
493 | dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n" , |
494 | __func__); |
495 | return -EINVAL; |
496 | } |
497 | |
498 | sr_config |= ((senn_en << senn_shift) | (senp_en << senp_shift)); |
499 | sr_write_reg(sr, SRCONFIG, value: sr_config); |
500 | sr_avgwt = (sr->senp_avgweight << AVGWEIGHT_SENPAVGWEIGHT_SHIFT) | |
501 | (sr->senn_avgweight << AVGWEIGHT_SENNAVGWEIGHT_SHIFT); |
502 | sr_write_reg(sr, AVGWEIGHT, value: sr_avgwt); |
503 | |
504 | /* |
505 | * Enabling the interrupts if MINMAXAVG module is used. |
506 | * TODO: check if all the interrupts are mandatory |
507 | */ |
508 | switch (sr->ip_type) { |
509 | case SR_TYPE_V1: |
510 | sr_modify_reg(sr, ERRCONFIG_V1, |
511 | mask: (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN | |
512 | ERRCONFIG_MCUBOUNDINTEN), |
513 | value: (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUACCUMINTST | |
514 | ERRCONFIG_MCUVALIDINTEN | ERRCONFIG_MCUVALIDINTST | |
515 | ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_MCUBOUNDINTST)); |
516 | break; |
517 | case SR_TYPE_V2: |
518 | sr_write_reg(sr, IRQSTATUS, |
519 | IRQSTATUS_MCUACCUMINT | IRQSTATUS_MCVALIDINT | |
520 | IRQSTATUS_MCBOUNDSINT | IRQSTATUS_MCUDISABLEACKINT); |
521 | sr_write_reg(sr, IRQENABLE_SET, |
522 | IRQENABLE_MCUACCUMINT | IRQENABLE_MCUVALIDINT | |
523 | IRQENABLE_MCUBOUNDSINT | IRQENABLE_MCUDISABLEACKINT); |
524 | break; |
525 | default: |
526 | dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n" , |
527 | __func__); |
528 | return -EINVAL; |
529 | } |
530 | |
531 | return 0; |
532 | } |
533 | |
534 | /** |
535 | * sr_enable() - Enables the smartreflex module. |
536 | * @sr: pointer to which the SR module to be configured belongs to. |
537 | * @volt: The voltage at which the Voltage domain associated with |
538 | * the smartreflex module is operating at. |
539 | * This is required only to program the correct Ntarget value. |
540 | * |
541 | * This API is to be called from the smartreflex class driver to |
542 | * enable a smartreflex module. Returns 0 on success. Returns error |
543 | * value if the voltage passed is wrong or if ntarget value is wrong. |
544 | */ |
545 | int sr_enable(struct omap_sr *sr, unsigned long volt) |
546 | { |
547 | struct omap_volt_data *volt_data; |
548 | struct omap_sr_nvalue_table *nvalue_row; |
549 | int ret; |
550 | |
551 | if (!sr) { |
552 | pr_warn("%s: NULL omap_sr from %pS\n" , |
553 | __func__, (void *)_RET_IP_); |
554 | return -EINVAL; |
555 | } |
556 | |
557 | volt_data = omap_voltage_get_voltdata(voltdm: sr->voltdm, volt); |
558 | |
559 | if (IS_ERR(ptr: volt_data)) { |
560 | dev_warn(&sr->pdev->dev, "%s: Unable to get voltage table for nominal voltage %ld\n" , |
561 | __func__, volt); |
562 | return PTR_ERR(ptr: volt_data); |
563 | } |
564 | |
565 | nvalue_row = sr_retrieve_nvalue_row(sr, efuse_offs: volt_data->sr_efuse_offs); |
566 | |
567 | if (!nvalue_row) { |
568 | dev_warn(&sr->pdev->dev, "%s: failure getting SR data for this voltage %ld\n" , |
569 | __func__, volt); |
570 | return -ENODATA; |
571 | } |
572 | |
573 | /* errminlimit is opp dependent and hence linked to voltage */ |
574 | sr->err_minlimit = nvalue_row->errminlimit; |
575 | |
576 | clk_enable(clk: sr->fck); |
577 | |
578 | /* Check if SR is already enabled. If yes do nothing */ |
579 | if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) |
580 | goto out_enabled; |
581 | |
582 | /* Configure SR */ |
583 | ret = sr_class->configure(sr); |
584 | if (ret) |
585 | goto out_enabled; |
586 | |
587 | sr_write_reg(sr, NVALUERECIPROCAL, value: nvalue_row->nvalue); |
588 | |
589 | /* SRCONFIG - enable SR */ |
590 | sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE); |
591 | |
592 | out_enabled: |
593 | sr->enabled = 1; |
594 | |
595 | return 0; |
596 | } |
597 | |
598 | /** |
599 | * sr_disable() - Disables the smartreflex module. |
600 | * @sr: pointer to which the SR module to be configured belongs to. |
601 | * |
602 | * This API is to be called from the smartreflex class driver to |
603 | * disable a smartreflex module. |
604 | */ |
605 | void sr_disable(struct omap_sr *sr) |
606 | { |
607 | if (!sr) { |
608 | pr_warn("%s: NULL omap_sr from %pS\n" , |
609 | __func__, (void *)_RET_IP_); |
610 | return; |
611 | } |
612 | |
613 | /* Check if SR clocks are already disabled. If yes do nothing */ |
614 | if (!sr->enabled) |
615 | return; |
616 | |
617 | /* |
618 | * Disable SR if only it is indeed enabled. Else just |
619 | * disable the clocks. |
620 | */ |
621 | if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) { |
622 | switch (sr->ip_type) { |
623 | case SR_TYPE_V1: |
624 | sr_v1_disable(sr); |
625 | break; |
626 | case SR_TYPE_V2: |
627 | sr_v2_disable(sr); |
628 | break; |
629 | default: |
630 | dev_err(&sr->pdev->dev, "UNKNOWN IP type %d\n" , |
631 | sr->ip_type); |
632 | } |
633 | } |
634 | |
635 | clk_disable(clk: sr->fck); |
636 | sr->enabled = 0; |
637 | } |
638 | |
639 | /** |
640 | * sr_register_class() - API to register a smartreflex class parameters. |
641 | * @class_data: The structure containing various sr class specific data. |
642 | * |
643 | * This API is to be called by the smartreflex class driver to register itself |
644 | * with the smartreflex driver during init. Returns 0 on success else the |
645 | * error value. |
646 | */ |
647 | int sr_register_class(struct omap_sr_class_data *class_data) |
648 | { |
649 | struct omap_sr *sr_info; |
650 | |
651 | if (!class_data) { |
652 | pr_warn("%s:, Smartreflex class data passed is NULL\n" , |
653 | __func__); |
654 | return -EINVAL; |
655 | } |
656 | |
657 | if (sr_class) { |
658 | pr_warn("%s: Smartreflex class driver already registered\n" , |
659 | __func__); |
660 | return -EBUSY; |
661 | } |
662 | |
663 | sr_class = class_data; |
664 | |
665 | /* |
666 | * Call into late init to do initializations that require |
667 | * both sr driver and sr class driver to be initiallized. |
668 | */ |
669 | list_for_each_entry(sr_info, &sr_list, node) |
670 | sr_late_init(sr_info); |
671 | |
672 | return 0; |
673 | } |
674 | |
675 | /** |
676 | * omap_sr_enable() - API to enable SR clocks and to call into the |
677 | * registered smartreflex class enable API. |
678 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. |
679 | * |
680 | * This API is to be called from the kernel in order to enable |
681 | * a particular smartreflex module. This API will do the initial |
682 | * configurations to turn on the smartreflex module and in turn call |
683 | * into the registered smartreflex class enable API. |
684 | */ |
685 | void omap_sr_enable(struct voltagedomain *voltdm) |
686 | { |
687 | struct omap_sr *sr = _sr_lookup(voltdm); |
688 | |
689 | if (IS_ERR(ptr: sr)) { |
690 | pr_warn("%s: omap_sr struct for voltdm not found\n" , __func__); |
691 | return; |
692 | } |
693 | |
694 | if (!sr->autocomp_active) |
695 | return; |
696 | |
697 | if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) { |
698 | dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n" , |
699 | __func__); |
700 | return; |
701 | } |
702 | |
703 | sr_class->enable(sr); |
704 | } |
705 | |
706 | /** |
707 | * omap_sr_disable() - API to disable SR without resetting the voltage |
708 | * processor voltage |
709 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. |
710 | * |
711 | * This API is to be called from the kernel in order to disable |
712 | * a particular smartreflex module. This API will in turn call |
713 | * into the registered smartreflex class disable API. This API will tell |
714 | * the smartreflex class disable not to reset the VP voltage after |
715 | * disabling smartreflex. |
716 | */ |
717 | void omap_sr_disable(struct voltagedomain *voltdm) |
718 | { |
719 | struct omap_sr *sr = _sr_lookup(voltdm); |
720 | |
721 | if (IS_ERR(ptr: sr)) { |
722 | pr_warn("%s: omap_sr struct for voltdm not found\n" , __func__); |
723 | return; |
724 | } |
725 | |
726 | if (!sr->autocomp_active) |
727 | return; |
728 | |
729 | if (!sr_class || !(sr_class->disable)) { |
730 | dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n" , |
731 | __func__); |
732 | return; |
733 | } |
734 | |
735 | sr_class->disable(sr, 0); |
736 | } |
737 | |
738 | /** |
739 | * omap_sr_disable_reset_volt() - API to disable SR and reset the |
740 | * voltage processor voltage |
741 | * @voltdm: VDD pointer to which the SR module to be configured belongs to. |
742 | * |
743 | * This API is to be called from the kernel in order to disable |
744 | * a particular smartreflex module. This API will in turn call |
745 | * into the registered smartreflex class disable API. This API will tell |
746 | * the smartreflex class disable to reset the VP voltage after |
747 | * disabling smartreflex. |
748 | */ |
749 | void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) |
750 | { |
751 | struct omap_sr *sr = _sr_lookup(voltdm); |
752 | |
753 | if (IS_ERR(ptr: sr)) { |
754 | pr_warn("%s: omap_sr struct for voltdm not found\n" , __func__); |
755 | return; |
756 | } |
757 | |
758 | if (!sr->autocomp_active) |
759 | return; |
760 | |
761 | if (!sr_class || !(sr_class->disable)) { |
762 | dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n" , |
763 | __func__); |
764 | return; |
765 | } |
766 | |
767 | sr_class->disable(sr, 1); |
768 | } |
769 | |
770 | /* PM Debug FS entries to enable and disable smartreflex. */ |
771 | static int omap_sr_autocomp_show(void *data, u64 *val) |
772 | { |
773 | struct omap_sr *sr_info = data; |
774 | |
775 | if (!sr_info) { |
776 | pr_warn("%s: omap_sr struct not found\n" , __func__); |
777 | return -EINVAL; |
778 | } |
779 | |
780 | *val = sr_info->autocomp_active; |
781 | |
782 | return 0; |
783 | } |
784 | |
785 | static int omap_sr_autocomp_store(void *data, u64 val) |
786 | { |
787 | struct omap_sr *sr_info = data; |
788 | |
789 | if (!sr_info) { |
790 | pr_warn("%s: omap_sr struct not found\n" , __func__); |
791 | return -EINVAL; |
792 | } |
793 | |
794 | /* Sanity check */ |
795 | if (val > 1) { |
796 | pr_warn("%s: Invalid argument %lld\n" , __func__, val); |
797 | return -EINVAL; |
798 | } |
799 | |
800 | /* control enable/disable only if there is a delta in value */ |
801 | if (sr_info->autocomp_active != val) { |
802 | if (!val) |
803 | sr_stop_vddautocomp(sr: sr_info); |
804 | else |
805 | sr_start_vddautocomp(sr: sr_info); |
806 | } |
807 | |
808 | return 0; |
809 | } |
810 | |
811 | DEFINE_SIMPLE_ATTRIBUTE(pm_sr_fops, omap_sr_autocomp_show, |
812 | omap_sr_autocomp_store, "%llu\n" ); |
813 | |
814 | static int omap_sr_probe(struct platform_device *pdev) |
815 | { |
816 | struct omap_sr *sr_info; |
817 | struct omap_sr_data *pdata = pdev->dev.platform_data; |
818 | struct dentry *nvalue_dir; |
819 | int i, ret = 0; |
820 | |
821 | sr_info = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct omap_sr), GFP_KERNEL); |
822 | if (!sr_info) |
823 | return -ENOMEM; |
824 | |
825 | sr_info->name = devm_kzalloc(dev: &pdev->dev, |
826 | SMARTREFLEX_NAME_LEN, GFP_KERNEL); |
827 | if (!sr_info->name) |
828 | return -ENOMEM; |
829 | |
830 | platform_set_drvdata(pdev, data: sr_info); |
831 | |
832 | if (!pdata) { |
833 | dev_err(&pdev->dev, "%s: platform data missing\n" , __func__); |
834 | return -EINVAL; |
835 | } |
836 | |
837 | sr_info->base = devm_platform_ioremap_resource(pdev, index: 0); |
838 | if (IS_ERR(ptr: sr_info->base)) |
839 | return PTR_ERR(ptr: sr_info->base); |
840 | |
841 | ret = platform_get_irq_optional(pdev, 0); |
842 | if (ret < 0 && ret != -ENXIO) |
843 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "failed to get IRQ resource\n" ); |
844 | if (ret > 0) |
845 | sr_info->irq = ret; |
846 | |
847 | sr_info->fck = devm_clk_get(dev: pdev->dev.parent, id: "fck" ); |
848 | if (IS_ERR(ptr: sr_info->fck)) |
849 | return PTR_ERR(ptr: sr_info->fck); |
850 | clk_prepare(clk: sr_info->fck); |
851 | |
852 | pm_runtime_enable(dev: &pdev->dev); |
853 | |
854 | snprintf(buf: sr_info->name, SMARTREFLEX_NAME_LEN, fmt: "%s" , pdata->name); |
855 | |
856 | sr_info->pdev = pdev; |
857 | sr_info->srid = pdev->id; |
858 | sr_info->voltdm = pdata->voltdm; |
859 | sr_info->nvalue_table = pdata->nvalue_table; |
860 | sr_info->nvalue_count = pdata->nvalue_count; |
861 | sr_info->senn_mod = pdata->senn_mod; |
862 | sr_info->senp_mod = pdata->senp_mod; |
863 | sr_info->err_weight = pdata->err_weight; |
864 | sr_info->err_maxlimit = pdata->err_maxlimit; |
865 | sr_info->accum_data = pdata->accum_data; |
866 | sr_info->senn_avgweight = pdata->senn_avgweight; |
867 | sr_info->senp_avgweight = pdata->senp_avgweight; |
868 | sr_info->autocomp_active = false; |
869 | sr_info->ip_type = pdata->ip_type; |
870 | |
871 | sr_set_clk_length(sr: sr_info); |
872 | |
873 | list_add(new: &sr_info->node, head: &sr_list); |
874 | |
875 | /* |
876 | * Call into late init to do initializations that require |
877 | * both sr driver and sr class driver to be initiallized. |
878 | */ |
879 | if (sr_class) { |
880 | ret = sr_late_init(sr_info); |
881 | if (ret) { |
882 | pr_warn("%s: Error in SR late init\n" , __func__); |
883 | goto err_list_del; |
884 | } |
885 | } |
886 | |
887 | dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n" , __func__); |
888 | if (!sr_dbg_dir) |
889 | sr_dbg_dir = debugfs_create_dir(name: "smartreflex" , NULL); |
890 | |
891 | sr_info->dbg_dir = debugfs_create_dir(name: sr_info->name, parent: sr_dbg_dir); |
892 | |
893 | debugfs_create_file(name: "autocomp" , S_IRUGO | S_IWUSR, parent: sr_info->dbg_dir, |
894 | data: sr_info, fops: &pm_sr_fops); |
895 | debugfs_create_x32(name: "errweight" , S_IRUGO, parent: sr_info->dbg_dir, |
896 | value: &sr_info->err_weight); |
897 | debugfs_create_x32(name: "errmaxlimit" , S_IRUGO, parent: sr_info->dbg_dir, |
898 | value: &sr_info->err_maxlimit); |
899 | |
900 | nvalue_dir = debugfs_create_dir(name: "nvalue" , parent: sr_info->dbg_dir); |
901 | |
902 | if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) { |
903 | dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n" , |
904 | __func__, sr_info->name); |
905 | |
906 | ret = -ENODATA; |
907 | goto err_debugfs; |
908 | } |
909 | |
910 | for (i = 0; i < sr_info->nvalue_count; i++) { |
911 | char name[NVALUE_NAME_LEN + 1]; |
912 | |
913 | snprintf(buf: name, size: sizeof(name), fmt: "volt_%lu" , |
914 | sr_info->nvalue_table[i].volt_nominal); |
915 | debugfs_create_x32(name, S_IRUGO | S_IWUSR, parent: nvalue_dir, |
916 | value: &(sr_info->nvalue_table[i].nvalue)); |
917 | snprintf(buf: name, size: sizeof(name), fmt: "errminlimit_%lu" , |
918 | sr_info->nvalue_table[i].volt_nominal); |
919 | debugfs_create_x32(name, S_IRUGO | S_IWUSR, parent: nvalue_dir, |
920 | value: &(sr_info->nvalue_table[i].errminlimit)); |
921 | |
922 | } |
923 | |
924 | return 0; |
925 | |
926 | err_debugfs: |
927 | debugfs_remove_recursive(dentry: sr_info->dbg_dir); |
928 | err_list_del: |
929 | pm_runtime_disable(dev: &pdev->dev); |
930 | list_del(entry: &sr_info->node); |
931 | clk_unprepare(clk: sr_info->fck); |
932 | |
933 | return ret; |
934 | } |
935 | |
936 | static void omap_sr_remove(struct platform_device *pdev) |
937 | { |
938 | struct device *dev = &pdev->dev; |
939 | struct omap_sr *sr_info = platform_get_drvdata(pdev); |
940 | |
941 | if (sr_info->autocomp_active) |
942 | sr_stop_vddautocomp(sr: sr_info); |
943 | debugfs_remove_recursive(dentry: sr_info->dbg_dir); |
944 | |
945 | pm_runtime_disable(dev); |
946 | clk_unprepare(clk: sr_info->fck); |
947 | list_del(entry: &sr_info->node); |
948 | } |
949 | |
950 | static void omap_sr_shutdown(struct platform_device *pdev) |
951 | { |
952 | struct omap_sr *sr_info = platform_get_drvdata(pdev); |
953 | |
954 | if (sr_info->autocomp_active) |
955 | sr_stop_vddautocomp(sr: sr_info); |
956 | |
957 | return; |
958 | } |
959 | |
960 | static const struct of_device_id omap_sr_match[] = { |
961 | { .compatible = "ti,omap3-smartreflex-core" , }, |
962 | { .compatible = "ti,omap3-smartreflex-mpu-iva" , }, |
963 | { .compatible = "ti,omap4-smartreflex-core" , }, |
964 | { .compatible = "ti,omap4-smartreflex-mpu" , }, |
965 | { .compatible = "ti,omap4-smartreflex-iva" , }, |
966 | { }, |
967 | }; |
968 | MODULE_DEVICE_TABLE(of, omap_sr_match); |
969 | |
970 | static struct platform_driver smartreflex_driver = { |
971 | .probe = omap_sr_probe, |
972 | .remove_new = omap_sr_remove, |
973 | .shutdown = omap_sr_shutdown, |
974 | .driver = { |
975 | .name = DRIVER_NAME, |
976 | .of_match_table = omap_sr_match, |
977 | }, |
978 | }; |
979 | |
980 | static int __init sr_init(void) |
981 | { |
982 | int ret = 0; |
983 | |
984 | ret = platform_driver_register(&smartreflex_driver); |
985 | if (ret) { |
986 | pr_err("%s: platform driver register failed for SR\n" , |
987 | __func__); |
988 | return ret; |
989 | } |
990 | |
991 | return 0; |
992 | } |
993 | late_initcall(sr_init); |
994 | |
995 | static void __exit sr_exit(void) |
996 | { |
997 | platform_driver_unregister(&smartreflex_driver); |
998 | } |
999 | module_exit(sr_exit); |
1000 | |
1001 | MODULE_DESCRIPTION("OMAP Smartreflex Driver" ); |
1002 | MODULE_LICENSE("GPL" ); |
1003 | MODULE_ALIAS("platform:" DRIVER_NAME); |
1004 | MODULE_AUTHOR("Texas Instruments Inc" ); |
1005 | |