Commit c48b90f8 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'pm-cpufreq'

* pm-cpufreq: (23 commits)
  cpufreq: Register drivers only after CPU devices have been registered
  cpufreq: Add NULL checks to show() and store() methods of cpufreq
  cpufreq: intel_pstate: Fix plain int as pointer warning from sparse
  cpufreq: sun50i: Fix CPU speed bin detection
  cpufreq: powernv: fix stack bloat and hard limit on number of CPUs
  cpufreq: Clarify the comment in cpufreq_set_policy()
  cpufreq: vexpress-spc: find and skip duplicates when merging frequencies
  cpufreq: vexpress-spc: use macros instead of hardcoded values for cluster ids
  cpufreq: s3c64xx: Remove pointless NULL check in s3c64xx_cpufreq_driver_init
  cpufreq: imx-cpufreq-dt: Correct i.MX8MN's default speed grade value
  cpufreq: vexpress-spc: fix some coding style issues
  cpufreq: vexpress-spc: remove lots of debug messages
  cpufreq: vexpress-spc: drop unnessary cpufreq_arm_bL_ops abstraction
  cpufreq: merge arm_big_little and vexpress-spc
  cpufreq: scpi: remove stale/outdated comment about the driver
  ARM: dts: Add OPP-V2 table for AM3517
  cpufreq: ti-cpufreq: Add support for AM3517
  ARM: dts: omap36xx: using OPP1G needs to control the abb_ldo
  cpufreq: ti-cpufreq: omap36xx use "cpu0","vbb" if run in multi_regulator mode
  ARM: dts: omap3: bulk convert compatible to be explicitly ti,omap3430 or ti,omap3630 or ti,am3517
  ...
parents 62214039 46770be0
...@@ -43,7 +43,7 @@ SoC Families: ...@@ -43,7 +43,7 @@ SoC Families:
- OMAP2 generic - defaults to OMAP2420 - OMAP2 generic - defaults to OMAP2420
compatible = "ti,omap2" compatible = "ti,omap2"
- OMAP3 generic - defaults to OMAP3430 - OMAP3 generic
compatible = "ti,omap3" compatible = "ti,omap3"
- OMAP4 generic - defaults to OMAP4430 - OMAP4 generic - defaults to OMAP4430
compatible = "ti,omap4" compatible = "ti,omap4"
...@@ -51,6 +51,8 @@ SoC Families: ...@@ -51,6 +51,8 @@ SoC Families:
compatible = "ti,omap5" compatible = "ti,omap5"
- DRA7 generic - defaults to DRA742 - DRA7 generic - defaults to DRA742
compatible = "ti,dra7" compatible = "ti,dra7"
- AM33x generic
compatible = "ti,am33xx"
- AM43x generic - defaults to AM4372 - AM43x generic - defaults to AM4372
compatible = "ti,am43" compatible = "ti,am43"
...@@ -63,12 +65,14 @@ SoCs: ...@@ -63,12 +65,14 @@ SoCs:
- OMAP3430 - OMAP3430
compatible = "ti,omap3430", "ti,omap3" compatible = "ti,omap3430", "ti,omap3"
legacy: "ti,omap34xx" - please do not use any more
- AM3517 - AM3517
compatible = "ti,am3517", "ti,omap3" compatible = "ti,am3517", "ti,omap3"
- OMAP3630 - OMAP3630
compatible = "ti,omap36xx", "ti,omap3" compatible = "ti,omap3630", "ti,omap3"
- AM33xx legacy: "ti,omap36xx" - please do not use any more
compatible = "ti,am33xx", "ti,omap3" - AM335x
compatible = "ti,am33xx"
- OMAP4430 - OMAP4430
compatible = "ti,omap4430", "ti,omap4" compatible = "ti,omap4430", "ti,omap4"
...@@ -110,19 +114,19 @@ SoCs: ...@@ -110,19 +114,19 @@ SoCs:
- AM4372 - AM4372
compatible = "ti,am4372", "ti,am43" compatible = "ti,am4372", "ti,am43"
Boards: Boards (incomplete list of examples):
- OMAP3 BeagleBoard : Low cost community board - OMAP3 BeagleBoard : Low cost community board
compatible = "ti,omap3-beagle", "ti,omap3" compatible = "ti,omap3-beagle", "ti,omap3430", "ti,omap3"
- OMAP3 Tobi with Overo : Commercial expansion board with daughter board - OMAP3 Tobi with Overo : Commercial expansion board with daughter board
compatible = "gumstix,omap3-overo-tobi", "gumstix,omap3-overo", "ti,omap3" compatible = "gumstix,omap3-overo-tobi", "gumstix,omap3-overo", "ti,omap3430", "ti,omap3"
- OMAP4 SDP : Software Development Board - OMAP4 SDP : Software Development Board
compatible = "ti,omap4-sdp", "ti,omap4430" compatible = "ti,omap4-sdp", "ti,omap4430", "ti,omap4"
- OMAP4 PandaBoard : Low cost community board - OMAP4 PandaBoard : Low cost community board
compatible = "ti,omap4-panda", "ti,omap4430" compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4"
- OMAP4 DuoVero with Parlor : Commercial expansion board with daughter board - OMAP4 DuoVero with Parlor : Commercial expansion board with daughter board
compatible = "gumstix,omap4-duovero-parlor", "gumstix,omap4-duovero", "ti,omap4430", "ti,omap4"; compatible = "gumstix,omap4-duovero-parlor", "gumstix,omap4-duovero", "ti,omap4430", "ti,omap4";
...@@ -134,16 +138,16 @@ Boards: ...@@ -134,16 +138,16 @@ Boards:
compatible = "variscite,var-dvk-om44", "variscite,var-som-om44", "ti,omap4460", "ti,omap4"; compatible = "variscite,var-dvk-om44", "variscite,var-som-om44", "ti,omap4460", "ti,omap4";
- OMAP3 EVM : Software Development Board for OMAP35x, AM/DM37x - OMAP3 EVM : Software Development Board for OMAP35x, AM/DM37x
compatible = "ti,omap3-evm", "ti,omap3" compatible = "ti,omap3-evm", "ti,omap3630", "ti,omap3"
- AM335X EVM : Software Development Board for AM335x - AM335X EVM : Software Development Board for AM335x
compatible = "ti,am335x-evm", "ti,am33xx", "ti,omap3" compatible = "ti,am335x-evm", "ti,am33xx"
- AM335X Bone : Low cost community board - AM335X Bone : Low cost community board
compatible = "ti,am335x-bone", "ti,am33xx", "ti,omap3" compatible = "ti,am335x-bone", "ti,am33xx"
- AM3359 ICEv2 : Low cost Industrial Communication Engine EVM. - AM3359 ICEv2 : Low cost Industrial Communication Engine EVM.
compatible = "ti,am3359-icev2", "ti,am33xx", "ti,omap3" compatible = "ti,am3359-icev2", "ti,am33xx"
- AM335X OrionLXm : Substation Automation Platform - AM335X OrionLXm : Substation Automation Platform
compatible = "novatech,am335x-lxm", "ti,am33xx" compatible = "novatech,am335x-lxm", "ti,am33xx"
......
...@@ -15,12 +15,16 @@ In 'cpus' nodes: ...@@ -15,12 +15,16 @@ In 'cpus' nodes:
In 'operating-points-v2' table: In 'operating-points-v2' table:
- compatible: Should be - compatible: Should be
- 'operating-points-v2-ti-cpu' for am335x, am43xx, and dra7xx/am57xx SoCs - 'operating-points-v2-ti-cpu' for am335x, am43xx, and dra7xx/am57xx,
omap34xx, omap36xx and am3517 SoCs
- syscon: A phandle pointing to a syscon node representing the control module - syscon: A phandle pointing to a syscon node representing the control module
register space of the SoC. register space of the SoC.
Optional properties: Optional properties:
-------------------- --------------------
- "vdd-supply", "vbb-supply": to define two regulators for dra7xx
- "cpu0-supply", "vbb-supply": to define two regulators for omap36xx
For each opp entry in 'operating-points-v2' table: For each opp entry in 'operating-points-v2' table:
- opp-supported-hw: Two bitfields indicating: - opp-supported-hw: Two bitfields indicating:
1. Which revision of the SoC the OPP is supported by 1. Which revision of the SoC the OPP is supported by
......
...@@ -4269,14 +4269,13 @@ F: include/linux/cpufreq.h ...@@ -4269,14 +4269,13 @@ F: include/linux/cpufreq.h
F: include/linux/sched/cpufreq.h F: include/linux/sched/cpufreq.h
F: tools/testing/selftests/cpufreq/ F: tools/testing/selftests/cpufreq/
CPU FREQUENCY DRIVERS - ARM BIG LITTLE CPU FREQUENCY DRIVERS - VEXPRESS SPC ARM BIG LITTLE
M: Viresh Kumar <viresh.kumar@linaro.org> M: Viresh Kumar <viresh.kumar@linaro.org>
M: Sudeep Holla <sudeep.holla@arm.com> M: Sudeep Holla <sudeep.holla@arm.com>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
W: http://www.arm.com/products/processors/technologies/biglittleprocessing.php W: http://www.arm.com/products/processors/technologies/biglittleprocessing.php
S: Maintained S: Maintained
F: drivers/cpufreq/arm_big_little.h F: drivers/cpufreq/vexpress-spc-cpufreq.c
F: drivers/cpufreq/arm_big_little.c
CPU POWER MONITORING SUBSYSTEM CPU POWER MONITORING SUBSYSTEM
M: Thomas Renninger <trenn@suse.com> M: Thomas Renninger <trenn@suse.com>
......
...@@ -16,6 +16,37 @@ aliases { ...@@ -16,6 +16,37 @@ aliases {
can = &hecc; can = &hecc;
}; };
cpus {
cpu: cpu@0 {
/* Based on OMAP3630 variants OPP50 and OPP100 */
operating-points-v2 = <&cpu0_opp_table>;
clock-latency = <300000>; /* From legacy driver */
};
};
cpu0_opp_table: opp-table {
compatible = "operating-points-v2-ti-cpu";
syscon = <&scm_conf>;
/*
* AM3517 TRM only lists 600MHz @ 1.2V, but omap36xx
* appear to operate at 300MHz as well. Since AM3517 only
* lists one operating voltage, it will remain fixed at 1.2V
*/
opp50-300000000 {
opp-hz = /bits/ 64 <300000000>;
opp-microvolt = <1200000>;
opp-supported-hw = <0xffffffff 0xffffffff>;
opp-suspend;
};
opp100-600000000 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <1200000>;
opp-supported-hw = <0xffffffff 0xffffffff>;
};
};
ocp@68000000 { ocp@68000000 {
am35x_otg_hs: am35x_otg_hs@5c040000 { am35x_otg_hs: am35x_otg_hs@5c040000 {
compatible = "ti,omap3-musb"; compatible = "ti,omap3-musb";
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "TeeJet Mt.Ventoux"; model = "TeeJet Mt.Ventoux";
compatible = "teejet,mt_ventoux", "ti,omap3"; compatible = "teejet,mt_ventoux", "ti,am3517", "ti,omap3";
memory@80000000 { memory@80000000 {
device_type = "memory"; device_type = "memory";
......
...@@ -9,5 +9,5 @@ ...@@ -9,5 +9,5 @@
/ { / {
model = "LogicPD Zoom OMAP35xx SOM-LV Development Kit"; model = "LogicPD Zoom OMAP35xx SOM-LV Development Kit";
compatible = "logicpd,dm3730-som-lv-devkit", "ti,omap3"; compatible = "logicpd,dm3730-som-lv-devkit", "ti,omap3430", "ti,omap3";
}; };
...@@ -9,5 +9,5 @@ ...@@ -9,5 +9,5 @@
/ { / {
model = "LogicPD Zoom OMAP35xx Torpedo Development Kit"; model = "LogicPD Zoom OMAP35xx Torpedo Development Kit";
compatible = "logicpd,dm3730-torpedo-devkit", "ti,omap3"; compatible = "logicpd,dm3730-torpedo-devkit", "ti,omap3430", "ti,omap3";
}; };
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "TI OMAP3 BeagleBoard xM"; model = "TI OMAP3 BeagleBoard xM";
compatible = "ti,omap3-beagle-xm", "ti,omap36xx", "ti,omap3"; compatible = "ti,omap3-beagle-xm", "ti,omap3630", "ti,omap36xx", "ti,omap3";
cpus { cpus {
cpu@0 { cpu@0 {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "TI OMAP3 BeagleBoard"; model = "TI OMAP3 BeagleBoard";
compatible = "ti,omap3-beagle", "ti,omap3"; compatible = "ti,omap3-beagle", "ti,omap3430", "ti,omap3";
cpus { cpus {
cpu@0 { cpu@0 {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
/ { / {
model = "CompuLab CM-T3530"; model = "CompuLab CM-T3530";
compatible = "compulab,omap3-cm-t3530", "ti,omap34xx", "ti,omap3"; compatible = "compulab,omap3-cm-t3530", "ti,omap3430", "ti,omap34xx", "ti,omap3";
/* Regulator to trigger the reset signal of the Wifi module */ /* Regulator to trigger the reset signal of the Wifi module */
mmc2_sdio_reset: regulator-mmc2-sdio-reset { mmc2_sdio_reset: regulator-mmc2-sdio-reset {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
/ { / {
model = "CompuLab CM-T3730"; model = "CompuLab CM-T3730";
compatible = "compulab,omap3-cm-t3730", "ti,omap36xx", "ti,omap3"; compatible = "compulab,omap3-cm-t3730", "ti,omap3630", "ti,omap36xx", "ti,omap3";
wl12xx_vmmc2: wl12xx_vmmc2 { wl12xx_vmmc2: wl12xx_vmmc2 {
compatible = "regulator-fixed"; compatible = "regulator-fixed";
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "omap3-devkit8000-lcd-common.dtsi" #include "omap3-devkit8000-lcd-common.dtsi"
/ { / {
model = "TimLL OMAP3 Devkit8000 with 4.3'' LCD panel"; model = "TimLL OMAP3 Devkit8000 with 4.3'' LCD panel";
compatible = "timll,omap3-devkit8000", "ti,omap3"; compatible = "timll,omap3-devkit8000", "ti,omap3430", "ti,omap3";
lcd0: display { lcd0: display {
panel-timing { panel-timing {
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "omap3-devkit8000-lcd-common.dtsi" #include "omap3-devkit8000-lcd-common.dtsi"
/ { / {
model = "TimLL OMAP3 Devkit8000 with 7.0'' LCD panel"; model = "TimLL OMAP3 Devkit8000 with 7.0'' LCD panel";
compatible = "timll,omap3-devkit8000", "ti,omap3"; compatible = "timll,omap3-devkit8000", "ti,omap3430", "ti,omap3";
lcd0: display { lcd0: display {
panel-timing { panel-timing {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include "omap3-devkit8000-common.dtsi" #include "omap3-devkit8000-common.dtsi"
/ { / {
model = "TimLL OMAP3 Devkit8000"; model = "TimLL OMAP3 Devkit8000";
compatible = "timll,omap3-devkit8000", "ti,omap3"; compatible = "timll,omap3-devkit8000", "ti,omap3430", "ti,omap3";
aliases { aliases {
display1 = &dvi0; display1 = &dvi0;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
/ { / {
model = "OMAP3 GTA04"; model = "OMAP3 GTA04";
compatible = "ti,omap3-gta04", "ti,omap36xx", "ti,omap3"; compatible = "ti,omap3-gta04", "ti,omap3630", "ti,omap36xx", "ti,omap3";
cpus { cpus {
cpu@0 { cpu@0 {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "TI OMAP3 HEAD acoustics LCD-baseboard with TAO3530 SOM"; model = "TI OMAP3 HEAD acoustics LCD-baseboard with TAO3530 SOM";
compatible = "headacoustics,omap3-ha-lcd", "technexion,omap3-tao3530", "ti,omap34xx", "ti,omap3"; compatible = "headacoustics,omap3-ha-lcd", "technexion,omap3-tao3530", "ti,omap3430", "ti,omap34xx", "ti,omap3";
}; };
&omap3_pmx_core { &omap3_pmx_core {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "TI OMAP3 HEAD acoustics baseboard with TAO3530 SOM"; model = "TI OMAP3 HEAD acoustics baseboard with TAO3530 SOM";
compatible = "headacoustics,omap3-ha", "technexion,omap3-tao3530", "ti,omap34xx", "ti,omap3"; compatible = "headacoustics,omap3-ha", "technexion,omap3-tao3530", "ti,omap3430", "ti,omap34xx", "ti,omap3";
}; };
&omap3_pmx_core { &omap3_pmx_core {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
/ { / {
model = "IGEPv2 Rev. F (TI OMAP AM/DM37x)"; model = "IGEPv2 Rev. F (TI OMAP AM/DM37x)";
compatible = "isee,omap3-igep0020-rev-f", "ti,omap36xx", "ti,omap3"; compatible = "isee,omap3-igep0020-rev-f", "ti,omap3630", "ti,omap36xx", "ti,omap3";
/* Regulator to trigger the WL_EN signal of the Wifi module */ /* Regulator to trigger the WL_EN signal of the Wifi module */
lbep5clwmc_wlen: regulator-lbep5clwmc-wlen { lbep5clwmc_wlen: regulator-lbep5clwmc-wlen {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
/ { / {
model = "IGEPv2 Rev. C (TI OMAP AM/DM37x)"; model = "IGEPv2 Rev. C (TI OMAP AM/DM37x)";
compatible = "isee,omap3-igep0020", "ti,omap36xx", "ti,omap3"; compatible = "isee,omap3-igep0020", "ti,omap3630", "ti,omap36xx", "ti,omap3";
vmmcsdio_fixed: fixedregulator-mmcsdio { vmmcsdio_fixed: fixedregulator-mmcsdio {
compatible = "regulator-fixed"; compatible = "regulator-fixed";
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
/ { / {
model = "IGEP COM MODULE Rev. G (TI OMAP AM/DM37x)"; model = "IGEP COM MODULE Rev. G (TI OMAP AM/DM37x)";
compatible = "isee,omap3-igep0030-rev-g", "ti,omap36xx", "ti,omap3"; compatible = "isee,omap3-igep0030-rev-g", "ti,omap3630", "ti,omap36xx", "ti,omap3";
/* Regulator to trigger the WL_EN signal of the Wifi module */ /* Regulator to trigger the WL_EN signal of the Wifi module */
lbep5clwmc_wlen: regulator-lbep5clwmc-wlen { lbep5clwmc_wlen: regulator-lbep5clwmc-wlen {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
/ { / {
model = "IGEP COM MODULE Rev. E (TI OMAP AM/DM37x)"; model = "IGEP COM MODULE Rev. E (TI OMAP AM/DM37x)";
compatible = "isee,omap3-igep0030", "ti,omap36xx", "ti,omap3"; compatible = "isee,omap3-igep0030", "ti,omap3630", "ti,omap36xx", "ti,omap3";
vmmcsdio_fixed: fixedregulator-mmcsdio { vmmcsdio_fixed: fixedregulator-mmcsdio {
compatible = "regulator-fixed"; compatible = "regulator-fixed";
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
/ { / {
model = "TI OMAP3430 LDP (Zoom1 Labrador)"; model = "TI OMAP3430 LDP (Zoom1 Labrador)";
compatible = "ti,omap3-ldp", "ti,omap3"; compatible = "ti,omap3-ldp", "ti,omap3430", "ti,omap3";
memory@80000000 { memory@80000000 {
device_type = "memory"; device_type = "memory";
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
/ { / {
model = "INCOstartec LILLY-A83X module (DM3730)"; model = "INCOstartec LILLY-A83X module (DM3730)";
compatible = "incostartec,omap3-lilly-a83x", "ti,omap36xx", "ti,omap3"; compatible = "incostartec,omap3-lilly-a83x", "ti,omap3630", "ti,omap36xx", "ti,omap3";
chosen { chosen {
bootargs = "console=ttyO0,115200n8 vt.global_cursor_default=0 consoleblank=0"; bootargs = "console=ttyO0,115200n8 vt.global_cursor_default=0 consoleblank=0";
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "INCOstartec LILLY-DBB056 (DM3730)"; model = "INCOstartec LILLY-DBB056 (DM3730)";
compatible = "incostartec,omap3-lilly-dbb056", "incostartec,omap3-lilly-a83x", "ti,omap36xx", "ti,omap3"; compatible = "incostartec,omap3-lilly-dbb056", "incostartec,omap3-lilly-a83x", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&twl { &twl {
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
/ { / {
model = "Nokia N9"; model = "Nokia N9";
compatible = "nokia,omap3-n9", "ti,omap36xx", "ti,omap3"; compatible = "nokia,omap3-n9", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&i2c2 { &i2c2 {
......
...@@ -11,13 +11,6 @@ / { ...@@ -11,13 +11,6 @@ / {
cpus { cpus {
cpu@0 { cpu@0 {
cpu0-supply = <&vcc>; cpu0-supply = <&vcc>;
operating-points = <
/* kHz uV */
300000 1012500
600000 1200000
800000 1325000
1000000 1375000
>;
}; };
}; };
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
/ { / {
model = "Nokia N950"; model = "Nokia N950";
compatible = "nokia,omap3-n950", "ti,omap36xx", "ti,omap3"; compatible = "nokia,omap3-n950", "ti,omap3630", "ti,omap36xx", "ti,omap3";
keys { keys {
compatible = "gpio-keys"; compatible = "gpio-keys";
......
...@@ -14,5 +14,5 @@ ...@@ -14,5 +14,5 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Alto35"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Alto35";
compatible = "gumstix,omap3-overo-alto35", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-alto35", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Chestnut43"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Chestnut43";
compatible = "gumstix,omap3-overo-chestnut43", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-chestnut43", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&omap3_pmx_core2 { &omap3_pmx_core2 {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Gallop43"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Gallop43";
compatible = "gumstix,omap3-overo-gallop43", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-gallop43", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&omap3_pmx_core2 { &omap3_pmx_core2 {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Palo35"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Palo35";
compatible = "gumstix,omap3-overo-palo35", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-palo35", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&omap3_pmx_core2 { &omap3_pmx_core2 {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Palo43"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Palo43";
compatible = "gumstix,omap3-overo-palo43", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-palo43", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&omap3_pmx_core2 { &omap3_pmx_core2 {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Summit"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Summit";
compatible = "gumstix,omap3-overo-summit", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-summit", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&omap3_pmx_core2 { &omap3_pmx_core2 {
......
...@@ -14,6 +14,6 @@ ...@@ -14,6 +14,6 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Tobi"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on Tobi";
compatible = "gumstix,omap3-overo-tobi", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-tobi", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
...@@ -14,5 +14,5 @@ ...@@ -14,5 +14,5 @@
/ { / {
model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on TobiDuo"; model = "OMAP36xx/AM37xx/DM37xx Gumstix Overo on TobiDuo";
compatible = "gumstix,omap3-overo-tobiduo", "gumstix,omap3-overo", "ti,omap36xx", "ti,omap3"; compatible = "gumstix,omap3-overo-tobiduo", "gumstix,omap3-overo", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
/ { / {
model = "Pandora Handheld Console 1GHz"; model = "Pandora Handheld Console 1GHz";
compatible = "openpandora,omap3-pandora-1ghz", "ti,omap36xx", "ti,omap3"; compatible = "openpandora,omap3-pandora-1ghz", "ti,omap3630", "ti,omap36xx", "ti,omap3";
}; };
&omap3_pmx_core2 { &omap3_pmx_core2 {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "CompuLab SBC-T3530 with CM-T3530"; model = "CompuLab SBC-T3530 with CM-T3530";
compatible = "compulab,omap3-sbc-t3530", "compulab,omap3-cm-t3530", "ti,omap34xx", "ti,omap3"; compatible = "compulab,omap3-sbc-t3530", "compulab,omap3-cm-t3530", "ti,omap3430", "ti,omap34xx", "ti,omap3";
aliases { aliases {
display0 = &dvi0; display0 = &dvi0;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "CompuLab SBC-T3730 with CM-T3730"; model = "CompuLab SBC-T3730 with CM-T3730";
compatible = "compulab,omap3-sbc-t3730", "compulab,omap3-cm-t3730", "ti,omap36xx", "ti,omap3"; compatible = "compulab,omap3-sbc-t3730", "compulab,omap3-cm-t3730", "ti,omap3630", "ti,omap36xx", "ti,omap3";
aliases { aliases {
display0 = &dvi0; display0 = &dvi0;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
/ { / {
model = "LG Optimus Black"; model = "LG Optimus Black";
compatible = "lg,omap3-sniper", "ti,omap36xx", "ti,omap3"; compatible = "lg,omap3-sniper", "ti,omap3630", "ti,omap36xx", "ti,omap3";
cpus { cpus {
cpu@0 { cpu@0 {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "TI OMAP3 Thunder baseboard with TAO3530 SOM"; model = "TI OMAP3 Thunder baseboard with TAO3530 SOM";
compatible = "technexion,omap3-thunder", "technexion,omap3-tao3530", "ti,omap34xx", "ti,omap3"; compatible = "technexion,omap3-thunder", "technexion,omap3-tao3530", "ti,omap3430", "ti,omap34xx", "ti,omap3";
}; };
&omap3_pmx_core { &omap3_pmx_core {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
/ { / {
model = "TI Zoom3"; model = "TI Zoom3";
compatible = "ti,omap3-zoom3", "ti,omap36xx", "ti,omap3"; compatible = "ti,omap3-zoom3", "ti,omap3630", "ti,omap36xx", "ti,omap3";
cpus { cpus {
cpu@0 { cpu@0 {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/ { / {
model = "TI OMAP3430 SDP"; model = "TI OMAP3430 SDP";
compatible = "ti,omap3430-sdp", "ti,omap3"; compatible = "ti,omap3430-sdp", "ti,omap3430", "ti,omap3";
memory@80000000 { memory@80000000 {
device_type = "memory"; device_type = "memory";
......
...@@ -16,19 +16,67 @@ ...@@ -16,19 +16,67 @@
/ { / {
cpus { cpus {
cpu: cpu@0 { cpu: cpu@0 {
/* OMAP343x/OMAP35xx variants OPP1-5 */ /* OMAP343x/OMAP35xx variants OPP1-6 */
operating-points = < operating-points-v2 = <&cpu0_opp_table>;
/* kHz uV */
125000 975000
250000 1075000
500000 1200000
550000 1270000
600000 1350000
>;
clock-latency = <300000>; /* From legacy driver */ clock-latency = <300000>; /* From legacy driver */
}; };
}; };
/* see Documentation/devicetree/bindings/opp/opp.txt */
cpu0_opp_table: opp-table {
compatible = "operating-points-v2-ti-cpu";
syscon = <&scm_conf>;
opp1-125000000 {
opp-hz = /bits/ 64 <125000000>;
/*
* we currently only select the max voltage from table
* Table 3-3 of the omap3530 Data sheet (SPRS507F).
* Format is: <target min max>
*/
opp-microvolt = <975000 975000 975000>;
/*
* first value is silicon revision bit mask
* second one 720MHz Device Identification bit mask
*/
opp-supported-hw = <0xffffffff 3>;
};
opp2-250000000 {
opp-hz = /bits/ 64 <250000000>;
opp-microvolt = <1075000 1075000 1075000>;
opp-supported-hw = <0xffffffff 3>;
opp-suspend;
};
opp3-500000000 {
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <1200000 1200000 1200000>;
opp-supported-hw = <0xffffffff 3>;
};
opp4-550000000 {
opp-hz = /bits/ 64 <550000000>;
opp-microvolt = <1275000 1275000 1275000>;
opp-supported-hw = <0xffffffff 3>;
};
opp5-600000000 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <1350000 1350000 1350000>;
opp-supported-hw = <0xffffffff 3>;
};
opp6-720000000 {
opp-hz = /bits/ 64 <720000000>;
opp-microvolt = <1350000 1350000 1350000>;
/* only high-speed grade omap3530 devices */
opp-supported-hw = <0xffffffff 2>;
turbo-mode;
};
};
ocp@68000000 { ocp@68000000 {
omap3_pmx_core2: pinmux@480025d8 { omap3_pmx_core2: pinmux@480025d8 {
compatible = "ti,omap3-padconf", "pinctrl-single"; compatible = "ti,omap3-padconf", "pinctrl-single";
......
...@@ -19,16 +19,65 @@ aliases { ...@@ -19,16 +19,65 @@ aliases {
}; };
cpus { cpus {
/* OMAP3630/OMAP37xx 'standard device' variants OPP50 to OPP130 */ /* OMAP3630/OMAP37xx variants OPP50 to OPP130 and OPP1G */
cpu: cpu@0 { cpu: cpu@0 {
operating-points = < operating-points-v2 = <&cpu0_opp_table>;
/* kHz uV */
300000 1012500 vbb-supply = <&abb_mpu_iva>;
600000 1200000 clock-latency = <300000>; /* From omap-cpufreq driver */
800000 1325000 };
>; };
clock-latency = <300000>; /* From legacy driver */
/* see Documentation/devicetree/bindings/opp/opp.txt */
cpu0_opp_table: opp-table {
compatible = "operating-points-v2-ti-cpu";
syscon = <&scm_conf>;
opp50-300000000 {
opp-hz = /bits/ 64 <300000000>;
/*
* we currently only select the max voltage from table
* Table 4-19 of the DM3730 Data sheet (SPRS685B)
* Format is: cpu0-supply: <target min max>
* vbb-supply: <target min max>
*/
opp-microvolt = <1012500 1012500 1012500>,
<1012500 1012500 1012500>;
/*
* first value is silicon revision bit mask
* second one is "speed binned" bit mask
*/
opp-supported-hw = <0xffffffff 3>;
opp-suspend;
};
opp100-600000000 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <1200000 1200000 1200000>,
<1200000 1200000 1200000>;
opp-supported-hw = <0xffffffff 3>;
};
opp130-800000000 {
opp-hz = /bits/ 64 <800000000>;
opp-microvolt = <1325000 1325000 1325000>,
<1325000 1325000 1325000>;
opp-supported-hw = <0xffffffff 3>;
}; };
opp1g-1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <1375000 1375000 1375000>,
<1375000 1375000 1375000>;
/* only on am/dm37x with speed-binned bit set */
opp-supported-hw = <0xffffffff 2>;
turbo-mode;
};
};
opp_supply_mpu_iva: opp_supply {
compatible = "ti,omap-opp-supply";
ti,absolute-max-voltage-uv = <1375000>;
}; };
ocp@68000000 { ocp@68000000 {
......
...@@ -49,14 +49,6 @@ config ARM_ARMADA_8K_CPUFREQ ...@@ -49,14 +49,6 @@ config ARM_ARMADA_8K_CPUFREQ
If in doubt, say N. If in doubt, say N.
# big LITTLE core layer and glue drivers
config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver"
depends on ARM_CPU_TOPOLOGY && HAVE_CLK
select PM_OPP
help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
config ARM_SCPI_CPUFREQ config ARM_SCPI_CPUFREQ
tristate "SCPI based CPUfreq driver" tristate "SCPI based CPUfreq driver"
depends on ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI depends on ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
...@@ -69,7 +61,9 @@ config ARM_SCPI_CPUFREQ ...@@ -69,7 +61,9 @@ config ARM_SCPI_CPUFREQ
config ARM_VEXPRESS_SPC_CPUFREQ config ARM_VEXPRESS_SPC_CPUFREQ
tristate "Versatile Express SPC based CPUfreq driver" tristate "Versatile Express SPC based CPUfreq driver"
depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC depends on ARM_CPU_TOPOLOGY && HAVE_CLK
depends on ARCH_VEXPRESS_SPC
select PM_OPP
help help
This add the CPUfreq driver support for Versatile Express This add the CPUfreq driver support for Versatile Express
big.LITTLE platforms using SPC for power management. big.LITTLE platforms using SPC for power management.
......
...@@ -47,8 +47,6 @@ obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o ...@@ -47,8 +47,6 @@ obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o
################################################################################## ##################################################################################
# ARM SoC drivers # ARM SoC drivers
obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o
obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
......
/*
* ARM big.LITTLE Platforms CPUFreq support
*
* Copyright (C) 2013 ARM Ltd.
* Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
*
* Copyright (C) 2013 Linaro.
* Viresh Kumar <viresh.kumar@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cpu_cooling.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/topology.h>
#include <linux/types.h>
#include "arm_big_little.h"
/* Currently we support only two clusters */
#define A15_CLUSTER 0
#define A7_CLUSTER 1
#define MAX_CLUSTERS 2
#ifdef CONFIG_BL_SWITCHER
#include <asm/bL_switcher.h>
static bool bL_switching_enabled;
#define is_bL_switching_enabled() bL_switching_enabled
#define set_switching_enabled(x) (bL_switching_enabled = (x))
#else
#define is_bL_switching_enabled() false
#define set_switching_enabled(x) do { } while (0)
#define bL_switch_request(...) do { } while (0)
#define bL_switcher_put_enabled() do { } while (0)
#define bL_switcher_get_enabled() do { } while (0)
#endif
#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
static struct thermal_cooling_device *cdev[MAX_CLUSTERS];
static const struct cpufreq_arm_bL_ops *arm_bL_ops;
static struct clk *clk[MAX_CLUSTERS];
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
static atomic_t cluster_usage[MAX_CLUSTERS + 1];
static unsigned int clk_big_min; /* (Big) clock frequencies */
static unsigned int clk_little_max; /* Maximum clock frequency (Little) */
static DEFINE_PER_CPU(unsigned int, physical_cluster);
static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq);
static struct mutex cluster_lock[MAX_CLUSTERS];
static inline int raw_cpu_to_cluster(int cpu)
{
return topology_physical_package_id(cpu);
}
static inline int cpu_to_cluster(int cpu)
{
return is_bL_switching_enabled() ?
MAX_CLUSTERS : raw_cpu_to_cluster(cpu);
}
static unsigned int find_cluster_maxfreq(int cluster)
{
int j;
u32 max_freq = 0, cpu_freq;
for_each_online_cpu(j) {
cpu_freq = per_cpu(cpu_last_req_freq, j);
if ((cluster == per_cpu(physical_cluster, j)) &&
(max_freq < cpu_freq))
max_freq = cpu_freq;
}
pr_debug("%s: cluster: %d, max freq: %d\n", __func__, cluster,
max_freq);
return max_freq;
}
static unsigned int clk_get_cpu_rate(unsigned int cpu)
{
u32 cur_cluster = per_cpu(physical_cluster, cpu);
u32 rate = clk_get_rate(clk[cur_cluster]) / 1000;
/* For switcher we use virtual A7 clock rates */
if (is_bL_switching_enabled())
rate = VIRT_FREQ(cur_cluster, rate);
pr_debug("%s: cpu: %d, cluster: %d, freq: %u\n", __func__, cpu,
cur_cluster, rate);
return rate;
}
static unsigned int bL_cpufreq_get_rate(unsigned int cpu)
{
if (is_bL_switching_enabled()) {
pr_debug("%s: freq: %d\n", __func__, per_cpu(cpu_last_req_freq,
cpu));
return per_cpu(cpu_last_req_freq, cpu);
} else {
return clk_get_cpu_rate(cpu);
}
}
static unsigned int
bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
{
u32 new_rate, prev_rate;
int ret;
bool bLs = is_bL_switching_enabled();
mutex_lock(&cluster_lock[new_cluster]);
if (bLs) {
prev_rate = per_cpu(cpu_last_req_freq, cpu);
per_cpu(cpu_last_req_freq, cpu) = rate;
per_cpu(physical_cluster, cpu) = new_cluster;
new_rate = find_cluster_maxfreq(new_cluster);
new_rate = ACTUAL_FREQ(new_cluster, new_rate);
} else {
new_rate = rate;
}
pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d, freq: %d\n",
__func__, cpu, old_cluster, new_cluster, new_rate);
ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
if (!ret) {
/*
* FIXME: clk_set_rate hasn't returned an error here however it
* may be that clk_change_rate failed due to hardware or
* firmware issues and wasn't able to report that due to the
* current design of the clk core layer. To work around this
* problem we will read back the clock rate and check it is
* correct. This needs to be removed once clk core is fixed.
*/
if (clk_get_rate(clk[new_cluster]) != new_rate * 1000)
ret = -EIO;
}
if (WARN_ON(ret)) {
pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret,
new_cluster);
if (bLs) {
per_cpu(cpu_last_req_freq, cpu) = prev_rate;
per_cpu(physical_cluster, cpu) = old_cluster;
}
mutex_unlock(&cluster_lock[new_cluster]);
return ret;
}
mutex_unlock(&cluster_lock[new_cluster]);
/* Recalc freq for old cluster when switching clusters */
if (old_cluster != new_cluster) {
pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n",
__func__, cpu, old_cluster, new_cluster);
/* Switch cluster */
bL_switch_request(cpu, new_cluster);
mutex_lock(&cluster_lock[old_cluster]);
/* Set freq of old cluster if there are cpus left on it */
new_rate = find_cluster_maxfreq(old_cluster);
new_rate = ACTUAL_FREQ(old_cluster, new_rate);
if (new_rate) {
pr_debug("%s: Updating rate of old cluster: %d, to freq: %d\n",
__func__, old_cluster, new_rate);
if (clk_set_rate(clk[old_cluster], new_rate * 1000))
pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n",
__func__, ret, old_cluster);
}
mutex_unlock(&cluster_lock[old_cluster]);
}
return 0;
}
/* Set clock frequency */
static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index)
{
u32 cpu = policy->cpu, cur_cluster, new_cluster, actual_cluster;
unsigned int freqs_new;
int ret;
cur_cluster = cpu_to_cluster(cpu);
new_cluster = actual_cluster = per_cpu(physical_cluster, cpu);
freqs_new = freq_table[cur_cluster][index].frequency;
if (is_bL_switching_enabled()) {
if ((actual_cluster == A15_CLUSTER) &&
(freqs_new < clk_big_min)) {
new_cluster = A7_CLUSTER;
} else if ((actual_cluster == A7_CLUSTER) &&
(freqs_new > clk_little_max)) {
new_cluster = A15_CLUSTER;
}
}
ret = bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs_new);
if (!ret) {
arch_set_freq_scale(policy->related_cpus, freqs_new,
policy->cpuinfo.max_freq);
}
return ret;
}
static inline u32 get_table_count(struct cpufreq_frequency_table *table)
{
int count;
for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++)
;
return count;
}
/* get the minimum frequency in the cpufreq_frequency_table */
static inline u32 get_table_min(struct cpufreq_frequency_table *table)
{
struct cpufreq_frequency_table *pos;
uint32_t min_freq = ~0;
cpufreq_for_each_entry(pos, table)
if (pos->frequency < min_freq)
min_freq = pos->frequency;
return min_freq;
}
/* get the maximum frequency in the cpufreq_frequency_table */
static inline u32 get_table_max(struct cpufreq_frequency_table *table)
{
struct cpufreq_frequency_table *pos;
uint32_t max_freq = 0;
cpufreq_for_each_entry(pos, table)
if (pos->frequency > max_freq)
max_freq = pos->frequency;
return max_freq;
}
static int merge_cluster_tables(void)
{
int i, j, k = 0, count = 1;
struct cpufreq_frequency_table *table;
for (i = 0; i < MAX_CLUSTERS; i++)
count += get_table_count(freq_table[i]);
table = kcalloc(count, sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
freq_table[MAX_CLUSTERS] = table;
/* Add in reverse order to get freqs in increasing order */
for (i = MAX_CLUSTERS - 1; i >= 0; i--) {
for (j = 0; freq_table[i][j].frequency != CPUFREQ_TABLE_END;
j++) {
table[k].frequency = VIRT_FREQ(i,
freq_table[i][j].frequency);
pr_debug("%s: index: %d, freq: %d\n", __func__, k,
table[k].frequency);
k++;
}
}
table[k].driver_data = k;
table[k].frequency = CPUFREQ_TABLE_END;
pr_debug("%s: End, table: %p, count: %d\n", __func__, table, k);
return 0;
}
static void _put_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{
u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
if (!freq_table[cluster])
return;
clk_put(clk[cluster]);
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
if (arm_bL_ops->free_opp_table)
arm_bL_ops->free_opp_table(cpumask);
dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
}
static void put_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{
u32 cluster = cpu_to_cluster(cpu_dev->id);
int i;
if (atomic_dec_return(&cluster_usage[cluster]))
return;
if (cluster < MAX_CLUSTERS)
return _put_cluster_clk_and_freq_table(cpu_dev, cpumask);
for_each_present_cpu(i) {
struct device *cdev = get_cpu_device(i);
if (!cdev) {
pr_err("%s: failed to get cpu%d device\n", __func__, i);
return;
}
_put_cluster_clk_and_freq_table(cdev, cpumask);
}
/* free virtual table */
kfree(freq_table[cluster]);
}
static int _get_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{
u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
int ret;
if (freq_table[cluster])
return 0;
ret = arm_bL_ops->init_opp_table(cpumask);
if (ret) {
dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n",
__func__, cpu_dev->id, ret);
goto out;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
if (ret) {
dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
__func__, cpu_dev->id, ret);
goto free_opp_table;
}
clk[cluster] = clk_get(cpu_dev, NULL);
if (!IS_ERR(clk[cluster])) {
dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
__func__, clk[cluster], freq_table[cluster],
cluster);
return 0;
}
dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n",
__func__, cpu_dev->id, cluster);
ret = PTR_ERR(clk[cluster]);
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
free_opp_table:
if (arm_bL_ops->free_opp_table)
arm_bL_ops->free_opp_table(cpumask);
out:
dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
cluster);
return ret;
}
static int get_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{
u32 cluster = cpu_to_cluster(cpu_dev->id);
int i, ret;
if (atomic_inc_return(&cluster_usage[cluster]) != 1)
return 0;
if (cluster < MAX_CLUSTERS) {
ret = _get_cluster_clk_and_freq_table(cpu_dev, cpumask);
if (ret)
atomic_dec(&cluster_usage[cluster]);
return ret;
}
/*
* Get data for all clusters and fill virtual cluster with a merge of
* both
*/
for_each_present_cpu(i) {
struct device *cdev = get_cpu_device(i);
if (!cdev) {
pr_err("%s: failed to get cpu%d device\n", __func__, i);
return -ENODEV;
}
ret = _get_cluster_clk_and_freq_table(cdev, cpumask);
if (ret)
goto put_clusters;
}
ret = merge_cluster_tables();
if (ret)
goto put_clusters;
/* Assuming 2 cluster, set clk_big_min and clk_little_max */
clk_big_min = get_table_min(freq_table[0]);
clk_little_max = VIRT_FREQ(1, get_table_max(freq_table[1]));
pr_debug("%s: cluster: %d, clk_big_min: %d, clk_little_max: %d\n",
__func__, cluster, clk_big_min, clk_little_max);
return 0;
put_clusters:
for_each_present_cpu(i) {
struct device *cdev = get_cpu_device(i);
if (!cdev) {
pr_err("%s: failed to get cpu%d device\n", __func__, i);
return -ENODEV;
}
_put_cluster_clk_and_freq_table(cdev, cpumask);
}
atomic_dec(&cluster_usage[cluster]);
return ret;
}
/* Per-CPU initialization */
static int bL_cpufreq_init(struct cpufreq_policy *policy)
{
u32 cur_cluster = cpu_to_cluster(policy->cpu);
struct device *cpu_dev;
int ret;
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("%s: failed to get cpu%d device\n", __func__,
policy->cpu);
return -ENODEV;
}
if (cur_cluster < MAX_CLUSTERS) {
int cpu;
cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
for_each_cpu(cpu, policy->cpus)
per_cpu(physical_cluster, cpu) = cur_cluster;
} else {
/* Assumption: during init, we are always running on A15 */
per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER;
}
ret = get_cluster_clk_and_freq_table(cpu_dev, policy->cpus);
if (ret)
return ret;
policy->freq_table = freq_table[cur_cluster];
policy->cpuinfo.transition_latency =
arm_bL_ops->get_transition_latency(cpu_dev);
dev_pm_opp_of_register_em(policy->cpus);
if (is_bL_switching_enabled())
per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu);
dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu);
return 0;
}
static int bL_cpufreq_exit(struct cpufreq_policy *policy)
{
struct device *cpu_dev;
int cur_cluster = cpu_to_cluster(policy->cpu);
if (cur_cluster < MAX_CLUSTERS) {
cpufreq_cooling_unregister(cdev[cur_cluster]);
cdev[cur_cluster] = NULL;
}
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("%s: failed to get cpu%d device\n", __func__,
policy->cpu);
return -ENODEV;
}
put_cluster_clk_and_freq_table(cpu_dev, policy->related_cpus);
dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu);
return 0;
}
static void bL_cpufreq_ready(struct cpufreq_policy *policy)
{
int cur_cluster = cpu_to_cluster(policy->cpu);
/* Do not register a cpu_cooling device if we are in IKS mode */
if (cur_cluster >= MAX_CLUSTERS)
return;
cdev[cur_cluster] = of_cpufreq_cooling_register(policy);
}
static struct cpufreq_driver bL_cpufreq_driver = {
.name = "arm-big-little",
.flags = CPUFREQ_STICKY |
CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = bL_cpufreq_set_target,
.get = bL_cpufreq_get_rate,
.init = bL_cpufreq_init,
.exit = bL_cpufreq_exit,
.ready = bL_cpufreq_ready,
.attr = cpufreq_generic_attr,
};
#ifdef CONFIG_BL_SWITCHER
static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb,
unsigned long action, void *_arg)
{
pr_debug("%s: action: %ld\n", __func__, action);
switch (action) {
case BL_NOTIFY_PRE_ENABLE:
case BL_NOTIFY_PRE_DISABLE:
cpufreq_unregister_driver(&bL_cpufreq_driver);
break;
case BL_NOTIFY_POST_ENABLE:
set_switching_enabled(true);
cpufreq_register_driver(&bL_cpufreq_driver);
break;
case BL_NOTIFY_POST_DISABLE:
set_switching_enabled(false);
cpufreq_register_driver(&bL_cpufreq_driver);
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block bL_switcher_notifier = {
.notifier_call = bL_cpufreq_switcher_notifier,
};
static int __bLs_register_notifier(void)
{
return bL_switcher_register_notifier(&bL_switcher_notifier);
}
static int __bLs_unregister_notifier(void)
{
return bL_switcher_unregister_notifier(&bL_switcher_notifier);
}
#else
static int __bLs_register_notifier(void) { return 0; }
static int __bLs_unregister_notifier(void) { return 0; }
#endif
int bL_cpufreq_register(const struct cpufreq_arm_bL_ops *ops)
{
int ret, i;
if (arm_bL_ops) {
pr_debug("%s: Already registered: %s, exiting\n", __func__,
arm_bL_ops->name);
return -EBUSY;
}
if (!ops || !strlen(ops->name) || !ops->init_opp_table ||
!ops->get_transition_latency) {
pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__);
return -ENODEV;
}
arm_bL_ops = ops;
set_switching_enabled(bL_switcher_get_enabled());
for (i = 0; i < MAX_CLUSTERS; i++)
mutex_init(&cluster_lock[i]);
ret = cpufreq_register_driver(&bL_cpufreq_driver);
if (ret) {
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
__func__, ops->name, ret);
arm_bL_ops = NULL;
} else {
ret = __bLs_register_notifier();
if (ret) {
cpufreq_unregister_driver(&bL_cpufreq_driver);
arm_bL_ops = NULL;
} else {
pr_info("%s: Registered platform driver: %s\n",
__func__, ops->name);
}
}
bL_switcher_put_enabled();
return ret;
}
EXPORT_SYMBOL_GPL(bL_cpufreq_register);
void bL_cpufreq_unregister(const struct cpufreq_arm_bL_ops *ops)
{
if (arm_bL_ops != ops) {
pr_err("%s: Registered with: %s, can't unregister, exiting\n",
__func__, arm_bL_ops->name);
return;
}
bL_switcher_get_enabled();
__bLs_unregister_notifier();
cpufreq_unregister_driver(&bL_cpufreq_driver);
bL_switcher_put_enabled();
pr_info("%s: Un-registered platform driver: %s\n", __func__,
arm_bL_ops->name);
arm_bL_ops = NULL;
}
EXPORT_SYMBOL_GPL(bL_cpufreq_unregister);
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
MODULE_DESCRIPTION("Generic ARM big LITTLE cpufreq driver");
MODULE_LICENSE("GPL v2");
/*
* ARM big.LITTLE platform's CPUFreq header file
*
* Copyright (C) 2013 ARM Ltd.
* Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
*
* Copyright (C) 2013 Linaro.
* Viresh Kumar <viresh.kumar@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CPUFREQ_ARM_BIG_LITTLE_H
#define CPUFREQ_ARM_BIG_LITTLE_H
#include <linux/cpufreq.h>
#include <linux/device.h>
#include <linux/types.h>
struct cpufreq_arm_bL_ops {
char name[CPUFREQ_NAME_LEN];
/*
* This must set opp table for cpu_dev in a similar way as done by
* dev_pm_opp_of_add_table().
*/
int (*init_opp_table)(const struct cpumask *cpumask);
/* Optional */
int (*get_transition_latency)(struct device *cpu_dev);
void (*free_opp_table)(const struct cpumask *cpumask);
};
int bL_cpufreq_register(const struct cpufreq_arm_bL_ops *ops);
void bL_cpufreq_unregister(const struct cpufreq_arm_bL_ops *ops);
#endif /* CPUFREQ_ARM_BIG_LITTLE_H */
...@@ -86,7 +86,6 @@ static const struct of_device_id whitelist[] __initconst = { ...@@ -86,7 +86,6 @@ static const struct of_device_id whitelist[] __initconst = {
{ .compatible = "st-ericsson,u9540", }, { .compatible = "st-ericsson,u9540", },
{ .compatible = "ti,omap2", }, { .compatible = "ti,omap2", },
{ .compatible = "ti,omap3", },
{ .compatible = "ti,omap4", }, { .compatible = "ti,omap4", },
{ .compatible = "ti,omap5", }, { .compatible = "ti,omap5", },
...@@ -137,6 +136,7 @@ static const struct of_device_id blacklist[] __initconst = { ...@@ -137,6 +136,7 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "ti,am33xx", }, { .compatible = "ti,am33xx", },
{ .compatible = "ti,am43", }, { .compatible = "ti,am43", },
{ .compatible = "ti,dra7", }, { .compatible = "ti,dra7", },
{ .compatible = "ti,omap3", },
{ } { }
}; };
......
...@@ -933,6 +933,9 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) ...@@ -933,6 +933,9 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
struct freq_attr *fattr = to_attr(attr); struct freq_attr *fattr = to_attr(attr);
ssize_t ret; ssize_t ret;
if (!fattr->show)
return -EIO;
down_read(&policy->rwsem); down_read(&policy->rwsem);
ret = fattr->show(policy, buf); ret = fattr->show(policy, buf);
up_read(&policy->rwsem); up_read(&policy->rwsem);
...@@ -947,6 +950,9 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, ...@@ -947,6 +950,9 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
struct freq_attr *fattr = to_attr(attr); struct freq_attr *fattr = to_attr(attr);
ssize_t ret = -EINVAL; ssize_t ret = -EINVAL;
if (!fattr->store)
return -EIO;
/* /*
* cpus_read_trylock() is used here to work around a circular lock * cpus_read_trylock() is used here to work around a circular lock
* dependency problem with respect to the cpufreq_register_driver(). * dependency problem with respect to the cpufreq_register_driver().
...@@ -2385,7 +2391,10 @@ int cpufreq_set_policy(struct cpufreq_policy *policy, ...@@ -2385,7 +2391,10 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
new_policy->min = freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN); new_policy->min = freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN);
new_policy->max = freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX); new_policy->max = freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX);
/* verify the cpu speed can be set within this limit */ /*
* Verify that the CPU speed can be set within these limits and make sure
* that min <= max.
*/
ret = cpufreq_driver->verify(new_policy); ret = cpufreq_driver->verify(new_policy);
if (ret) if (ret)
return ret; return ret;
...@@ -2628,6 +2637,13 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) ...@@ -2628,6 +2637,13 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
if (cpufreq_disabled()) if (cpufreq_disabled())
return -ENODEV; return -ENODEV;
/*
* The cpufreq core depends heavily on the availability of device
* structure, make sure they are available before proceeding further.
*/
if (!get_cpu_device(0))
return -EPROBE_DEFER;
if (!driver_data || !driver_data->verify || !driver_data->init || if (!driver_data || !driver_data->verify || !driver_data->init ||
!(driver_data->setpolicy || driver_data->target_index || !(driver_data->setpolicy || driver_data->target_index ||
driver_data->target) || driver_data->target) ||
......
...@@ -44,19 +44,19 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) ...@@ -44,19 +44,19 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev)
mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK) >> OCOTP_CFG3_MKT_SEGMENT_SHIFT; mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK) >> OCOTP_CFG3_MKT_SEGMENT_SHIFT;
/* /*
* Early samples without fuses written report "0 0" which means * Early samples without fuses written report "0 0" which may NOT
* consumer segment and minimum speed grading. * match any OPP defined in DT. So clamp to minimum OPP defined in
* * DT to avoid warning for "no OPPs".
* According to datasheet minimum speed grading is not supported for
* consumer parts so clamp to 1 to avoid warning for "no OPPs"
* *
* Applies to i.MX8M series SoCs. * Applies to i.MX8M series SoCs.
*/ */
if (mkt_segment == 0 && speed_grade == 0 && ( if (mkt_segment == 0 && speed_grade == 0) {
of_machine_is_compatible("fsl,imx8mm") || if (of_machine_is_compatible("fsl,imx8mm") ||
of_machine_is_compatible("fsl,imx8mn") || of_machine_is_compatible("fsl,imx8mq"))
of_machine_is_compatible("fsl,imx8mq"))) speed_grade = 1;
speed_grade = 1; if (of_machine_is_compatible("fsl,imx8mn"))
speed_grade = 0xb;
}
supported_hw[0] = BIT(speed_grade); supported_hw[0] = BIT(speed_grade);
supported_hw[1] = BIT(mkt_segment); supported_hw[1] = BIT(mkt_segment);
......
...@@ -2662,21 +2662,21 @@ enum { ...@@ -2662,21 +2662,21 @@ enum {
/* Hardware vendor-specific info that has its own power management modes */ /* Hardware vendor-specific info that has its own power management modes */
static struct acpi_platform_list plat_info[] __initdata = { static struct acpi_platform_list plat_info[] __initdata = {
{"HP ", "ProLiant", 0, ACPI_SIG_FADT, all_versions, 0, PSS}, {"HP ", "ProLiant", 0, ACPI_SIG_FADT, all_versions, NULL, PSS},
{"ORACLE", "X4-2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4-2 ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4-2L ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4-2L ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4-2B ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4-2B ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X3-2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X3-2 ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X3-2L ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X3-2L ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X3-2B ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X3-2B ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4470M2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4470M2 ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4270M3 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4270M3 ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4270M2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4270M2 ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4170M2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4170M2 ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4170 M3", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4170 M3", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X4275 M3", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X4275 M3", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "X6-2 ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "X6-2 ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{"ORACLE", "Sudbury ", 0, ACPI_SIG_FADT, all_versions, 0, PPC}, {"ORACLE", "Sudbury ", 0, ACPI_SIG_FADT, all_versions, NULL, PPC},
{ } /* End */ { } /* End */
}; };
......
...@@ -1041,9 +1041,14 @@ static struct cpufreq_driver powernv_cpufreq_driver = { ...@@ -1041,9 +1041,14 @@ static struct cpufreq_driver powernv_cpufreq_driver = {
static int init_chip_info(void) static int init_chip_info(void)
{ {
unsigned int chip[256]; unsigned int *chip;
unsigned int cpu, i; unsigned int cpu, i;
unsigned int prev_chip_id = UINT_MAX; unsigned int prev_chip_id = UINT_MAX;
int ret = 0;
chip = kcalloc(num_possible_cpus(), sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
unsigned int id = cpu_to_chip_id(cpu); unsigned int id = cpu_to_chip_id(cpu);
...@@ -1055,8 +1060,10 @@ static int init_chip_info(void) ...@@ -1055,8 +1060,10 @@ static int init_chip_info(void)
} }
chips = kcalloc(nr_chips, sizeof(struct chip), GFP_KERNEL); chips = kcalloc(nr_chips, sizeof(struct chip), GFP_KERNEL);
if (!chips) if (!chips) {
return -ENOMEM; ret = -ENOMEM;
goto free_and_return;
}
for (i = 0; i < nr_chips; i++) { for (i = 0; i < nr_chips; i++) {
chips[i].id = chip[i]; chips[i].id = chip[i];
...@@ -1066,7 +1073,9 @@ static int init_chip_info(void) ...@@ -1066,7 +1073,9 @@ static int init_chip_info(void)
per_cpu(chip_info, cpu) = &chips[i]; per_cpu(chip_info, cpu) = &chips[i];
} }
return 0; free_and_return:
kfree(chip);
return ret;
} }
static inline void clean_chip_info(void) static inline void clean_chip_info(void)
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
static struct regulator *vddarm; static struct regulator *vddarm;
static unsigned long regulator_latency; static unsigned long regulator_latency;
#ifdef CONFIG_CPU_S3C6410
struct s3c64xx_dvfs { struct s3c64xx_dvfs {
unsigned int vddarm_min; unsigned int vddarm_min;
unsigned int vddarm_max; unsigned int vddarm_max;
...@@ -48,7 +47,6 @@ static struct cpufreq_frequency_table s3c64xx_freq_table[] = { ...@@ -48,7 +47,6 @@ static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
{ 0, 4, 800000 }, { 0, 4, 800000 },
{ 0, 0, CPUFREQ_TABLE_END }, { 0, 0, CPUFREQ_TABLE_END },
}; };
#endif
static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index) unsigned int index)
...@@ -149,11 +147,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) ...@@ -149,11 +147,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
if (policy->cpu != 0) if (policy->cpu != 0)
return -EINVAL; return -EINVAL;
if (s3c64xx_freq_table == NULL) {
pr_err("No frequency information for this CPU\n");
return -ENODEV;
}
policy->clk = clk_get(NULL, "armclk"); policy->clk = clk_get(NULL, "armclk");
if (IS_ERR(policy->clk)) { if (IS_ERR(policy->clk)) {
pr_err("Unable to obtain ARMCLK: %ld\n", pr_err("Unable to obtain ARMCLK: %ld\n",
......
/* /*
* System Control and Power Interface (SCPI) based CPUFreq Interface driver * System Control and Power Interface (SCPI) based CPUFreq Interface driver
* *
* It provides necessary ops to arm_big_little cpufreq driver.
*
* Copyright (C) 2015 ARM Ltd. * Copyright (C) 2015 ARM Ltd.
* Sudeep Holla <sudeep.holla@arm.com> * Sudeep Holla <sudeep.holla@arm.com>
* *
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev; static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev;
/** /**
* sun50i_cpufreq_get_efuse() - Parse and return efuse value present on SoC * sun50i_cpufreq_get_efuse() - Determine speed grade from efuse value
* @versions: Set to the value parsed from efuse * @versions: Set to the value parsed from efuse
* *
* Returns 0 if success. * Returns 0 if success.
...@@ -69,21 +69,16 @@ static int sun50i_cpufreq_get_efuse(u32 *versions) ...@@ -69,21 +69,16 @@ static int sun50i_cpufreq_get_efuse(u32 *versions)
return PTR_ERR(speedbin); return PTR_ERR(speedbin);
efuse_value = (*speedbin >> NVMEM_SHIFT) & NVMEM_MASK; efuse_value = (*speedbin >> NVMEM_SHIFT) & NVMEM_MASK;
switch (efuse_value) {
case 0b0001: /*
*versions = 1; * We treat unexpected efuse values as if the SoC was from
break; * the slowest bin. Expected efuse values are 1-3, slowest
case 0b0011: * to fastest.
*versions = 2; */
break; if (efuse_value >= 1 && efuse_value <= 3)
default: *versions = efuse_value - 1;
/* else
* For other situations, we treat it as bin0.
* This vf table can be run for any good cpu.
*/
*versions = 0; *versions = 0;
break;
}
kfree(speedbin); kfree(speedbin);
return 0; return 0;
......
...@@ -31,11 +31,17 @@ ...@@ -31,11 +31,17 @@
#define DRA7_EFUSE_OD_MPU_OPP BIT(1) #define DRA7_EFUSE_OD_MPU_OPP BIT(1)
#define DRA7_EFUSE_HIGH_MPU_OPP BIT(2) #define DRA7_EFUSE_HIGH_MPU_OPP BIT(2)
#define OMAP3_CONTROL_DEVICE_STATUS 0x4800244C
#define OMAP3_CONTROL_IDCODE 0x4830A204
#define OMAP34xx_ProdID_SKUID 0x4830A20C
#define OMAP3_SYSCON_BASE (0x48000000 + 0x2000 + 0x270)
#define VERSION_COUNT 2 #define VERSION_COUNT 2
struct ti_cpufreq_data; struct ti_cpufreq_data;
struct ti_cpufreq_soc_data { struct ti_cpufreq_soc_data {
const char * const *reg_names;
unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data, unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data,
unsigned long efuse); unsigned long efuse);
unsigned long efuse_fallback; unsigned long efuse_fallback;
...@@ -85,6 +91,13 @@ static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data, ...@@ -85,6 +91,13 @@ static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data,
return calculated_efuse; return calculated_efuse;
} }
static unsigned long omap3_efuse_xlate(struct ti_cpufreq_data *opp_data,
unsigned long efuse)
{
/* OPP enable bit ("Speed Binned") */
return BIT(efuse);
}
static struct ti_cpufreq_soc_data am3x_soc_data = { static struct ti_cpufreq_soc_data am3x_soc_data = {
.efuse_xlate = amx3_efuse_xlate, .efuse_xlate = amx3_efuse_xlate,
.efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ, .efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ,
...@@ -112,6 +125,74 @@ static struct ti_cpufreq_soc_data dra7_soc_data = { ...@@ -112,6 +125,74 @@ static struct ti_cpufreq_soc_data dra7_soc_data = {
.multi_regulator = true, .multi_regulator = true,
}; };
/*
* OMAP35x TRM (SPRUF98K):
* CONTROL_IDCODE (0x4830 A204) describes Silicon revisions.
* Control OMAP Status Register 15:0 (Address 0x4800 244C)
* to separate between omap3503, omap3515, omap3525, omap3530
* and feature presence.
* There are encodings for versions limited to 400/266MHz
* but we ignore.
* Not clear if this also holds for omap34xx.
* some eFuse values e.g. CONTROL_FUSE_OPP1_VDD1
* are stored in the SYSCON register range
* Register 0x4830A20C [ProdID.SKUID] [0:3]
* 0x0 for normal 600/430MHz device.
* 0x8 for 720/520MHz device.
* Not clear what omap34xx value is.
*/
static struct ti_cpufreq_soc_data omap34xx_soc_data = {
.efuse_xlate = omap3_efuse_xlate,
.efuse_offset = OMAP34xx_ProdID_SKUID - OMAP3_SYSCON_BASE,
.efuse_shift = 3,
.efuse_mask = BIT(3),
.rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE,
.multi_regulator = false,
};
/*
* AM/DM37x TRM (SPRUGN4M)
* CONTROL_IDCODE (0x4830 A204) describes Silicon revisions.
* Control Device Status Register 15:0 (Address 0x4800 244C)
* to separate between am3703, am3715, dm3725, dm3730
* and feature presence.
* Speed Binned = Bit 9
* 0 800/600 MHz
* 1 1000/800 MHz
* some eFuse values e.g. CONTROL_FUSE_OPP 1G_VDD1
* are stored in the SYSCON register range.
* There is no 0x4830A20C [ProdID.SKUID] register (exists but
* seems to always read as 0).
*/
static const char * const omap3_reg_names[] = {"cpu0", "vbb"};
static struct ti_cpufreq_soc_data omap36xx_soc_data = {
.reg_names = omap3_reg_names,
.efuse_xlate = omap3_efuse_xlate,
.efuse_offset = OMAP3_CONTROL_DEVICE_STATUS - OMAP3_SYSCON_BASE,
.efuse_shift = 9,
.efuse_mask = BIT(9),
.rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE,
.multi_regulator = true,
};
/*
* AM3517 is quite similar to AM/DM37x except that it has no
* high speed grade eFuse and no abb ldo
*/
static struct ti_cpufreq_soc_data am3517_soc_data = {
.efuse_xlate = omap3_efuse_xlate,
.efuse_offset = OMAP3_CONTROL_DEVICE_STATUS - OMAP3_SYSCON_BASE,
.efuse_shift = 0,
.efuse_mask = 0,
.rev_offset = OMAP3_CONTROL_IDCODE - OMAP3_SYSCON_BASE,
.multi_regulator = false,
};
/** /**
* ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC
* @opp_data: pointer to ti_cpufreq_data context * @opp_data: pointer to ti_cpufreq_data context
...@@ -128,7 +209,17 @@ static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data, ...@@ -128,7 +209,17 @@ static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data,
ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset, ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset,
&efuse); &efuse);
if (ret) { if (ret == -EIO) {
/* not a syscon register! */
void __iomem *regs = ioremap(OMAP3_SYSCON_BASE +
opp_data->soc_data->efuse_offset, 4);
if (!regs)
return -ENOMEM;
efuse = readl(regs);
iounmap(regs);
}
else if (ret) {
dev_err(dev, dev_err(dev,
"Failed to read the efuse value from syscon: %d\n", "Failed to read the efuse value from syscon: %d\n",
ret); ret);
...@@ -159,7 +250,17 @@ static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data, ...@@ -159,7 +250,17 @@ static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data,
ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset, ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset,
&revision); &revision);
if (ret) { if (ret == -EIO) {
/* not a syscon register! */
void __iomem *regs = ioremap(OMAP3_SYSCON_BASE +
opp_data->soc_data->rev_offset, 4);
if (!regs)
return -ENOMEM;
revision = readl(regs);
iounmap(regs);
}
else if (ret) {
dev_err(dev, dev_err(dev,
"Failed to read the revision number from syscon: %d\n", "Failed to read the revision number from syscon: %d\n",
ret); ret);
...@@ -189,8 +290,14 @@ static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data) ...@@ -189,8 +290,14 @@ static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data)
static const struct of_device_id ti_cpufreq_of_match[] = { static const struct of_device_id ti_cpufreq_of_match[] = {
{ .compatible = "ti,am33xx", .data = &am3x_soc_data, }, { .compatible = "ti,am33xx", .data = &am3x_soc_data, },
{ .compatible = "ti,am3517", .data = &am3517_soc_data, },
{ .compatible = "ti,am43", .data = &am4x_soc_data, }, { .compatible = "ti,am43", .data = &am4x_soc_data, },
{ .compatible = "ti,dra7", .data = &dra7_soc_data }, { .compatible = "ti,dra7", .data = &dra7_soc_data },
{ .compatible = "ti,omap34xx", .data = &omap34xx_soc_data, },
{ .compatible = "ti,omap36xx", .data = &omap36xx_soc_data, },
/* legacy */
{ .compatible = "ti,omap3430", .data = &omap34xx_soc_data, },
{ .compatible = "ti,omap3630", .data = &omap36xx_soc_data, },
{}, {},
}; };
...@@ -212,7 +319,7 @@ static int ti_cpufreq_probe(struct platform_device *pdev) ...@@ -212,7 +319,7 @@ static int ti_cpufreq_probe(struct platform_device *pdev)
const struct of_device_id *match; const struct of_device_id *match;
struct opp_table *ti_opp_table; struct opp_table *ti_opp_table;
struct ti_cpufreq_data *opp_data; struct ti_cpufreq_data *opp_data;
const char * const reg_names[] = {"vdd", "vbb"}; const char * const default_reg_names[] = {"vdd", "vbb"};
int ret; int ret;
match = dev_get_platdata(&pdev->dev); match = dev_get_platdata(&pdev->dev);
...@@ -268,9 +375,13 @@ static int ti_cpufreq_probe(struct platform_device *pdev) ...@@ -268,9 +375,13 @@ static int ti_cpufreq_probe(struct platform_device *pdev)
opp_data->opp_table = ti_opp_table; opp_data->opp_table = ti_opp_table;
if (opp_data->soc_data->multi_regulator) { if (opp_data->soc_data->multi_regulator) {
const char * const *reg_names = default_reg_names;
if (opp_data->soc_data->reg_names)
reg_names = opp_data->soc_data->reg_names;
ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev, ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev,
reg_names, reg_names,
ARRAY_SIZE(reg_names)); ARRAY_SIZE(default_reg_names));
if (IS_ERR(ti_opp_table)) { if (IS_ERR(ti_opp_table)) {
dev_pm_opp_put_supported_hw(opp_data->opp_table); dev_pm_opp_put_supported_hw(opp_data->opp_table);
ret = PTR_ERR(ti_opp_table); ret = PTR_ERR(ti_opp_table);
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* Versatile Express SPC CPUFreq Interface driver * Versatile Express SPC CPUFreq Interface driver
* *
* It provides necessary ops to arm_big_little cpufreq driver. * Copyright (C) 2013 - 2019 ARM Ltd.
* Sudeep Holla <sudeep.holla@arm.com>
* *
* Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro.
* Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com> * Viresh Kumar <viresh.kumar@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cpu_cooling.h>
#include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/topology.h>
#include <linux/types.h> #include <linux/types.h>
#include "arm_big_little.h" /* Currently we support only two clusters */
#define A15_CLUSTER 0
#define A7_CLUSTER 1
#define MAX_CLUSTERS 2
#ifdef CONFIG_BL_SWITCHER
#include <asm/bL_switcher.h>
static bool bL_switching_enabled;
#define is_bL_switching_enabled() bL_switching_enabled
#define set_switching_enabled(x) (bL_switching_enabled = (x))
#else
#define is_bL_switching_enabled() false
#define set_switching_enabled(x) do { } while (0)
#define bL_switch_request(...) do { } while (0)
#define bL_switcher_put_enabled() do { } while (0)
#define bL_switcher_get_enabled() do { } while (0)
#endif
#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
static struct thermal_cooling_device *cdev[MAX_CLUSTERS];
static struct clk *clk[MAX_CLUSTERS];
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
static atomic_t cluster_usage[MAX_CLUSTERS + 1];
static unsigned int clk_big_min; /* (Big) clock frequencies */
static unsigned int clk_little_max; /* Maximum clock frequency (Little) */
static DEFINE_PER_CPU(unsigned int, physical_cluster);
static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq);
static struct mutex cluster_lock[MAX_CLUSTERS];
static inline int raw_cpu_to_cluster(int cpu)
{
return topology_physical_package_id(cpu);
}
static inline int cpu_to_cluster(int cpu)
{
return is_bL_switching_enabled() ?
MAX_CLUSTERS : raw_cpu_to_cluster(cpu);
}
static unsigned int find_cluster_maxfreq(int cluster)
{
int j;
u32 max_freq = 0, cpu_freq;
for_each_online_cpu(j) {
cpu_freq = per_cpu(cpu_last_req_freq, j);
if (cluster == per_cpu(physical_cluster, j) &&
max_freq < cpu_freq)
max_freq = cpu_freq;
}
return max_freq;
}
static unsigned int clk_get_cpu_rate(unsigned int cpu)
{
u32 cur_cluster = per_cpu(physical_cluster, cpu);
u32 rate = clk_get_rate(clk[cur_cluster]) / 1000;
/* For switcher we use virtual A7 clock rates */
if (is_bL_switching_enabled())
rate = VIRT_FREQ(cur_cluster, rate);
return rate;
}
static unsigned int ve_spc_cpufreq_get_rate(unsigned int cpu)
{
if (is_bL_switching_enabled())
return per_cpu(cpu_last_req_freq, cpu);
else
return clk_get_cpu_rate(cpu);
}
static unsigned int
ve_spc_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
{
u32 new_rate, prev_rate;
int ret;
bool bLs = is_bL_switching_enabled();
mutex_lock(&cluster_lock[new_cluster]);
if (bLs) {
prev_rate = per_cpu(cpu_last_req_freq, cpu);
per_cpu(cpu_last_req_freq, cpu) = rate;
per_cpu(physical_cluster, cpu) = new_cluster;
new_rate = find_cluster_maxfreq(new_cluster);
new_rate = ACTUAL_FREQ(new_cluster, new_rate);
} else {
new_rate = rate;
}
ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
if (!ret) {
/*
* FIXME: clk_set_rate hasn't returned an error here however it
* may be that clk_change_rate failed due to hardware or
* firmware issues and wasn't able to report that due to the
* current design of the clk core layer. To work around this
* problem we will read back the clock rate and check it is
* correct. This needs to be removed once clk core is fixed.
*/
if (clk_get_rate(clk[new_cluster]) != new_rate * 1000)
ret = -EIO;
}
if (WARN_ON(ret)) {
if (bLs) {
per_cpu(cpu_last_req_freq, cpu) = prev_rate;
per_cpu(physical_cluster, cpu) = old_cluster;
}
mutex_unlock(&cluster_lock[new_cluster]);
return ret;
}
mutex_unlock(&cluster_lock[new_cluster]);
/* Recalc freq for old cluster when switching clusters */
if (old_cluster != new_cluster) {
/* Switch cluster */
bL_switch_request(cpu, new_cluster);
mutex_lock(&cluster_lock[old_cluster]);
/* Set freq of old cluster if there are cpus left on it */
new_rate = find_cluster_maxfreq(old_cluster);
new_rate = ACTUAL_FREQ(old_cluster, new_rate);
if (new_rate &&
clk_set_rate(clk[old_cluster], new_rate * 1000)) {
pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n",
__func__, ret, old_cluster);
}
mutex_unlock(&cluster_lock[old_cluster]);
}
return 0;
}
/* Set clock frequency */
static int ve_spc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index)
{
u32 cpu = policy->cpu, cur_cluster, new_cluster, actual_cluster;
unsigned int freqs_new;
int ret;
cur_cluster = cpu_to_cluster(cpu);
new_cluster = actual_cluster = per_cpu(physical_cluster, cpu);
freqs_new = freq_table[cur_cluster][index].frequency;
if (is_bL_switching_enabled()) {
if (actual_cluster == A15_CLUSTER && freqs_new < clk_big_min)
new_cluster = A7_CLUSTER;
else if (actual_cluster == A7_CLUSTER &&
freqs_new > clk_little_max)
new_cluster = A15_CLUSTER;
}
ret = ve_spc_cpufreq_set_rate(cpu, actual_cluster, new_cluster,
freqs_new);
if (!ret) {
arch_set_freq_scale(policy->related_cpus, freqs_new,
policy->cpuinfo.max_freq);
}
return ret;
}
static inline u32 get_table_count(struct cpufreq_frequency_table *table)
{
int count;
for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++)
;
return count;
}
/* get the minimum frequency in the cpufreq_frequency_table */
static inline u32 get_table_min(struct cpufreq_frequency_table *table)
{
struct cpufreq_frequency_table *pos;
u32 min_freq = ~0;
cpufreq_for_each_entry(pos, table)
if (pos->frequency < min_freq)
min_freq = pos->frequency;
return min_freq;
}
/* get the maximum frequency in the cpufreq_frequency_table */
static inline u32 get_table_max(struct cpufreq_frequency_table *table)
{
struct cpufreq_frequency_table *pos;
u32 max_freq = 0;
cpufreq_for_each_entry(pos, table)
if (pos->frequency > max_freq)
max_freq = pos->frequency;
return max_freq;
}
static bool search_frequency(struct cpufreq_frequency_table *table, int size,
unsigned int freq)
{
int count;
for (count = 0; count < size; count++) {
if (table[count].frequency == freq)
return true;
}
return false;
}
static int merge_cluster_tables(void)
{
int i, j, k = 0, count = 1;
struct cpufreq_frequency_table *table;
for (i = 0; i < MAX_CLUSTERS; i++)
count += get_table_count(freq_table[i]);
table = kcalloc(count, sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
freq_table[MAX_CLUSTERS] = table;
/* Add in reverse order to get freqs in increasing order */
for (i = MAX_CLUSTERS - 1; i >= 0; i--, count = k) {
for (j = 0; freq_table[i][j].frequency != CPUFREQ_TABLE_END;
j++) {
if (i == A15_CLUSTER &&
search_frequency(table, count, freq_table[i][j].frequency))
continue; /* skip duplicates */
table[k++].frequency =
VIRT_FREQ(i, freq_table[i][j].frequency);
}
}
table[k].driver_data = k;
table[k].frequency = CPUFREQ_TABLE_END;
return 0;
}
static int ve_spc_init_opp_table(const struct cpumask *cpumask) static void _put_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{ {
struct device *cpu_dev = get_cpu_device(cpumask_first(cpumask)); u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
if (!freq_table[cluster])
return;
clk_put(clk[cluster]);
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
}
static void put_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{
u32 cluster = cpu_to_cluster(cpu_dev->id);
int i;
if (atomic_dec_return(&cluster_usage[cluster]))
return;
if (cluster < MAX_CLUSTERS)
return _put_cluster_clk_and_freq_table(cpu_dev, cpumask);
for_each_present_cpu(i) {
struct device *cdev = get_cpu_device(i);
if (!cdev)
return;
_put_cluster_clk_and_freq_table(cdev, cpumask);
}
/* free virtual table */
kfree(freq_table[cluster]);
}
static int _get_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{
u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
int ret;
if (freq_table[cluster])
return 0;
/* /*
* platform specific SPC code must initialise the opp table * platform specific SPC code must initialise the opp table
* so just check if the OPP count is non-zero * so just check if the OPP count is non-zero
*/ */
return dev_pm_opp_get_opp_count(cpu_dev) <= 0; ret = dev_pm_opp_get_opp_count(cpu_dev) <= 0;
if (ret)
goto out;
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
if (ret)
goto out;
clk[cluster] = clk_get(cpu_dev, NULL);
if (!IS_ERR(clk[cluster]))
return 0;
dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n",
__func__, cpu_dev->id, cluster);
ret = PTR_ERR(clk[cluster]);
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
out:
dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
cluster);
return ret;
} }
static int ve_spc_get_transition_latency(struct device *cpu_dev) static int get_cluster_clk_and_freq_table(struct device *cpu_dev,
const struct cpumask *cpumask)
{ {
return 1000000; /* 1 ms */ u32 cluster = cpu_to_cluster(cpu_dev->id);
int i, ret;
if (atomic_inc_return(&cluster_usage[cluster]) != 1)
return 0;
if (cluster < MAX_CLUSTERS) {
ret = _get_cluster_clk_and_freq_table(cpu_dev, cpumask);
if (ret)
atomic_dec(&cluster_usage[cluster]);
return ret;
}
/*
* Get data for all clusters and fill virtual cluster with a merge of
* both
*/
for_each_present_cpu(i) {
struct device *cdev = get_cpu_device(i);
if (!cdev)
return -ENODEV;
ret = _get_cluster_clk_and_freq_table(cdev, cpumask);
if (ret)
goto put_clusters;
}
ret = merge_cluster_tables();
if (ret)
goto put_clusters;
/* Assuming 2 cluster, set clk_big_min and clk_little_max */
clk_big_min = get_table_min(freq_table[A15_CLUSTER]);
clk_little_max = VIRT_FREQ(A7_CLUSTER,
get_table_max(freq_table[A7_CLUSTER]));
return 0;
put_clusters:
for_each_present_cpu(i) {
struct device *cdev = get_cpu_device(i);
if (!cdev)
return -ENODEV;
_put_cluster_clk_and_freq_table(cdev, cpumask);
}
atomic_dec(&cluster_usage[cluster]);
return ret;
} }
static const struct cpufreq_arm_bL_ops ve_spc_cpufreq_ops = { /* Per-CPU initialization */
.name = "vexpress-spc", static int ve_spc_cpufreq_init(struct cpufreq_policy *policy)
.get_transition_latency = ve_spc_get_transition_latency, {
.init_opp_table = ve_spc_init_opp_table, u32 cur_cluster = cpu_to_cluster(policy->cpu);
struct device *cpu_dev;
int ret;
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("%s: failed to get cpu%d device\n", __func__,
policy->cpu);
return -ENODEV;
}
if (cur_cluster < MAX_CLUSTERS) {
int cpu;
cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
for_each_cpu(cpu, policy->cpus)
per_cpu(physical_cluster, cpu) = cur_cluster;
} else {
/* Assumption: during init, we are always running on A15 */
per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER;
}
ret = get_cluster_clk_and_freq_table(cpu_dev, policy->cpus);
if (ret)
return ret;
policy->freq_table = freq_table[cur_cluster];
policy->cpuinfo.transition_latency = 1000000; /* 1 ms */
dev_pm_opp_of_register_em(policy->cpus);
if (is_bL_switching_enabled())
per_cpu(cpu_last_req_freq, policy->cpu) =
clk_get_cpu_rate(policy->cpu);
dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu);
return 0;
}
static int ve_spc_cpufreq_exit(struct cpufreq_policy *policy)
{
struct device *cpu_dev;
int cur_cluster = cpu_to_cluster(policy->cpu);
if (cur_cluster < MAX_CLUSTERS) {
cpufreq_cooling_unregister(cdev[cur_cluster]);
cdev[cur_cluster] = NULL;
}
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("%s: failed to get cpu%d device\n", __func__,
policy->cpu);
return -ENODEV;
}
put_cluster_clk_and_freq_table(cpu_dev, policy->related_cpus);
return 0;
}
static void ve_spc_cpufreq_ready(struct cpufreq_policy *policy)
{
int cur_cluster = cpu_to_cluster(policy->cpu);
/* Do not register a cpu_cooling device if we are in IKS mode */
if (cur_cluster >= MAX_CLUSTERS)
return;
cdev[cur_cluster] = of_cpufreq_cooling_register(policy);
}
static struct cpufreq_driver ve_spc_cpufreq_driver = {
.name = "vexpress-spc",
.flags = CPUFREQ_STICKY |
CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = ve_spc_cpufreq_set_target,
.get = ve_spc_cpufreq_get_rate,
.init = ve_spc_cpufreq_init,
.exit = ve_spc_cpufreq_exit,
.ready = ve_spc_cpufreq_ready,
.attr = cpufreq_generic_attr,
}; };
#ifdef CONFIG_BL_SWITCHER
static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb,
unsigned long action, void *_arg)
{
pr_debug("%s: action: %ld\n", __func__, action);
switch (action) {
case BL_NOTIFY_PRE_ENABLE:
case BL_NOTIFY_PRE_DISABLE:
cpufreq_unregister_driver(&ve_spc_cpufreq_driver);
break;
case BL_NOTIFY_POST_ENABLE:
set_switching_enabled(true);
cpufreq_register_driver(&ve_spc_cpufreq_driver);
break;
case BL_NOTIFY_POST_DISABLE:
set_switching_enabled(false);
cpufreq_register_driver(&ve_spc_cpufreq_driver);
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block bL_switcher_notifier = {
.notifier_call = bL_cpufreq_switcher_notifier,
};
static int __bLs_register_notifier(void)
{
return bL_switcher_register_notifier(&bL_switcher_notifier);
}
static int __bLs_unregister_notifier(void)
{
return bL_switcher_unregister_notifier(&bL_switcher_notifier);
}
#else
static int __bLs_register_notifier(void) { return 0; }
static int __bLs_unregister_notifier(void) { return 0; }
#endif
static int ve_spc_cpufreq_probe(struct platform_device *pdev) static int ve_spc_cpufreq_probe(struct platform_device *pdev)
{ {
return bL_cpufreq_register(&ve_spc_cpufreq_ops); int ret, i;
set_switching_enabled(bL_switcher_get_enabled());
for (i = 0; i < MAX_CLUSTERS; i++)
mutex_init(&cluster_lock[i]);
ret = cpufreq_register_driver(&ve_spc_cpufreq_driver);
if (ret) {
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
__func__, ve_spc_cpufreq_driver.name, ret);
} else {
ret = __bLs_register_notifier();
if (ret)
cpufreq_unregister_driver(&ve_spc_cpufreq_driver);
else
pr_info("%s: Registered platform driver: %s\n",
__func__, ve_spc_cpufreq_driver.name);
}
bL_switcher_put_enabled();
return ret;
} }
static int ve_spc_cpufreq_remove(struct platform_device *pdev) static int ve_spc_cpufreq_remove(struct platform_device *pdev)
{ {
bL_cpufreq_unregister(&ve_spc_cpufreq_ops); bL_switcher_get_enabled();
__bLs_unregister_notifier();
cpufreq_unregister_driver(&ve_spc_cpufreq_driver);
bL_switcher_put_enabled();
pr_info("%s: Un-registered platform driver: %s\n", __func__,
ve_spc_cpufreq_driver.name);
return 0; return 0;
} }
...@@ -68,4 +599,7 @@ static struct platform_driver ve_spc_cpufreq_platdrv = { ...@@ -68,4 +599,7 @@ static struct platform_driver ve_spc_cpufreq_platdrv = {
}; };
module_platform_driver(ve_spc_cpufreq_platdrv); module_platform_driver(ve_spc_cpufreq_platdrv);
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("Vexpress SPC ARM big LITTLE cpufreq driver");
MODULE_LICENSE("GPL v2");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment