diff options
Diffstat (limited to 'drivers/devfreq/exynos')
-rw-r--r-- | drivers/devfreq/exynos/Makefile | 2 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos4_bus.c | 164 |
2 files changed, 62 insertions, 104 deletions
diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile index bfaaf5b0d61d..49bc9175f923 100644 --- a/drivers/devfreq/exynos/Makefile +++ b/drivers/devfreq/exynos/Makefile | |||
@@ -1,3 +1,3 @@ | |||
1 | # Exynos DEVFREQ Drivers | 1 | # Exynos DEVFREQ Drivers |
2 | obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos4_bus.o | 2 | obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o |
3 | obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o | 3 | obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o |
diff --git a/drivers/devfreq/exynos/exynos4_bus.c b/drivers/devfreq/exynos/exynos4_bus.c index 5a48d162b228..228c658c0e8c 100644 --- a/drivers/devfreq/exynos/exynos4_bus.c +++ b/drivers/devfreq/exynos/exynos4_bus.c | |||
@@ -25,15 +25,16 @@ | |||
25 | #include <linux/regulator/consumer.h> | 25 | #include <linux/regulator/consumer.h> |
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | 27 | ||
28 | #include <mach/map.h> | ||
29 | |||
30 | #include "exynos_ppmu.h" | ||
31 | #include "exynos4_bus.h" | ||
32 | |||
28 | /* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */ | 33 | /* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */ |
29 | #ifdef CONFIG_EXYNOS_ASV | 34 | #ifdef CONFIG_EXYNOS_ASV |
30 | extern unsigned int exynos_result_of_asv; | 35 | extern unsigned int exynos_result_of_asv; |
31 | #endif | 36 | #endif |
32 | 37 | ||
33 | #include <mach/map.h> | ||
34 | |||
35 | #include "exynos4_bus.h" | ||
36 | |||
37 | #define MAX_SAFEVOLT 1200000 /* 1.2V */ | 38 | #define MAX_SAFEVOLT 1200000 /* 1.2V */ |
38 | 39 | ||
39 | enum exynos4_busf_type { | 40 | enum exynos4_busf_type { |
@@ -44,22 +45,6 @@ enum exynos4_busf_type { | |||
44 | /* Assume that the bus is saturated if the utilization is 40% */ | 45 | /* Assume that the bus is saturated if the utilization is 40% */ |
45 | #define BUS_SATURATION_RATIO 40 | 46 | #define BUS_SATURATION_RATIO 40 |
46 | 47 | ||
47 | enum ppmu_counter { | ||
48 | PPMU_PMNCNT0 = 0, | ||
49 | PPMU_PMCCNT1, | ||
50 | PPMU_PMNCNT2, | ||
51 | PPMU_PMNCNT3, | ||
52 | PPMU_PMNCNT_MAX, | ||
53 | }; | ||
54 | struct exynos4_ppmu { | ||
55 | void __iomem *hw_base; | ||
56 | unsigned int ccnt; | ||
57 | unsigned int event; | ||
58 | unsigned int count[PPMU_PMNCNT_MAX]; | ||
59 | bool ccnt_overflow; | ||
60 | bool count_overflow[PPMU_PMNCNT_MAX]; | ||
61 | }; | ||
62 | |||
63 | enum busclk_level_idx { | 48 | enum busclk_level_idx { |
64 | LV_0 = 0, | 49 | LV_0 = 0, |
65 | LV_1, | 50 | LV_1, |
@@ -68,6 +53,13 @@ enum busclk_level_idx { | |||
68 | LV_4, | 53 | LV_4, |
69 | _LV_END | 54 | _LV_END |
70 | }; | 55 | }; |
56 | |||
57 | enum exynos_ppmu_idx { | ||
58 | PPMU_DMC0, | ||
59 | PPMU_DMC1, | ||
60 | PPMU_END, | ||
61 | }; | ||
62 | |||
71 | #define EX4210_LV_MAX LV_2 | 63 | #define EX4210_LV_MAX LV_2 |
72 | #define EX4x12_LV_MAX LV_4 | 64 | #define EX4x12_LV_MAX LV_4 |
73 | #define EX4210_LV_NUM (LV_2 + 1) | 65 | #define EX4210_LV_NUM (LV_2 + 1) |
@@ -91,7 +83,7 @@ struct busfreq_data { | |||
91 | struct regulator *vdd_int; | 83 | struct regulator *vdd_int; |
92 | struct regulator *vdd_mif; /* Exynos4412/4212 only */ | 84 | struct regulator *vdd_mif; /* Exynos4412/4212 only */ |
93 | struct busfreq_opp_info curr_oppinfo; | 85 | struct busfreq_opp_info curr_oppinfo; |
94 | struct exynos4_ppmu dmc[2]; | 86 | struct exynos_ppmu ppmu[PPMU_END]; |
95 | 87 | ||
96 | struct notifier_block pm_notifier; | 88 | struct notifier_block pm_notifier; |
97 | struct mutex lock; | 89 | struct mutex lock; |
@@ -101,12 +93,6 @@ struct busfreq_data { | |||
101 | unsigned int top_divtable[_LV_END]; | 93 | unsigned int top_divtable[_LV_END]; |
102 | }; | 94 | }; |
103 | 95 | ||
104 | struct bus_opp_table { | ||
105 | unsigned int idx; | ||
106 | unsigned long clk; | ||
107 | unsigned long volt; | ||
108 | }; | ||
109 | |||
110 | /* 4210 controls clock of mif and voltage of int */ | 96 | /* 4210 controls clock of mif and voltage of int */ |
111 | static struct bus_opp_table exynos4210_busclk_table[] = { | 97 | static struct bus_opp_table exynos4210_busclk_table[] = { |
112 | {LV_0, 400000, 1150000}, | 98 | {LV_0, 400000, 1150000}, |
@@ -524,27 +510,22 @@ static int exynos4x12_set_busclk(struct busfreq_data *data, | |||
524 | return 0; | 510 | return 0; |
525 | } | 511 | } |
526 | 512 | ||
527 | |||
528 | static void busfreq_mon_reset(struct busfreq_data *data) | 513 | static void busfreq_mon_reset(struct busfreq_data *data) |
529 | { | 514 | { |
530 | unsigned int i; | 515 | unsigned int i; |
531 | 516 | ||
532 | for (i = 0; i < 2; i++) { | 517 | for (i = 0; i < PPMU_END; i++) { |
533 | void __iomem *ppmu_base = data->dmc[i].hw_base; | 518 | void __iomem *ppmu_base = data->ppmu[i].hw_base; |
534 | 519 | ||
535 | /* Reset PPMU */ | 520 | /* Reset the performance and cycle counters */ |
536 | __raw_writel(0x8000000f, ppmu_base + 0xf010); | 521 | exynos_ppmu_reset(ppmu_base); |
537 | __raw_writel(0x8000000f, ppmu_base + 0xf050); | ||
538 | __raw_writel(0x6, ppmu_base + 0xf000); | ||
539 | __raw_writel(0x0, ppmu_base + 0xf100); | ||
540 | 522 | ||
541 | /* Set PPMU Event */ | 523 | /* Setup count registers to monitor read/write transactions */ |
542 | data->dmc[i].event = 0x6; | 524 | data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; |
543 | __raw_writel(((data->dmc[i].event << 12) | 0x1), | 525 | exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, |
544 | ppmu_base + 0xfc); | 526 | data->ppmu[i].event[PPMU_PMNCNT3]); |
545 | 527 | ||
546 | /* Start PPMU */ | 528 | exynos_ppmu_start(ppmu_base); |
547 | __raw_writel(0x1, ppmu_base + 0xf000); | ||
548 | } | 529 | } |
549 | } | 530 | } |
550 | 531 | ||
@@ -552,23 +533,20 @@ static void exynos4_read_ppmu(struct busfreq_data *data) | |||
552 | { | 533 | { |
553 | int i, j; | 534 | int i, j; |
554 | 535 | ||
555 | for (i = 0; i < 2; i++) { | 536 | for (i = 0; i < PPMU_END; i++) { |
556 | void __iomem *ppmu_base = data->dmc[i].hw_base; | 537 | void __iomem *ppmu_base = data->ppmu[i].hw_base; |
557 | u32 overflow; | ||
558 | 538 | ||
559 | /* Stop PPMU */ | 539 | exynos_ppmu_stop(ppmu_base); |
560 | __raw_writel(0x0, ppmu_base + 0xf000); | ||
561 | 540 | ||
562 | /* Update local data from PPMU */ | 541 | /* Update local data from PPMU */ |
563 | overflow = __raw_readl(ppmu_base + 0xf050); | 542 | data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); |
564 | 543 | ||
565 | data->dmc[i].ccnt = __raw_readl(ppmu_base + 0xf100); | 544 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { |
566 | data->dmc[i].ccnt_overflow = overflow & (1 << 31); | 545 | if (data->ppmu[i].event[j] == 0) |
567 | 546 | data->ppmu[i].count[j] = 0; | |
568 | for (j = 0; j < PPMU_PMNCNT_MAX; j++) { | 547 | else |
569 | data->dmc[i].count[j] = __raw_readl( | 548 | data->ppmu[i].count[j] = |
570 | ppmu_base + (0xf110 + (0x10 * j))); | 549 | exynos_ppmu_read(ppmu_base, j); |
571 | data->dmc[i].count_overflow[j] = overflow & (1 << j); | ||
572 | } | 550 | } |
573 | } | 551 | } |
574 | 552 | ||
@@ -698,66 +676,42 @@ out: | |||
698 | return err; | 676 | return err; |
699 | } | 677 | } |
700 | 678 | ||
701 | static int exynos4_get_busier_dmc(struct busfreq_data *data) | 679 | static int exynos4_get_busier_ppmu(struct busfreq_data *data) |
702 | { | 680 | { |
703 | u64 p0 = data->dmc[0].count[0]; | 681 | int i, j; |
704 | u64 p1 = data->dmc[1].count[0]; | 682 | int busy = 0; |
705 | 683 | unsigned int temp = 0; | |
706 | p0 *= data->dmc[1].ccnt; | 684 | |
707 | p1 *= data->dmc[0].ccnt; | 685 | for (i = 0; i < PPMU_END; i++) { |
708 | 686 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | |
709 | if (data->dmc[1].ccnt == 0) | 687 | if (data->ppmu[i].count[j] > temp) { |
710 | return 0; | 688 | temp = data->ppmu[i].count[j]; |
689 | busy = i; | ||
690 | } | ||
691 | } | ||
692 | } | ||
711 | 693 | ||
712 | if (p0 > p1) | 694 | return busy; |
713 | return 0; | ||
714 | return 1; | ||
715 | } | 695 | } |
716 | 696 | ||
717 | static int exynos4_bus_get_dev_status(struct device *dev, | 697 | static int exynos4_bus_get_dev_status(struct device *dev, |
718 | struct devfreq_dev_status *stat) | 698 | struct devfreq_dev_status *stat) |
719 | { | 699 | { |
720 | struct busfreq_data *data = dev_get_drvdata(dev); | 700 | struct busfreq_data *data = dev_get_drvdata(dev); |
721 | int busier_dmc; | 701 | int busier; |
722 | int cycles_x2 = 2; /* 2 x cycles */ | ||
723 | void __iomem *addr; | ||
724 | u32 timing; | ||
725 | u32 memctrl; | ||
726 | 702 | ||
727 | exynos4_read_ppmu(data); | 703 | exynos4_read_ppmu(data); |
728 | busier_dmc = exynos4_get_busier_dmc(data); | 704 | busier = exynos4_get_busier_ppmu(data); |
729 | stat->current_frequency = data->curr_oppinfo.rate; | 705 | stat->current_frequency = data->curr_oppinfo.rate; |
730 | 706 | ||
731 | if (busier_dmc) | ||
732 | addr = S5P_VA_DMC1; | ||
733 | else | ||
734 | addr = S5P_VA_DMC0; | ||
735 | |||
736 | memctrl = __raw_readl(addr + 0x04); /* one of DDR2/3/LPDDR2 */ | ||
737 | timing = __raw_readl(addr + 0x38); /* CL or WL/RL values */ | ||
738 | |||
739 | switch ((memctrl >> 8) & 0xf) { | ||
740 | case 0x4: /* DDR2 */ | ||
741 | cycles_x2 = ((timing >> 16) & 0xf) * 2; | ||
742 | break; | ||
743 | case 0x5: /* LPDDR2 */ | ||
744 | case 0x6: /* DDR3 */ | ||
745 | cycles_x2 = ((timing >> 8) & 0xf) + ((timing >> 0) & 0xf); | ||
746 | break; | ||
747 | default: | ||
748 | pr_err("%s: Unknown Memory Type(%d).\n", __func__, | ||
749 | (memctrl >> 8) & 0xf); | ||
750 | return -EINVAL; | ||
751 | } | ||
752 | |||
753 | /* Number of cycles spent on memory access */ | 707 | /* Number of cycles spent on memory access */ |
754 | stat->busy_time = data->dmc[busier_dmc].count[0] / 2 * (cycles_x2 + 2); | 708 | stat->busy_time = data->ppmu[busier].count[PPMU_PMNCNT3]; |
755 | stat->busy_time *= 100 / BUS_SATURATION_RATIO; | 709 | stat->busy_time *= 100 / BUS_SATURATION_RATIO; |
756 | stat->total_time = data->dmc[busier_dmc].ccnt; | 710 | stat->total_time = data->ppmu[busier].ccnt; |
757 | 711 | ||
758 | /* If the counters have overflown, retry */ | 712 | /* If the counters have overflown, retry */ |
759 | if (data->dmc[busier_dmc].ccnt_overflow || | 713 | if (data->ppmu[busier].ccnt_overflow || |
760 | data->dmc[busier_dmc].count_overflow[0]) | 714 | data->ppmu[busier].count_overflow[0]) |
761 | return -EAGAIN; | 715 | return -EAGAIN; |
762 | 716 | ||
763 | return 0; | 717 | return 0; |
@@ -1023,8 +977,8 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) | |||
1023 | } | 977 | } |
1024 | 978 | ||
1025 | data->type = pdev->id_entry->driver_data; | 979 | data->type = pdev->id_entry->driver_data; |
1026 | data->dmc[0].hw_base = S5P_VA_DMC0; | 980 | data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0; |
1027 | data->dmc[1].hw_base = S5P_VA_DMC1; | 981 | data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1; |
1028 | data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; | 982 | data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; |
1029 | data->dev = dev; | 983 | data->dev = dev; |
1030 | mutex_init(&data->lock); | 984 | mutex_init(&data->lock); |
@@ -1074,13 +1028,17 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) | |||
1074 | 1028 | ||
1075 | platform_set_drvdata(pdev, data); | 1029 | platform_set_drvdata(pdev, data); |
1076 | 1030 | ||
1077 | busfreq_mon_reset(data); | ||
1078 | |||
1079 | data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, | 1031 | data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, |
1080 | "simple_ondemand", NULL); | 1032 | "simple_ondemand", NULL); |
1081 | if (IS_ERR(data->devfreq)) | 1033 | if (IS_ERR(data->devfreq)) |
1082 | return PTR_ERR(data->devfreq); | 1034 | return PTR_ERR(data->devfreq); |
1083 | 1035 | ||
1036 | /* | ||
1037 | * Start PPMU (Performance Profiling Monitoring Unit) to check | ||
1038 | * utilization of each IP in the Exynos4 SoC. | ||
1039 | */ | ||
1040 | busfreq_mon_reset(data); | ||
1041 | |||
1084 | /* Register opp_notifier for Exynos4 busfreq */ | 1042 | /* Register opp_notifier for Exynos4 busfreq */ |
1085 | err = devfreq_register_opp_notifier(dev, data->devfreq); | 1043 | err = devfreq_register_opp_notifier(dev, data->devfreq); |
1086 | if (err < 0) { | 1044 | if (err < 0) { |