aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-exynos/Kconfig1
-rw-r--r--arch/arm/mach-exynos/pmu.c171
2 files changed, 130 insertions, 42 deletions
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 349c86708298..b9e3f1c61baf 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -24,6 +24,7 @@ menuconfig ARCH_EXYNOS
24 select PM_GENERIC_DOMAINS if PM_RUNTIME 24 select PM_GENERIC_DOMAINS if PM_RUNTIME
25 select S5P_DEV_MFC 25 select S5P_DEV_MFC
26 select SRAM 26 select SRAM
27 select MFD_SYSCON
27 help 28 help
28 Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5) 29 Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5)
29 30
diff --git a/arch/arm/mach-exynos/pmu.c b/arch/arm/mach-exynos/pmu.c
index e5e1846f10c9..24cd4c471c47 100644
--- a/arch/arm/mach-exynos/pmu.c
+++ b/arch/arm/mach-exynos/pmu.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. 2 * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com/ 3 * http://www.samsung.com/
4 * 4 *
5 * EXYNOS - CPU PMU(Power Management Unit) support 5 * EXYNOS - CPU PMU(Power Management Unit) support
@@ -10,12 +10,26 @@
10 */ 10 */
11 11
12#include <linux/io.h> 12#include <linux/io.h>
13#include <linux/kernel.h> 13#include <linux/of.h>
14#include <linux/platform_device.h>
14 15
15#include "common.h" 16#include "common.h"
16#include "regs-pmu.h" 17#include "regs-pmu.h"
17 18
18static const struct exynos_pmu_conf *exynos_pmu_config; 19struct exynos_pmu_data {
20 const struct exynos_pmu_conf *pmu_config;
21 const struct exynos_pmu_conf *pmu_config_extra;
22
23 void (*pmu_init)(void);
24 void (*powerdown_conf)(enum sys_powerdown);
25};
26
27struct exynos_pmu_context {
28 struct device *dev;
29 const struct exynos_pmu_data *pmu_data;
30};
31
32static struct exynos_pmu_context *pmu_context;
19 33
20static const struct exynos_pmu_conf exynos4210_pmu_config[] = { 34static const struct exynos_pmu_conf exynos4210_pmu_config[] = {
21 /* { .offset = offset, .val = { AFTR, LPA, SLEEP } */ 35 /* { .offset = offset, .val = { AFTR, LPA, SLEEP } */
@@ -336,7 +350,7 @@ static unsigned int const exynos5_list_disable_wfi_wfe[] = {
336 EXYNOS5_ISP_ARM_OPTION, 350 EXYNOS5_ISP_ARM_OPTION,
337}; 351};
338 352
339static void exynos5_init_pmu(void) 353static void exynos5_powerdown_conf(enum sys_powerdown mode)
340{ 354{
341 unsigned int i; 355 unsigned int i;
342 unsigned int tmp; 356 unsigned int tmp;
@@ -344,7 +358,7 @@ static void exynos5_init_pmu(void)
344 /* 358 /*
345 * Enable both SC_FEEDBACK and SC_COUNTER 359 * Enable both SC_FEEDBACK and SC_COUNTER
346 */ 360 */
347 for (i = 0 ; i < ARRAY_SIZE(exynos5_list_both_cnt_feed) ; i++) { 361 for (i = 0; i < ARRAY_SIZE(exynos5_list_both_cnt_feed); i++) {
348 tmp = pmu_raw_readl(exynos5_list_both_cnt_feed[i]); 362 tmp = pmu_raw_readl(exynos5_list_both_cnt_feed[i]);
349 tmp |= (EXYNOS5_USE_SC_FEEDBACK | 363 tmp |= (EXYNOS5_USE_SC_FEEDBACK |
350 EXYNOS5_USE_SC_COUNTER); 364 EXYNOS5_USE_SC_COUNTER);
@@ -361,7 +375,7 @@ static void exynos5_init_pmu(void)
361 /* 375 /*
362 * Disable WFI/WFE on XXX_OPTION 376 * Disable WFI/WFE on XXX_OPTION
363 */ 377 */
364 for (i = 0 ; i < ARRAY_SIZE(exynos5_list_disable_wfi_wfe) ; i++) { 378 for (i = 0; i < ARRAY_SIZE(exynos5_list_disable_wfi_wfe); i++) {
365 tmp = pmu_raw_readl(exynos5_list_disable_wfi_wfe[i]); 379 tmp = pmu_raw_readl(exynos5_list_disable_wfi_wfe[i]);
366 tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE | 380 tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE |
367 EXYNOS5_OPTION_USE_STANDBYWFI); 381 EXYNOS5_OPTION_USE_STANDBYWFI);
@@ -373,51 +387,124 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
373{ 387{
374 unsigned int i; 388 unsigned int i;
375 389
376 if (soc_is_exynos5250()) 390 const struct exynos_pmu_data *pmu_data = pmu_context->pmu_data;
377 exynos5_init_pmu();
378 391
379 for (i = 0; (exynos_pmu_config[i].offset != PMU_TABLE_END) ; i++) 392 if (pmu_data->powerdown_conf)
380 pmu_raw_writel(exynos_pmu_config[i].val[mode], 393 pmu_data->powerdown_conf(mode);
381 exynos_pmu_config[i].offset);
382 394
383 if (soc_is_exynos4412()) { 395 if (pmu_data->pmu_config) {
384 for (i = 0; exynos4412_pmu_config[i].offset != PMU_TABLE_END ; i++) 396 for (i = 0; (pmu_data->pmu_config[i].offset != PMU_TABLE_END); i++)
385 pmu_raw_writel(exynos4412_pmu_config[i].val[mode], 397 pmu_raw_writel(pmu_data->pmu_config[i].val[mode],
386 exynos4412_pmu_config[i].offset); 398 pmu_data->pmu_config[i].offset);
399 }
400
401 if (pmu_data->pmu_config_extra) {
402 for (i = 0; pmu_data->pmu_config_extra[i].offset != PMU_TABLE_END; i++)
403 pmu_raw_writel(pmu_data->pmu_config_extra[i].val[mode],
404 pmu_data->pmu_config_extra[i].offset);
387 } 405 }
388} 406}
389 407
390static int __init exynos_pmu_init(void) 408static void exynos5250_pmu_init(void)
391{ 409{
392 unsigned int value; 410 unsigned int value;
411 /*
412 * When SYS_WDTRESET is set, watchdog timer reset request
413 * is ignored by power management unit.
414 */
415 value = pmu_raw_readl(EXYNOS5_AUTO_WDTRESET_DISABLE);
416 value &= ~EXYNOS5_SYS_WDTRESET;
417 pmu_raw_writel(value, EXYNOS5_AUTO_WDTRESET_DISABLE);
418
419 value = pmu_raw_readl(EXYNOS5_MASK_WDTRESET_REQUEST);
420 value &= ~EXYNOS5_SYS_WDTRESET;
421 pmu_raw_writel(value, EXYNOS5_MASK_WDTRESET_REQUEST);
422}
423
424static const struct exynos_pmu_data exynos4210_pmu_data = {
425 .pmu_config = exynos4210_pmu_config,
426};
427
428static const struct exynos_pmu_data exynos4212_pmu_data = {
429 .pmu_config = exynos4x12_pmu_config,
430};
431
432static const struct exynos_pmu_data exynos4412_pmu_data = {
433 .pmu_config = exynos4x12_pmu_config,
434 .pmu_config_extra = exynos4412_pmu_config,
435};
436
437static const struct exynos_pmu_data exynos5250_pmu_data = {
438 .pmu_config = exynos5250_pmu_config,
439 .pmu_init = exynos5250_pmu_init,
440 .powerdown_conf = exynos5_powerdown_conf,
441};
442
443/*
444 * PMU platform driver and devicetree bindings.
445 */
446static const struct of_device_id exynos_pmu_of_device_ids[] = {
447 {
448 .compatible = "samsung,exynos4210-pmu",
449 .data = &exynos4210_pmu_data,
450 }, {
451 .compatible = "samsung,exynos4212-pmu",
452 .data = &exynos4212_pmu_data,
453 }, {
454 .compatible = "samsung,exynos4412-pmu",
455 .data = &exynos4412_pmu_data,
456 }, {
457 .compatible = "samsung,exynos5250-pmu",
458 .data = &exynos5250_pmu_data,
459 },
460 { /*sentinel*/ },
461};
393 462
394 exynos_pmu_config = exynos4210_pmu_config; 463static int exynos_pmu_probe(struct platform_device *pdev)
395 464{
396 if (soc_is_exynos4210()) { 465 const struct of_device_id *match;
397 exynos_pmu_config = exynos4210_pmu_config; 466 struct device *dev = &pdev->dev;
398 pr_info("EXYNOS4210 PMU Initialize\n"); 467 struct resource *res;
399 } else if (soc_is_exynos4212() || soc_is_exynos4412()) { 468
400 exynos_pmu_config = exynos4x12_pmu_config; 469 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
401 pr_info("EXYNOS4x12 PMU Initialize\n"); 470 pmu_base_addr = devm_ioremap_resource(dev, res);
402 } else if (soc_is_exynos5250()) { 471 if (IS_ERR(pmu_base_addr))
403 /* 472 return PTR_ERR(pmu_base_addr);
404 * When SYS_WDTRESET is set, watchdog timer reset request 473
405 * is ignored by power management unit. 474 pmu_context = devm_kzalloc(&pdev->dev,
406 */ 475 sizeof(struct exynos_pmu_context),
407 value = pmu_raw_readl(EXYNOS5_AUTO_WDTRESET_DISABLE); 476 GFP_KERNEL);
408 value &= ~EXYNOS5_SYS_WDTRESET; 477 if (!pmu_context) {
409 pmu_raw_writel(value, EXYNOS5_AUTO_WDTRESET_DISABLE); 478 dev_err(dev, "Cannot allocate memory.\n");
410 479 return -ENOMEM;
411 value = pmu_raw_readl(EXYNOS5_MASK_WDTRESET_REQUEST);
412 value &= ~EXYNOS5_SYS_WDTRESET;
413 pmu_raw_writel(value, EXYNOS5_MASK_WDTRESET_REQUEST);
414
415 exynos_pmu_config = exynos5250_pmu_config;
416 pr_info("EXYNOS5250 PMU Initialize\n");
417 } else {
418 pr_info("EXYNOS: PMU not supported\n");
419 } 480 }
481 pmu_context->dev = dev;
482
483 match = of_match_node(exynos_pmu_of_device_ids, dev->of_node);
484
485 pmu_context->pmu_data = match->data;
420 486
487 if (pmu_context->pmu_data->pmu_init)
488 pmu_context->pmu_data->pmu_init();
489
490 platform_set_drvdata(pdev, pmu_context);
491
492 dev_dbg(dev, "Exynos PMU Driver probe done\n");
421 return 0; 493 return 0;
422} 494}
423arch_initcall(exynos_pmu_init); 495
496static struct platform_driver exynos_pmu_driver = {
497 .driver = {
498 .name = "exynos-pmu",
499 .owner = THIS_MODULE,
500 .of_match_table = exynos_pmu_of_device_ids,
501 },
502 .probe = exynos_pmu_probe,
503};
504
505static int __init exynos_pmu_init(void)
506{
507 return platform_driver_register(&exynos_pmu_driver);
508
509}
510postcore_initcall(exynos_pmu_init);