diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-06-03 17:13:13 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-06-03 17:13:13 -0400 |
commit | 42a09284fab8abc15c8554f2e2aa2368161c77c6 (patch) | |
tree | 45c9b33c1e7667628baad14f30eeba540680a8c8 | |
parent | cd0c5bd391c45ace8527c105b79017149e695f85 (diff) | |
parent | 6392bfd57654338f0df8346dd058e91b8215ab11 (diff) |
Merge branch 'pm-devfreq'
* pm-devfreq:
PM / devfreq: remove checks for CONFIG_EXYNOS_ASV
PM / devfreq: exynos5: Use devm_devfreq_* function using device resource management
PM / devfreq: exynos4: Use devm_devfreq_* function using device resource management
PM / devfreq: Add devm_devfreq_{register,unregister}_opp_notfier function
PM / devfreq: Add resource-managed function for devfreq device
PM / devfreq: Fix devfreq_remove_device() to improve the sequence of resource free
PM / devfreq: exynos: make more PPMU code common
PM / devfreq: exynos5: introduce struct busfreq_ppmu_data
PM / devfreq: exynos4: introduce struct busfreq_ppmu_data
PM / devfreq: exynos4: use common PPMU code
PM / devfreq: exynos5: Add CONFIG_PM_OPP dependency to fix probe fail
PM / devfreq: exynos5: Use SIMPLE_DEV_PM_OPS macro
PM / devfreq: exynos4: Add CONFIG_PM_OPP dependency to fix probe fail
PM / devfreq: exynos4: Use SIMPLE_DEV_PM_OPS macro
PM / devfreq: exynos4: Fix bug of resource leak and code clean on probe()
-rw-r--r-- | drivers/devfreq/Kconfig | 5 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 125 | ||||
-rw-r--r-- | drivers/devfreq/exynos/Makefile | 2 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos4_bus.c | 219 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos5_bus.c | 130 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos_ppmu.c | 60 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos_ppmu.h | 8 | ||||
-rw-r--r-- | include/linux/devfreq.h | 35 |
8 files changed, 317 insertions, 267 deletions
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 7d2f43550700..49e74c1fc639 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig | |||
@@ -70,19 +70,20 @@ config ARM_EXYNOS4_BUS_DEVFREQ | |||
70 | depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM | 70 | depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM |
71 | select ARCH_HAS_OPP | 71 | select ARCH_HAS_OPP |
72 | select DEVFREQ_GOV_SIMPLE_ONDEMAND | 72 | select DEVFREQ_GOV_SIMPLE_ONDEMAND |
73 | select PM_OPP | ||
73 | help | 74 | help |
74 | This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int) | 75 | This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int) |
75 | and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int). | 76 | and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int). |
76 | It reads PPMU counters of memory controllers and adjusts | 77 | It reads PPMU counters of memory controllers and adjusts |
77 | the operating frequencies and voltages with OPP support. | 78 | the operating frequencies and voltages with OPP support. |
78 | To operate with optimal voltages, ASV support is required | 79 | This does not yet operate with optimal voltages. |
79 | (CONFIG_EXYNOS_ASV). | ||
80 | 80 | ||
81 | config ARM_EXYNOS5_BUS_DEVFREQ | 81 | config ARM_EXYNOS5_BUS_DEVFREQ |
82 | bool "ARM Exynos5250 Bus DEVFREQ Driver" | 82 | bool "ARM Exynos5250 Bus DEVFREQ Driver" |
83 | depends on SOC_EXYNOS5250 | 83 | depends on SOC_EXYNOS5250 |
84 | select ARCH_HAS_OPP | 84 | select ARCH_HAS_OPP |
85 | select DEVFREQ_GOV_SIMPLE_ONDEMAND | 85 | select DEVFREQ_GOV_SIMPLE_ONDEMAND |
86 | select PM_OPP | ||
86 | help | 87 | help |
87 | This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int). | 88 | This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int). |
88 | It reads PPMU counters of memory controllers and adjusts the | 89 | It reads PPMU counters of memory controllers and adjusts the |
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 2042ec3656ba..9f90369dd6bd 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
@@ -394,7 +394,7 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, | |||
394 | * @devfreq: the devfreq struct | 394 | * @devfreq: the devfreq struct |
395 | * @skip: skip calling device_unregister(). | 395 | * @skip: skip calling device_unregister(). |
396 | */ | 396 | */ |
397 | static void _remove_devfreq(struct devfreq *devfreq, bool skip) | 397 | static void _remove_devfreq(struct devfreq *devfreq) |
398 | { | 398 | { |
399 | mutex_lock(&devfreq_list_lock); | 399 | mutex_lock(&devfreq_list_lock); |
400 | if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { | 400 | if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { |
@@ -412,11 +412,6 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip) | |||
412 | if (devfreq->profile->exit) | 412 | if (devfreq->profile->exit) |
413 | devfreq->profile->exit(devfreq->dev.parent); | 413 | devfreq->profile->exit(devfreq->dev.parent); |
414 | 414 | ||
415 | if (!skip && get_device(&devfreq->dev)) { | ||
416 | device_unregister(&devfreq->dev); | ||
417 | put_device(&devfreq->dev); | ||
418 | } | ||
419 | |||
420 | mutex_destroy(&devfreq->lock); | 415 | mutex_destroy(&devfreq->lock); |
421 | kfree(devfreq); | 416 | kfree(devfreq); |
422 | } | 417 | } |
@@ -426,14 +421,12 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip) | |||
426 | * @dev: the devfreq device | 421 | * @dev: the devfreq device |
427 | * | 422 | * |
428 | * This calls _remove_devfreq() if _remove_devfreq() is not called. | 423 | * This calls _remove_devfreq() if _remove_devfreq() is not called. |
429 | * Note that devfreq_dev_release() could be called by _remove_devfreq() as | ||
430 | * well as by others unregistering the device. | ||
431 | */ | 424 | */ |
432 | static void devfreq_dev_release(struct device *dev) | 425 | static void devfreq_dev_release(struct device *dev) |
433 | { | 426 | { |
434 | struct devfreq *devfreq = to_devfreq(dev); | 427 | struct devfreq *devfreq = to_devfreq(dev); |
435 | 428 | ||
436 | _remove_devfreq(devfreq, true); | 429 | _remove_devfreq(devfreq); |
437 | } | 430 | } |
438 | 431 | ||
439 | /** | 432 | /** |
@@ -544,12 +537,76 @@ int devfreq_remove_device(struct devfreq *devfreq) | |||
544 | if (!devfreq) | 537 | if (!devfreq) |
545 | return -EINVAL; | 538 | return -EINVAL; |
546 | 539 | ||
547 | _remove_devfreq(devfreq, false); | 540 | device_unregister(&devfreq->dev); |
541 | put_device(&devfreq->dev); | ||
548 | 542 | ||
549 | return 0; | 543 | return 0; |
550 | } | 544 | } |
551 | EXPORT_SYMBOL(devfreq_remove_device); | 545 | EXPORT_SYMBOL(devfreq_remove_device); |
552 | 546 | ||
547 | static int devm_devfreq_dev_match(struct device *dev, void *res, void *data) | ||
548 | { | ||
549 | struct devfreq **r = res; | ||
550 | |||
551 | if (WARN_ON(!r || !*r)) | ||
552 | return 0; | ||
553 | |||
554 | return *r == data; | ||
555 | } | ||
556 | |||
557 | static void devm_devfreq_dev_release(struct device *dev, void *res) | ||
558 | { | ||
559 | devfreq_remove_device(*(struct devfreq **)res); | ||
560 | } | ||
561 | |||
562 | /** | ||
563 | * devm_devfreq_add_device() - Resource-managed devfreq_add_device() | ||
564 | * @dev: the device to add devfreq feature. | ||
565 | * @profile: device-specific profile to run devfreq. | ||
566 | * @governor_name: name of the policy to choose frequency. | ||
567 | * @data: private data for the governor. The devfreq framework does not | ||
568 | * touch this value. | ||
569 | * | ||
570 | * This function manages automatically the memory of devfreq device using device | ||
571 | * resource management and simplify the free operation for memory of devfreq | ||
572 | * device. | ||
573 | */ | ||
574 | struct devfreq *devm_devfreq_add_device(struct device *dev, | ||
575 | struct devfreq_dev_profile *profile, | ||
576 | const char *governor_name, | ||
577 | void *data) | ||
578 | { | ||
579 | struct devfreq **ptr, *devfreq; | ||
580 | |||
581 | ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL); | ||
582 | if (!ptr) | ||
583 | return ERR_PTR(-ENOMEM); | ||
584 | |||
585 | devfreq = devfreq_add_device(dev, profile, governor_name, data); | ||
586 | if (IS_ERR(devfreq)) { | ||
587 | devres_free(ptr); | ||
588 | return ERR_PTR(-ENOMEM); | ||
589 | } | ||
590 | |||
591 | *ptr = devfreq; | ||
592 | devres_add(dev, ptr); | ||
593 | |||
594 | return devfreq; | ||
595 | } | ||
596 | EXPORT_SYMBOL(devm_devfreq_add_device); | ||
597 | |||
598 | /** | ||
599 | * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() | ||
600 | * @dev: the device to add devfreq feature. | ||
601 | * @devfreq: the devfreq instance to be removed | ||
602 | */ | ||
603 | void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) | ||
604 | { | ||
605 | WARN_ON(devres_release(dev, devm_devfreq_dev_release, | ||
606 | devm_devfreq_dev_match, devfreq)); | ||
607 | } | ||
608 | EXPORT_SYMBOL(devm_devfreq_remove_device); | ||
609 | |||
553 | /** | 610 | /** |
554 | * devfreq_suspend_device() - Suspend devfreq of a device. | 611 | * devfreq_suspend_device() - Suspend devfreq of a device. |
555 | * @devfreq: the devfreq instance to be suspended | 612 | * @devfreq: the devfreq instance to be suspended |
@@ -1112,6 +1169,54 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) | |||
1112 | return ret; | 1169 | return ret; |
1113 | } | 1170 | } |
1114 | 1171 | ||
1172 | static void devm_devfreq_opp_release(struct device *dev, void *res) | ||
1173 | { | ||
1174 | devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res); | ||
1175 | } | ||
1176 | |||
1177 | /** | ||
1178 | * devm_ devfreq_register_opp_notifier() | ||
1179 | * - Resource-managed devfreq_register_opp_notifier() | ||
1180 | * @dev: The devfreq user device. (parent of devfreq) | ||
1181 | * @devfreq: The devfreq object. | ||
1182 | */ | ||
1183 | int devm_devfreq_register_opp_notifier(struct device *dev, | ||
1184 | struct devfreq *devfreq) | ||
1185 | { | ||
1186 | struct devfreq **ptr; | ||
1187 | int ret; | ||
1188 | |||
1189 | ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL); | ||
1190 | if (!ptr) | ||
1191 | return -ENOMEM; | ||
1192 | |||
1193 | ret = devfreq_register_opp_notifier(dev, devfreq); | ||
1194 | if (ret) { | ||
1195 | devres_free(ptr); | ||
1196 | return ret; | ||
1197 | } | ||
1198 | |||
1199 | *ptr = devfreq; | ||
1200 | devres_add(dev, ptr); | ||
1201 | |||
1202 | return 0; | ||
1203 | } | ||
1204 | EXPORT_SYMBOL(devm_devfreq_register_opp_notifier); | ||
1205 | |||
1206 | /** | ||
1207 | * devm_devfreq_unregister_opp_notifier() | ||
1208 | * - Resource-managed devfreq_unregister_opp_notifier() | ||
1209 | * @dev: The devfreq user device. (parent of devfreq) | ||
1210 | * @devfreq: The devfreq object. | ||
1211 | */ | ||
1212 | void devm_devfreq_unregister_opp_notifier(struct device *dev, | ||
1213 | struct devfreq *devfreq) | ||
1214 | { | ||
1215 | WARN_ON(devres_release(dev, devm_devfreq_opp_release, | ||
1216 | devm_devfreq_dev_match, devfreq)); | ||
1217 | } | ||
1218 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); | ||
1219 | |||
1115 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | 1220 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
1116 | MODULE_DESCRIPTION("devfreq class support"); | 1221 | MODULE_DESCRIPTION("devfreq class support"); |
1117 | MODULE_LICENSE("GPL"); | 1222 | MODULE_LICENSE("GPL"); |
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 e07b0c68c715..d9b08d3b6830 100644 --- a/drivers/devfreq/exynos/exynos4_bus.c +++ b/drivers/devfreq/exynos/exynos4_bus.c | |||
@@ -25,13 +25,9 @@ | |||
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 | /* Exynos4 ASV has been in the mailing list, but not upstreamed, yet. */ | ||
29 | #ifdef CONFIG_EXYNOS_ASV | ||
30 | extern unsigned int exynos_result_of_asv; | ||
31 | #endif | ||
32 | |||
33 | #include <mach/map.h> | 28 | #include <mach/map.h> |
34 | 29 | ||
30 | #include "exynos_ppmu.h" | ||
35 | #include "exynos4_bus.h" | 31 | #include "exynos4_bus.h" |
36 | 32 | ||
37 | #define MAX_SAFEVOLT 1200000 /* 1.2V */ | 33 | #define MAX_SAFEVOLT 1200000 /* 1.2V */ |
@@ -44,22 +40,6 @@ enum exynos4_busf_type { | |||
44 | /* Assume that the bus is saturated if the utilization is 40% */ | 40 | /* Assume that the bus is saturated if the utilization is 40% */ |
45 | #define BUS_SATURATION_RATIO 40 | 41 | #define BUS_SATURATION_RATIO 40 |
46 | 42 | ||
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 { | 43 | enum busclk_level_idx { |
64 | LV_0 = 0, | 44 | LV_0 = 0, |
65 | LV_1, | 45 | LV_1, |
@@ -68,6 +48,13 @@ enum busclk_level_idx { | |||
68 | LV_4, | 48 | LV_4, |
69 | _LV_END | 49 | _LV_END |
70 | }; | 50 | }; |
51 | |||
52 | enum exynos_ppmu_idx { | ||
53 | PPMU_DMC0, | ||
54 | PPMU_DMC1, | ||
55 | PPMU_END, | ||
56 | }; | ||
57 | |||
71 | #define EX4210_LV_MAX LV_2 | 58 | #define EX4210_LV_MAX LV_2 |
72 | #define EX4x12_LV_MAX LV_4 | 59 | #define EX4x12_LV_MAX LV_4 |
73 | #define EX4210_LV_NUM (LV_2 + 1) | 60 | #define EX4210_LV_NUM (LV_2 + 1) |
@@ -91,7 +78,7 @@ struct busfreq_data { | |||
91 | struct regulator *vdd_int; | 78 | struct regulator *vdd_int; |
92 | struct regulator *vdd_mif; /* Exynos4412/4212 only */ | 79 | struct regulator *vdd_mif; /* Exynos4412/4212 only */ |
93 | struct busfreq_opp_info curr_oppinfo; | 80 | struct busfreq_opp_info curr_oppinfo; |
94 | struct exynos4_ppmu dmc[2]; | 81 | struct busfreq_ppmu_data ppmu_data; |
95 | 82 | ||
96 | struct notifier_block pm_notifier; | 83 | struct notifier_block pm_notifier; |
97 | struct mutex lock; | 84 | struct mutex lock; |
@@ -101,12 +88,6 @@ struct busfreq_data { | |||
101 | unsigned int top_divtable[_LV_END]; | 88 | unsigned int top_divtable[_LV_END]; |
102 | }; | 89 | }; |
103 | 90 | ||
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 */ | 91 | /* 4210 controls clock of mif and voltage of int */ |
111 | static struct bus_opp_table exynos4210_busclk_table[] = { | 92 | static struct bus_opp_table exynos4210_busclk_table[] = { |
112 | {LV_0, 400000, 1150000}, | 93 | {LV_0, 400000, 1150000}, |
@@ -524,57 +505,6 @@ static int exynos4x12_set_busclk(struct busfreq_data *data, | |||
524 | return 0; | 505 | return 0; |
525 | } | 506 | } |
526 | 507 | ||
527 | |||
528 | static void busfreq_mon_reset(struct busfreq_data *data) | ||
529 | { | ||
530 | unsigned int i; | ||
531 | |||
532 | for (i = 0; i < 2; i++) { | ||
533 | void __iomem *ppmu_base = data->dmc[i].hw_base; | ||
534 | |||
535 | /* Reset PPMU */ | ||
536 | __raw_writel(0x8000000f, ppmu_base + 0xf010); | ||
537 | __raw_writel(0x8000000f, ppmu_base + 0xf050); | ||
538 | __raw_writel(0x6, ppmu_base + 0xf000); | ||
539 | __raw_writel(0x0, ppmu_base + 0xf100); | ||
540 | |||
541 | /* Set PPMU Event */ | ||
542 | data->dmc[i].event = 0x6; | ||
543 | __raw_writel(((data->dmc[i].event << 12) | 0x1), | ||
544 | ppmu_base + 0xfc); | ||
545 | |||
546 | /* Start PPMU */ | ||
547 | __raw_writel(0x1, ppmu_base + 0xf000); | ||
548 | } | ||
549 | } | ||
550 | |||
551 | static void exynos4_read_ppmu(struct busfreq_data *data) | ||
552 | { | ||
553 | int i, j; | ||
554 | |||
555 | for (i = 0; i < 2; i++) { | ||
556 | void __iomem *ppmu_base = data->dmc[i].hw_base; | ||
557 | u32 overflow; | ||
558 | |||
559 | /* Stop PPMU */ | ||
560 | __raw_writel(0x0, ppmu_base + 0xf000); | ||
561 | |||
562 | /* Update local data from PPMU */ | ||
563 | overflow = __raw_readl(ppmu_base + 0xf050); | ||
564 | |||
565 | data->dmc[i].ccnt = __raw_readl(ppmu_base + 0xf100); | ||
566 | data->dmc[i].ccnt_overflow = overflow & (1 << 31); | ||
567 | |||
568 | for (j = 0; j < PPMU_PMNCNT_MAX; j++) { | ||
569 | data->dmc[i].count[j] = __raw_readl( | ||
570 | ppmu_base + (0xf110 + (0x10 * j))); | ||
571 | data->dmc[i].count_overflow[j] = overflow & (1 << j); | ||
572 | } | ||
573 | } | ||
574 | |||
575 | busfreq_mon_reset(data); | ||
576 | } | ||
577 | |||
578 | static int exynos4x12_get_intspec(unsigned long mifclk) | 508 | static int exynos4x12_get_intspec(unsigned long mifclk) |
579 | { | 509 | { |
580 | int i = 0; | 510 | int i = 0; |
@@ -698,84 +628,35 @@ out: | |||
698 | return err; | 628 | return err; |
699 | } | 629 | } |
700 | 630 | ||
701 | static int exynos4_get_busier_dmc(struct busfreq_data *data) | ||
702 | { | ||
703 | u64 p0 = data->dmc[0].count[0]; | ||
704 | u64 p1 = data->dmc[1].count[0]; | ||
705 | |||
706 | p0 *= data->dmc[1].ccnt; | ||
707 | p1 *= data->dmc[0].ccnt; | ||
708 | |||
709 | if (data->dmc[1].ccnt == 0) | ||
710 | return 0; | ||
711 | |||
712 | if (p0 > p1) | ||
713 | return 0; | ||
714 | return 1; | ||
715 | } | ||
716 | |||
717 | static int exynos4_bus_get_dev_status(struct device *dev, | 631 | static int exynos4_bus_get_dev_status(struct device *dev, |
718 | struct devfreq_dev_status *stat) | 632 | struct devfreq_dev_status *stat) |
719 | { | 633 | { |
720 | struct busfreq_data *data = dev_get_drvdata(dev); | 634 | struct busfreq_data *data = dev_get_drvdata(dev); |
721 | int busier_dmc; | 635 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; |
722 | int cycles_x2 = 2; /* 2 x cycles */ | 636 | int busier; |
723 | void __iomem *addr; | ||
724 | u32 timing; | ||
725 | u32 memctrl; | ||
726 | |||
727 | exynos4_read_ppmu(data); | ||
728 | busier_dmc = exynos4_get_busier_dmc(data); | ||
729 | stat->current_frequency = data->curr_oppinfo.rate; | ||
730 | 637 | ||
731 | if (busier_dmc) | 638 | exynos_read_ppmu(ppmu_data); |
732 | addr = S5P_VA_DMC1; | 639 | busier = exynos_get_busier_ppmu(ppmu_data); |
733 | else | 640 | stat->current_frequency = data->curr_oppinfo.rate; |
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 | 641 | ||
753 | /* Number of cycles spent on memory access */ | 642 | /* Number of cycles spent on memory access */ |
754 | stat->busy_time = data->dmc[busier_dmc].count[0] / 2 * (cycles_x2 + 2); | 643 | stat->busy_time = ppmu_data->ppmu[busier].count[PPMU_PMNCNT3]; |
755 | stat->busy_time *= 100 / BUS_SATURATION_RATIO; | 644 | stat->busy_time *= 100 / BUS_SATURATION_RATIO; |
756 | stat->total_time = data->dmc[busier_dmc].ccnt; | 645 | stat->total_time = ppmu_data->ppmu[busier].ccnt; |
757 | 646 | ||
758 | /* If the counters have overflown, retry */ | 647 | /* If the counters have overflown, retry */ |
759 | if (data->dmc[busier_dmc].ccnt_overflow || | 648 | if (ppmu_data->ppmu[busier].ccnt_overflow || |
760 | data->dmc[busier_dmc].count_overflow[0]) | 649 | ppmu_data->ppmu[busier].count_overflow[0]) |
761 | return -EAGAIN; | 650 | return -EAGAIN; |
762 | 651 | ||
763 | return 0; | 652 | return 0; |
764 | } | 653 | } |
765 | 654 | ||
766 | static void exynos4_bus_exit(struct device *dev) | ||
767 | { | ||
768 | struct busfreq_data *data = dev_get_drvdata(dev); | ||
769 | |||
770 | devfreq_unregister_opp_notifier(dev, data->devfreq); | ||
771 | } | ||
772 | |||
773 | static struct devfreq_dev_profile exynos4_devfreq_profile = { | 655 | static struct devfreq_dev_profile exynos4_devfreq_profile = { |
774 | .initial_freq = 400000, | 656 | .initial_freq = 400000, |
775 | .polling_ms = 50, | 657 | .polling_ms = 50, |
776 | .target = exynos4_bus_target, | 658 | .target = exynos4_bus_target, |
777 | .get_dev_status = exynos4_bus_get_dev_status, | 659 | .get_dev_status = exynos4_bus_get_dev_status, |
778 | .exit = exynos4_bus_exit, | ||
779 | }; | 660 | }; |
780 | 661 | ||
781 | static int exynos4210_init_tables(struct busfreq_data *data) | 662 | static int exynos4210_init_tables(struct busfreq_data *data) |
@@ -837,11 +718,11 @@ static int exynos4210_init_tables(struct busfreq_data *data) | |||
837 | data->top_divtable[i] = tmp; | 718 | data->top_divtable[i] = tmp; |
838 | } | 719 | } |
839 | 720 | ||
840 | #ifdef CONFIG_EXYNOS_ASV | 721 | /* |
841 | tmp = exynos4_result_of_asv; | 722 | * TODO: init tmp based on busfreq_data |
842 | #else | 723 | * (device-tree or platform-data) |
724 | */ | ||
843 | tmp = 0; /* Max voltages for the reliability of the unknown */ | 725 | tmp = 0; /* Max voltages for the reliability of the unknown */ |
844 | #endif | ||
845 | 726 | ||
846 | pr_debug("ASV Group of Exynos4 is %d\n", tmp); | 727 | pr_debug("ASV Group of Exynos4 is %d\n", tmp); |
847 | /* Use merged grouping for voltage */ | 728 | /* Use merged grouping for voltage */ |
@@ -922,11 +803,7 @@ static int exynos4x12_init_tables(struct busfreq_data *data) | |||
922 | data->dmc_divtable[i] = tmp; | 803 | data->dmc_divtable[i] = tmp; |
923 | } | 804 | } |
924 | 805 | ||
925 | #ifdef CONFIG_EXYNOS_ASV | ||
926 | tmp = exynos4_result_of_asv; | ||
927 | #else | ||
928 | tmp = 0; /* Max voltages for the reliability of the unknown */ | 806 | tmp = 0; /* Max voltages for the reliability of the unknown */ |
929 | #endif | ||
930 | 807 | ||
931 | if (tmp > 8) | 808 | if (tmp > 8) |
932 | tmp = 0; | 809 | tmp = 0; |
@@ -1020,6 +897,7 @@ unlock: | |||
1020 | static int exynos4_busfreq_probe(struct platform_device *pdev) | 897 | static int exynos4_busfreq_probe(struct platform_device *pdev) |
1021 | { | 898 | { |
1022 | struct busfreq_data *data; | 899 | struct busfreq_data *data; |
900 | struct busfreq_ppmu_data *ppmu_data; | ||
1023 | struct dev_pm_opp *opp; | 901 | struct dev_pm_opp *opp; |
1024 | struct device *dev = &pdev->dev; | 902 | struct device *dev = &pdev->dev; |
1025 | int err = 0; | 903 | int err = 0; |
@@ -1030,9 +908,19 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) | |||
1030 | return -ENOMEM; | 908 | return -ENOMEM; |
1031 | } | 909 | } |
1032 | 910 | ||
911 | ppmu_data = &data->ppmu_data; | ||
912 | ppmu_data->ppmu_end = PPMU_END; | ||
913 | ppmu_data->ppmu = devm_kzalloc(dev, | ||
914 | sizeof(struct exynos_ppmu) * PPMU_END, | ||
915 | GFP_KERNEL); | ||
916 | if (!ppmu_data->ppmu) { | ||
917 | dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); | ||
918 | return -ENOMEM; | ||
919 | } | ||
920 | |||
1033 | data->type = pdev->id_entry->driver_data; | 921 | data->type = pdev->id_entry->driver_data; |
1034 | data->dmc[0].hw_base = S5P_VA_DMC0; | 922 | ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0; |
1035 | data->dmc[1].hw_base = S5P_VA_DMC1; | 923 | ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1; |
1036 | data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; | 924 | data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; |
1037 | data->dev = dev; | 925 | data->dev = dev; |
1038 | mutex_init(&data->lock); | 926 | mutex_init(&data->lock); |
@@ -1048,8 +936,11 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) | |||
1048 | dev_err(dev, "Cannot determine the device id %d\n", data->type); | 936 | dev_err(dev, "Cannot determine the device id %d\n", data->type); |
1049 | err = -EINVAL; | 937 | err = -EINVAL; |
1050 | } | 938 | } |
1051 | if (err) | 939 | if (err) { |
940 | dev_err(dev, "Cannot initialize busfreq table %d\n", | ||
941 | data->type); | ||
1052 | return err; | 942 | return err; |
943 | } | ||
1053 | 944 | ||
1054 | data->vdd_int = devm_regulator_get(dev, "vdd_int"); | 945 | data->vdd_int = devm_regulator_get(dev, "vdd_int"); |
1055 | if (IS_ERR(data->vdd_int)) { | 946 | if (IS_ERR(data->vdd_int)) { |
@@ -1079,19 +970,28 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) | |||
1079 | 970 | ||
1080 | platform_set_drvdata(pdev, data); | 971 | platform_set_drvdata(pdev, data); |
1081 | 972 | ||
1082 | busfreq_mon_reset(data); | 973 | data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile, |
1083 | |||
1084 | data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile, | ||
1085 | "simple_ondemand", NULL); | 974 | "simple_ondemand", NULL); |
1086 | if (IS_ERR(data->devfreq)) | 975 | if (IS_ERR(data->devfreq)) |
1087 | return PTR_ERR(data->devfreq); | 976 | return PTR_ERR(data->devfreq); |
1088 | 977 | ||
1089 | devfreq_register_opp_notifier(dev, data->devfreq); | 978 | /* |
979 | * Start PPMU (Performance Profiling Monitoring Unit) to check | ||
980 | * utilization of each IP in the Exynos4 SoC. | ||
981 | */ | ||
982 | busfreq_mon_reset(ppmu_data); | ||
1090 | 983 | ||
984 | /* Register opp_notifier for Exynos4 busfreq */ | ||
985 | err = devm_devfreq_register_opp_notifier(dev, data->devfreq); | ||
986 | if (err < 0) { | ||
987 | dev_err(dev, "Failed to register opp notifier\n"); | ||
988 | return err; | ||
989 | } | ||
990 | |||
991 | /* Register pm_notifier for Exynos4 busfreq */ | ||
1091 | err = register_pm_notifier(&data->pm_notifier); | 992 | err = register_pm_notifier(&data->pm_notifier); |
1092 | if (err) { | 993 | if (err) { |
1093 | dev_err(dev, "Failed to setup pm notifier\n"); | 994 | dev_err(dev, "Failed to setup pm notifier\n"); |
1094 | devfreq_remove_device(data->devfreq); | ||
1095 | return err; | 995 | return err; |
1096 | } | 996 | } |
1097 | 997 | ||
@@ -1102,23 +1002,24 @@ static int exynos4_busfreq_remove(struct platform_device *pdev) | |||
1102 | { | 1002 | { |
1103 | struct busfreq_data *data = platform_get_drvdata(pdev); | 1003 | struct busfreq_data *data = platform_get_drvdata(pdev); |
1104 | 1004 | ||
1005 | /* Unregister all of notifier chain */ | ||
1105 | unregister_pm_notifier(&data->pm_notifier); | 1006 | unregister_pm_notifier(&data->pm_notifier); |
1106 | devfreq_remove_device(data->devfreq); | ||
1107 | 1007 | ||
1108 | return 0; | 1008 | return 0; |
1109 | } | 1009 | } |
1110 | 1010 | ||
1011 | #ifdef CONFIG_PM_SLEEP | ||
1111 | static int exynos4_busfreq_resume(struct device *dev) | 1012 | static int exynos4_busfreq_resume(struct device *dev) |
1112 | { | 1013 | { |
1113 | struct busfreq_data *data = dev_get_drvdata(dev); | 1014 | struct busfreq_data *data = dev_get_drvdata(dev); |
1015 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; | ||
1114 | 1016 | ||
1115 | busfreq_mon_reset(data); | 1017 | busfreq_mon_reset(ppmu_data); |
1116 | return 0; | 1018 | return 0; |
1117 | } | 1019 | } |
1020 | #endif | ||
1118 | 1021 | ||
1119 | static const struct dev_pm_ops exynos4_busfreq_pm = { | 1022 | static SIMPLE_DEV_PM_OPS(exynos4_busfreq_pm_ops, NULL, exynos4_busfreq_resume); |
1120 | .resume = exynos4_busfreq_resume, | ||
1121 | }; | ||
1122 | 1023 | ||
1123 | static const struct platform_device_id exynos4_busfreq_id[] = { | 1024 | static const struct platform_device_id exynos4_busfreq_id[] = { |
1124 | { "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 }, | 1025 | { "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 }, |
@@ -1134,7 +1035,7 @@ static struct platform_driver exynos4_busfreq_driver = { | |||
1134 | .driver = { | 1035 | .driver = { |
1135 | .name = "exynos4-busfreq", | 1036 | .name = "exynos4-busfreq", |
1136 | .owner = THIS_MODULE, | 1037 | .owner = THIS_MODULE, |
1137 | .pm = &exynos4_busfreq_pm, | 1038 | .pm = &exynos4_busfreq_pm_ops, |
1138 | }, | 1039 | }, |
1139 | }; | 1040 | }; |
1140 | 1041 | ||
diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c index 6eef1f7397c6..6cd0392e2798 100644 --- a/drivers/devfreq/exynos/exynos5_bus.c +++ b/drivers/devfreq/exynos/exynos5_bus.c | |||
@@ -50,7 +50,7 @@ struct busfreq_data_int { | |||
50 | struct device *dev; | 50 | struct device *dev; |
51 | struct devfreq *devfreq; | 51 | struct devfreq *devfreq; |
52 | struct regulator *vdd_int; | 52 | struct regulator *vdd_int; |
53 | struct exynos_ppmu ppmu[PPMU_END]; | 53 | struct busfreq_ppmu_data ppmu_data; |
54 | unsigned long curr_freq; | 54 | unsigned long curr_freq; |
55 | bool disabled; | 55 | bool disabled; |
56 | 56 | ||
@@ -75,49 +75,6 @@ static struct int_bus_opp_table exynos5_int_opp_table[] = { | |||
75 | {0, 0, 0}, | 75 | {0, 0, 0}, |
76 | }; | 76 | }; |
77 | 77 | ||
78 | static void busfreq_mon_reset(struct busfreq_data_int *data) | ||
79 | { | ||
80 | unsigned int i; | ||
81 | |||
82 | for (i = PPMU_RIGHT; i < PPMU_END; i++) { | ||
83 | void __iomem *ppmu_base = data->ppmu[i].hw_base; | ||
84 | |||
85 | /* Reset the performance and cycle counters */ | ||
86 | exynos_ppmu_reset(ppmu_base); | ||
87 | |||
88 | /* Setup count registers to monitor read/write transactions */ | ||
89 | data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; | ||
90 | exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, | ||
91 | data->ppmu[i].event[PPMU_PMNCNT3]); | ||
92 | |||
93 | exynos_ppmu_start(ppmu_base); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | static void exynos5_read_ppmu(struct busfreq_data_int *data) | ||
98 | { | ||
99 | int i, j; | ||
100 | |||
101 | for (i = PPMU_RIGHT; i < PPMU_END; i++) { | ||
102 | void __iomem *ppmu_base = data->ppmu[i].hw_base; | ||
103 | |||
104 | exynos_ppmu_stop(ppmu_base); | ||
105 | |||
106 | /* Update local data from PPMU */ | ||
107 | data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); | ||
108 | |||
109 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | ||
110 | if (data->ppmu[i].event[j] == 0) | ||
111 | data->ppmu[i].count[j] = 0; | ||
112 | else | ||
113 | data->ppmu[i].count[j] = | ||
114 | exynos_ppmu_read(ppmu_base, j); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | busfreq_mon_reset(data); | ||
119 | } | ||
120 | |||
121 | static int exynos5_int_setvolt(struct busfreq_data_int *data, | 78 | static int exynos5_int_setvolt(struct busfreq_data_int *data, |
122 | unsigned long volt) | 79 | unsigned long volt) |
123 | { | 80 | { |
@@ -185,59 +142,33 @@ out: | |||
185 | return err; | 142 | return err; |
186 | } | 143 | } |
187 | 144 | ||
188 | static int exynos5_get_busier_dmc(struct busfreq_data_int *data) | ||
189 | { | ||
190 | int i, j; | ||
191 | int busy = 0; | ||
192 | unsigned int temp = 0; | ||
193 | |||
194 | for (i = PPMU_RIGHT; i < PPMU_END; i++) { | ||
195 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | ||
196 | if (data->ppmu[i].count[j] > temp) { | ||
197 | temp = data->ppmu[i].count[j]; | ||
198 | busy = i; | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | |||
203 | return busy; | ||
204 | } | ||
205 | |||
206 | static int exynos5_int_get_dev_status(struct device *dev, | 145 | static int exynos5_int_get_dev_status(struct device *dev, |
207 | struct devfreq_dev_status *stat) | 146 | struct devfreq_dev_status *stat) |
208 | { | 147 | { |
209 | struct platform_device *pdev = container_of(dev, struct platform_device, | 148 | struct platform_device *pdev = container_of(dev, struct platform_device, |
210 | dev); | 149 | dev); |
211 | struct busfreq_data_int *data = platform_get_drvdata(pdev); | 150 | struct busfreq_data_int *data = platform_get_drvdata(pdev); |
151 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; | ||
212 | int busier_dmc; | 152 | int busier_dmc; |
213 | 153 | ||
214 | exynos5_read_ppmu(data); | 154 | exynos_read_ppmu(ppmu_data); |
215 | busier_dmc = exynos5_get_busier_dmc(data); | 155 | busier_dmc = exynos_get_busier_ppmu(ppmu_data); |
216 | 156 | ||
217 | stat->current_frequency = data->curr_freq; | 157 | stat->current_frequency = data->curr_freq; |
218 | 158 | ||
219 | /* Number of cycles spent on memory access */ | 159 | /* Number of cycles spent on memory access */ |
220 | stat->busy_time = data->ppmu[busier_dmc].count[PPMU_PMNCNT3]; | 160 | stat->busy_time = ppmu_data->ppmu[busier_dmc].count[PPMU_PMNCNT3]; |
221 | stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO; | 161 | stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO; |
222 | stat->total_time = data->ppmu[busier_dmc].ccnt; | 162 | stat->total_time = ppmu_data->ppmu[busier_dmc].ccnt; |
223 | 163 | ||
224 | return 0; | 164 | return 0; |
225 | } | 165 | } |
226 | static void exynos5_int_exit(struct device *dev) | ||
227 | { | ||
228 | struct platform_device *pdev = container_of(dev, struct platform_device, | ||
229 | dev); | ||
230 | struct busfreq_data_int *data = platform_get_drvdata(pdev); | ||
231 | |||
232 | devfreq_unregister_opp_notifier(dev, data->devfreq); | ||
233 | } | ||
234 | 166 | ||
235 | static struct devfreq_dev_profile exynos5_devfreq_int_profile = { | 167 | static struct devfreq_dev_profile exynos5_devfreq_int_profile = { |
236 | .initial_freq = 160000, | 168 | .initial_freq = 160000, |
237 | .polling_ms = 100, | 169 | .polling_ms = 100, |
238 | .target = exynos5_busfreq_int_target, | 170 | .target = exynos5_busfreq_int_target, |
239 | .get_dev_status = exynos5_int_get_dev_status, | 171 | .get_dev_status = exynos5_int_get_dev_status, |
240 | .exit = exynos5_int_exit, | ||
241 | }; | 172 | }; |
242 | 173 | ||
243 | static int exynos5250_init_int_tables(struct busfreq_data_int *data) | 174 | static int exynos5250_init_int_tables(struct busfreq_data_int *data) |
@@ -315,6 +246,7 @@ unlock: | |||
315 | static int exynos5_busfreq_int_probe(struct platform_device *pdev) | 246 | static int exynos5_busfreq_int_probe(struct platform_device *pdev) |
316 | { | 247 | { |
317 | struct busfreq_data_int *data; | 248 | struct busfreq_data_int *data; |
249 | struct busfreq_ppmu_data *ppmu_data; | ||
318 | struct dev_pm_opp *opp; | 250 | struct dev_pm_opp *opp; |
319 | struct device *dev = &pdev->dev; | 251 | struct device *dev = &pdev->dev; |
320 | struct device_node *np; | 252 | struct device_node *np; |
@@ -330,16 +262,26 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev) | |||
330 | return -ENOMEM; | 262 | return -ENOMEM; |
331 | } | 263 | } |
332 | 264 | ||
265 | ppmu_data = &data->ppmu_data; | ||
266 | ppmu_data->ppmu_end = PPMU_END; | ||
267 | ppmu_data->ppmu = devm_kzalloc(dev, | ||
268 | sizeof(struct exynos_ppmu) * PPMU_END, | ||
269 | GFP_KERNEL); | ||
270 | if (!ppmu_data->ppmu) { | ||
271 | dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); | ||
272 | return -ENOMEM; | ||
273 | } | ||
274 | |||
333 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu"); | 275 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu"); |
334 | if (np == NULL) { | 276 | if (np == NULL) { |
335 | pr_err("Unable to find PPMU node\n"); | 277 | pr_err("Unable to find PPMU node\n"); |
336 | return -ENOENT; | 278 | return -ENOENT; |
337 | } | 279 | } |
338 | 280 | ||
339 | for (i = PPMU_RIGHT; i < PPMU_END; i++) { | 281 | for (i = 0; i < ppmu_data->ppmu_end; i++) { |
340 | /* map PPMU memory region */ | 282 | /* map PPMU memory region */ |
341 | data->ppmu[i].hw_base = of_iomap(np, i); | 283 | ppmu_data->ppmu[i].hw_base = of_iomap(np, i); |
342 | if (data->ppmu[i].hw_base == NULL) { | 284 | if (ppmu_data->ppmu[i].hw_base == NULL) { |
343 | dev_err(&pdev->dev, "failed to map memory region\n"); | 285 | dev_err(&pdev->dev, "failed to map memory region\n"); |
344 | return -ENOMEM; | 286 | return -ENOMEM; |
345 | } | 287 | } |
@@ -390,32 +332,29 @@ static int exynos5_busfreq_int_probe(struct platform_device *pdev) | |||
390 | 332 | ||
391 | platform_set_drvdata(pdev, data); | 333 | platform_set_drvdata(pdev, data); |
392 | 334 | ||
393 | busfreq_mon_reset(data); | 335 | busfreq_mon_reset(ppmu_data); |
394 | 336 | ||
395 | data->devfreq = devfreq_add_device(dev, &exynos5_devfreq_int_profile, | 337 | data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile, |
396 | "simple_ondemand", NULL); | 338 | "simple_ondemand", NULL); |
339 | if (IS_ERR(data->devfreq)) | ||
340 | return PTR_ERR(data->devfreq); | ||
397 | 341 | ||
398 | if (IS_ERR(data->devfreq)) { | 342 | err = devm_devfreq_register_opp_notifier(dev, data->devfreq); |
399 | err = PTR_ERR(data->devfreq); | 343 | if (err < 0) { |
400 | goto err_devfreq_add; | 344 | dev_err(dev, "Failed to register opp notifier\n"); |
345 | return err; | ||
401 | } | 346 | } |
402 | 347 | ||
403 | devfreq_register_opp_notifier(dev, data->devfreq); | ||
404 | |||
405 | err = register_pm_notifier(&data->pm_notifier); | 348 | err = register_pm_notifier(&data->pm_notifier); |
406 | if (err) { | 349 | if (err) { |
407 | dev_err(dev, "Failed to setup pm notifier\n"); | 350 | dev_err(dev, "Failed to setup pm notifier\n"); |
408 | goto err_devfreq_add; | 351 | return err; |
409 | } | 352 | } |
410 | 353 | ||
411 | /* TODO: Add a new QOS class for int/mif bus */ | 354 | /* TODO: Add a new QOS class for int/mif bus */ |
412 | pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1); | 355 | pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1); |
413 | 356 | ||
414 | return 0; | 357 | return 0; |
415 | |||
416 | err_devfreq_add: | ||
417 | devfreq_remove_device(data->devfreq); | ||
418 | return err; | ||
419 | } | 358 | } |
420 | 359 | ||
421 | static int exynos5_busfreq_int_remove(struct platform_device *pdev) | 360 | static int exynos5_busfreq_int_remove(struct platform_device *pdev) |
@@ -424,24 +363,27 @@ static int exynos5_busfreq_int_remove(struct platform_device *pdev) | |||
424 | 363 | ||
425 | pm_qos_remove_request(&data->int_req); | 364 | pm_qos_remove_request(&data->int_req); |
426 | unregister_pm_notifier(&data->pm_notifier); | 365 | unregister_pm_notifier(&data->pm_notifier); |
427 | devfreq_remove_device(data->devfreq); | ||
428 | 366 | ||
429 | return 0; | 367 | return 0; |
430 | } | 368 | } |
431 | 369 | ||
370 | #ifdef CONFIG_PM_SLEEP | ||
432 | static int exynos5_busfreq_int_resume(struct device *dev) | 371 | static int exynos5_busfreq_int_resume(struct device *dev) |
433 | { | 372 | { |
434 | struct platform_device *pdev = container_of(dev, struct platform_device, | 373 | struct platform_device *pdev = container_of(dev, struct platform_device, |
435 | dev); | 374 | dev); |
436 | struct busfreq_data_int *data = platform_get_drvdata(pdev); | 375 | struct busfreq_data_int *data = platform_get_drvdata(pdev); |
376 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; | ||
437 | 377 | ||
438 | busfreq_mon_reset(data); | 378 | busfreq_mon_reset(ppmu_data); |
439 | return 0; | 379 | return 0; |
440 | } | 380 | } |
441 | |||
442 | static const struct dev_pm_ops exynos5_busfreq_int_pm = { | 381 | static const struct dev_pm_ops exynos5_busfreq_int_pm = { |
443 | .resume = exynos5_busfreq_int_resume, | 382 | .resume = exynos5_busfreq_int_resume, |
444 | }; | 383 | }; |
384 | #endif | ||
385 | static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL, | ||
386 | exynos5_busfreq_int_resume); | ||
445 | 387 | ||
446 | /* platform device pointer for exynos5 devfreq device. */ | 388 | /* platform device pointer for exynos5 devfreq device. */ |
447 | static struct platform_device *exynos5_devfreq_pdev; | 389 | static struct platform_device *exynos5_devfreq_pdev; |
@@ -452,7 +394,7 @@ static struct platform_driver exynos5_busfreq_int_driver = { | |||
452 | .driver = { | 394 | .driver = { |
453 | .name = "exynos5-bus-int", | 395 | .name = "exynos5-bus-int", |
454 | .owner = THIS_MODULE, | 396 | .owner = THIS_MODULE, |
455 | .pm = &exynos5_busfreq_int_pm, | 397 | .pm = &exynos5_busfreq_int_pm_ops, |
456 | }, | 398 | }, |
457 | }; | 399 | }; |
458 | 400 | ||
diff --git a/drivers/devfreq/exynos/exynos_ppmu.c b/drivers/devfreq/exynos/exynos_ppmu.c index 85fc5ac1036a..75fcc5140ffb 100644 --- a/drivers/devfreq/exynos/exynos_ppmu.c +++ b/drivers/devfreq/exynos/exynos_ppmu.c | |||
@@ -54,3 +54,63 @@ unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch) | |||
54 | 54 | ||
55 | return total; | 55 | return total; |
56 | } | 56 | } |
57 | |||
58 | void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data) | ||
59 | { | ||
60 | unsigned int i; | ||
61 | |||
62 | for (i = 0; i < ppmu_data->ppmu_end; i++) { | ||
63 | void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; | ||
64 | |||
65 | /* Reset the performance and cycle counters */ | ||
66 | exynos_ppmu_reset(ppmu_base); | ||
67 | |||
68 | /* Setup count registers to monitor read/write transactions */ | ||
69 | ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; | ||
70 | exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, | ||
71 | ppmu_data->ppmu[i].event[PPMU_PMNCNT3]); | ||
72 | |||
73 | exynos_ppmu_start(ppmu_base); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data) | ||
78 | { | ||
79 | int i, j; | ||
80 | |||
81 | for (i = 0; i < ppmu_data->ppmu_end; i++) { | ||
82 | void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; | ||
83 | |||
84 | exynos_ppmu_stop(ppmu_base); | ||
85 | |||
86 | /* Update local data from PPMU */ | ||
87 | ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); | ||
88 | |||
89 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | ||
90 | if (ppmu_data->ppmu[i].event[j] == 0) | ||
91 | ppmu_data->ppmu[i].count[j] = 0; | ||
92 | else | ||
93 | ppmu_data->ppmu[i].count[j] = | ||
94 | exynos_ppmu_read(ppmu_base, j); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | busfreq_mon_reset(ppmu_data); | ||
99 | } | ||
100 | |||
101 | int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data) | ||
102 | { | ||
103 | unsigned int count = 0; | ||
104 | int i, j, busy = 0; | ||
105 | |||
106 | for (i = 0; i < ppmu_data->ppmu_end; i++) { | ||
107 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | ||
108 | if (ppmu_data->ppmu[i].count[j] > count) { | ||
109 | count = ppmu_data->ppmu[i].count[j]; | ||
110 | busy = i; | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | return busy; | ||
116 | } | ||
diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h index 7dfb221eaccd..71f17ba3563c 100644 --- a/drivers/devfreq/exynos/exynos_ppmu.h +++ b/drivers/devfreq/exynos/exynos_ppmu.h | |||
@@ -69,10 +69,18 @@ struct exynos_ppmu { | |||
69 | bool count_overflow[PPMU_PMNCNT_MAX]; | 69 | bool count_overflow[PPMU_PMNCNT_MAX]; |
70 | }; | 70 | }; |
71 | 71 | ||
72 | struct busfreq_ppmu_data { | ||
73 | struct exynos_ppmu *ppmu; | ||
74 | int ppmu_end; | ||
75 | }; | ||
76 | |||
72 | void exynos_ppmu_reset(void __iomem *ppmu_base); | 77 | void exynos_ppmu_reset(void __iomem *ppmu_base); |
73 | void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, | 78 | void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, |
74 | unsigned int evt); | 79 | unsigned int evt); |
75 | void exynos_ppmu_start(void __iomem *ppmu_base); | 80 | void exynos_ppmu_start(void __iomem *ppmu_base); |
76 | void exynos_ppmu_stop(void __iomem *ppmu_base); | 81 | void exynos_ppmu_stop(void __iomem *ppmu_base); |
77 | unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch); | 82 | unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch); |
83 | void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data); | ||
84 | void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data); | ||
85 | int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data); | ||
78 | #endif /* __DEVFREQ_EXYNOS_PPMU_H */ | 86 | #endif /* __DEVFREQ_EXYNOS_PPMU_H */ |
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index d48dc00232a4..f1863dcd83ea 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h | |||
@@ -181,6 +181,12 @@ extern struct devfreq *devfreq_add_device(struct device *dev, | |||
181 | const char *governor_name, | 181 | const char *governor_name, |
182 | void *data); | 182 | void *data); |
183 | extern int devfreq_remove_device(struct devfreq *devfreq); | 183 | extern int devfreq_remove_device(struct devfreq *devfreq); |
184 | extern struct devfreq *devm_devfreq_add_device(struct device *dev, | ||
185 | struct devfreq_dev_profile *profile, | ||
186 | const char *governor_name, | ||
187 | void *data); | ||
188 | extern void devm_devfreq_remove_device(struct device *dev, | ||
189 | struct devfreq *devfreq); | ||
184 | 190 | ||
185 | /* Supposed to be called by PM_SLEEP/PM_RUNTIME callbacks */ | 191 | /* Supposed to be called by PM_SLEEP/PM_RUNTIME callbacks */ |
186 | extern int devfreq_suspend_device(struct devfreq *devfreq); | 192 | extern int devfreq_suspend_device(struct devfreq *devfreq); |
@@ -193,6 +199,10 @@ extern int devfreq_register_opp_notifier(struct device *dev, | |||
193 | struct devfreq *devfreq); | 199 | struct devfreq *devfreq); |
194 | extern int devfreq_unregister_opp_notifier(struct device *dev, | 200 | extern int devfreq_unregister_opp_notifier(struct device *dev, |
195 | struct devfreq *devfreq); | 201 | struct devfreq *devfreq); |
202 | extern int devm_devfreq_register_opp_notifier(struct device *dev, | ||
203 | struct devfreq *devfreq); | ||
204 | extern void devm_devfreq_unregister_opp_notifier(struct device *dev, | ||
205 | struct devfreq *devfreq); | ||
196 | 206 | ||
197 | #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) | 207 | #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) |
198 | /** | 208 | /** |
@@ -220,7 +230,7 @@ static inline struct devfreq *devfreq_add_device(struct device *dev, | |||
220 | const char *governor_name, | 230 | const char *governor_name, |
221 | void *data) | 231 | void *data) |
222 | { | 232 | { |
223 | return NULL; | 233 | return ERR_PTR(-ENOSYS); |
224 | } | 234 | } |
225 | 235 | ||
226 | static inline int devfreq_remove_device(struct devfreq *devfreq) | 236 | static inline int devfreq_remove_device(struct devfreq *devfreq) |
@@ -228,6 +238,19 @@ static inline int devfreq_remove_device(struct devfreq *devfreq) | |||
228 | return 0; | 238 | return 0; |
229 | } | 239 | } |
230 | 240 | ||
241 | static inline struct devfreq *devm_devfreq_add_device(struct device *dev, | ||
242 | struct devfreq_dev_profile *profile, | ||
243 | const char *governor_name, | ||
244 | void *data) | ||
245 | { | ||
246 | return ERR_PTR(-ENOSYS); | ||
247 | } | ||
248 | |||
249 | static inline void devm_devfreq_remove_device(struct device *dev, | ||
250 | struct devfreq *devfreq) | ||
251 | { | ||
252 | } | ||
253 | |||
231 | static inline int devfreq_suspend_device(struct devfreq *devfreq) | 254 | static inline int devfreq_suspend_device(struct devfreq *devfreq) |
232 | { | 255 | { |
233 | return 0; | 256 | return 0; |
@@ -256,6 +279,16 @@ static inline int devfreq_unregister_opp_notifier(struct device *dev, | |||
256 | return -EINVAL; | 279 | return -EINVAL; |
257 | } | 280 | } |
258 | 281 | ||
282 | static inline int devm_devfreq_register_opp_notifier(struct device *dev, | ||
283 | struct devfreq *devfreq) | ||
284 | { | ||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | static inline void devm_devfreq_unregister_opp_notifier(struct device *dev, | ||
289 | struct devfreq *devfreq) | ||
290 | { | ||
291 | } | ||
259 | #endif /* CONFIG_PM_DEVFREQ */ | 292 | #endif /* CONFIG_PM_DEVFREQ */ |
260 | 293 | ||
261 | #endif /* __LINUX_DEVFREQ_H__ */ | 294 | #endif /* __LINUX_DEVFREQ_H__ */ |