Merge branch 'pm-cpufreq'

* pm-cpufreq: (25 commits)
  dt-bindings: cpufreq: Document operating-points-v2-kryo-cpu
  cpufreq: Add Kryo CPU scaling driver
  cpufreq: Use static SRCU initializer
  kernel/SRCU: provide a static initializer
  cpufreq: Fix new policy initialization during limits updates via sysfs
  cpufreq: tegra20: Wrap cpufreq into platform driver
  cpufreq: tegra20: Allow cpufreq driver to be built as loadable module
  cpufreq: tegra20: Check if this is Tegra20 machine
  cpufreq: tegra20: Remove unneeded variable initialization
  cpufreq: tegra20: Remove unnecessary parentheses
  cpufreq: tegra20: Remove unneeded check in tegra_cpu_init
  cpufreq: tegra20: Release clocks properly
  cpufreq: tegra20: Remove EMC clock usage
  cpufreq: tegra20: Clean up included headers
  cpufreq: tegra20: Clean up whitespaces in the code
  cpufreq: tegra20: Change module description
  Revert "cpufreq: rcar: Add support for R8A7795 SoC"
  Revert "cpufreq: dt: Add r8a7796 support to to use generic cpufreq driver"
  cpufreq: intel_pstate: allow trace in passive mode
  cpufreq: optimize cpufreq_notify_transition()
  ...
This commit is contained in:
Rafael J. Wysocki 2018-06-04 10:40:57 +02:00
commit e27f84e163
17 changed files with 1240 additions and 165 deletions

View File

@ -0,0 +1,680 @@
Qualcomm Technologies, Inc. KRYO CPUFreq and OPP bindings
===================================
In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
that have KRYO processors, the CPU ferequencies subset and voltage value
of each OPP varies based on the silicon variant in use.
Qualcomm Technologies, Inc. Process Voltage Scaling Tables
defines the voltage and frequency value based on the msm-id in SMEM
and speedbin blown in the efuse combination.
The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
to provide the OPP framework with required information (existing HW bitmap).
This is used to determine the voltage and frequency value for each OPP of
operating-points-v2 table when it is parsed by the OPP framework.
Required properties:
--------------------
In 'cpus' nodes:
- operating-points-v2: Phandle to the operating-points-v2 table to use.
In 'operating-points-v2' table:
- compatible: Should be
- 'operating-points-v2-kryo-cpu' for apq8096 and msm8996.
- nvmem-cells: A phandle pointing to a nvmem-cells node representing the
efuse registers that has information about the
speedbin that is used to select the right frequency/voltage
value pair.
Please refer the for nvmem-cells
bindings Documentation/devicetree/bindings/nvmem/nvmem.txt
and also examples below.
In every OPP node:
- opp-supported-hw: A single 32 bit bitmap value, representing compatible HW.
Bitmap:
0: MSM8996 V3, speedbin 0
1: MSM8996 V3, speedbin 1
2: MSM8996 V3, speedbin 2
3: unused
4: MSM8996 SG, speedbin 0
5: MSM8996 SG, speedbin 1
6: MSM8996 SG, speedbin 2
7-31: unused
Example 1:
---------
cpus {
#address-cells = <2>;
#size-cells = <0>;
CPU0: cpu@0 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x0>;
enable-method = "psci";
clocks = <&kryocc 0>;
cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster0_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_0>;
L2_0: l2-cache {
compatible = "cache";
cache-level = <2>;
};
};
CPU1: cpu@1 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x1>;
enable-method = "psci";
clocks = <&kryocc 0>;
cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster0_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_0>;
};
CPU2: cpu@100 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x100>;
enable-method = "psci";
clocks = <&kryocc 1>;
cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster1_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_1>;
L2_1: l2-cache {
compatible = "cache";
cache-level = <2>;
};
};
CPU3: cpu@101 {
device_type = "cpu";
compatible = "qcom,kryo";
reg = <0x0 0x101>;
enable-method = "psci";
clocks = <&kryocc 1>;
cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster1_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_1>;
};
cpu-map {
cluster0 {
core0 {
cpu = <&CPU0>;
};
core1 {
cpu = <&CPU1>;
};
};
cluster1 {
core0 {
cpu = <&CPU2>;
};
core1 {
cpu = <&CPU3>;
};
};
};
};
cluster0_opp: opp_table0 {
compatible = "operating-points-v2-kryo-cpu";
nvmem-cells = <&speedbin_efuse>;
opp-shared;
opp-307200000 {
opp-hz = /bits/ 64 <307200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-384000000 {
opp-hz = /bits/ 64 <384000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-422400000 {
opp-hz = /bits/ 64 <422400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-460800000 {
opp-hz = /bits/ 64 <460800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-480000000 {
opp-hz = /bits/ 64 <480000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-537600000 {
opp-hz = /bits/ 64 <537600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-556800000 {
opp-hz = /bits/ 64 <556800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-614400000 {
opp-hz = /bits/ 64 <614400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-652800000 {
opp-hz = /bits/ 64 <652800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-691200000 {
opp-hz = /bits/ 64 <691200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-729600000 {
opp-hz = /bits/ 64 <729600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-768000000 {
opp-hz = /bits/ 64 <768000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-844800000 {
opp-hz = /bits/ 64 <844800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-902400000 {
opp-hz = /bits/ 64 <902400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-960000000 {
opp-hz = /bits/ 64 <960000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-979200000 {
opp-hz = /bits/ 64 <979200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1036800000 {
opp-hz = /bits/ 64 <1036800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1056000000 {
opp-hz = /bits/ 64 <1056000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1113600000 {
opp-hz = /bits/ 64 <1113600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1132800000 {
opp-hz = /bits/ 64 <1132800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1190400000 {
opp-hz = /bits/ 64 <1190400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1209600000 {
opp-hz = /bits/ 64 <1209600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1228800000 {
opp-hz = /bits/ 64 <1228800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1286400000 {
opp-hz = /bits/ 64 <1286400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1324800000 {
opp-hz = /bits/ 64 <1324800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x5>;
clock-latency-ns = <200000>;
};
opp-1363200000 {
opp-hz = /bits/ 64 <1363200000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x72>;
clock-latency-ns = <200000>;
};
opp-1401600000 {
opp-hz = /bits/ 64 <1401600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x5>;
clock-latency-ns = <200000>;
};
opp-1440000000 {
opp-hz = /bits/ 64 <1440000000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1478400000 {
opp-hz = /bits/ 64 <1478400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-1497600000 {
opp-hz = /bits/ 64 <1497600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x4>;
clock-latency-ns = <200000>;
};
opp-1516800000 {
opp-hz = /bits/ 64 <1516800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1593600000 {
opp-hz = /bits/ 64 <1593600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x71>;
clock-latency-ns = <200000>;
};
opp-1996800000 {
opp-hz = /bits/ 64 <1996800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x20>;
clock-latency-ns = <200000>;
};
opp-2188800000 {
opp-hz = /bits/ 64 <2188800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
};
cluster1_opp: opp_table1 {
compatible = "operating-points-v2-kryo-cpu";
nvmem-cells = <&speedbin_efuse>;
opp-shared;
opp-307200000 {
opp-hz = /bits/ 64 <307200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-384000000 {
opp-hz = /bits/ 64 <384000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-403200000 {
opp-hz = /bits/ 64 <403200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-460800000 {
opp-hz = /bits/ 64 <460800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-480000000 {
opp-hz = /bits/ 64 <480000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-537600000 {
opp-hz = /bits/ 64 <537600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-556800000 {
opp-hz = /bits/ 64 <556800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-614400000 {
opp-hz = /bits/ 64 <614400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-652800000 {
opp-hz = /bits/ 64 <652800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-691200000 {
opp-hz = /bits/ 64 <691200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-729600000 {
opp-hz = /bits/ 64 <729600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-748800000 {
opp-hz = /bits/ 64 <748800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-806400000 {
opp-hz = /bits/ 64 <806400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-825600000 {
opp-hz = /bits/ 64 <825600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-883200000 {
opp-hz = /bits/ 64 <883200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-902400000 {
opp-hz = /bits/ 64 <902400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-940800000 {
opp-hz = /bits/ 64 <940800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-979200000 {
opp-hz = /bits/ 64 <979200000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1036800000 {
opp-hz = /bits/ 64 <1036800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1056000000 {
opp-hz = /bits/ 64 <1056000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1113600000 {
opp-hz = /bits/ 64 <1113600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1132800000 {
opp-hz = /bits/ 64 <1132800000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1190400000 {
opp-hz = /bits/ 64 <1190400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1209600000 {
opp-hz = /bits/ 64 <1209600000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1248000000 {
opp-hz = /bits/ 64 <1248000000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1286400000 {
opp-hz = /bits/ 64 <1286400000>;
opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1324800000 {
opp-hz = /bits/ 64 <1324800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1363200000 {
opp-hz = /bits/ 64 <1363200000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1401600000 {
opp-hz = /bits/ 64 <1401600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1440000000 {
opp-hz = /bits/ 64 <1440000000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1478400000 {
opp-hz = /bits/ 64 <1478400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1516800000 {
opp-hz = /bits/ 64 <1516800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1555200000 {
opp-hz = /bits/ 64 <1555200000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1593600000 {
opp-hz = /bits/ 64 <1593600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1632000000 {
opp-hz = /bits/ 64 <1632000000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1670400000 {
opp-hz = /bits/ 64 <1670400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1708800000 {
opp-hz = /bits/ 64 <1708800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1747200000 {
opp-hz = /bits/ 64 <1747200000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1785600000 {
opp-hz = /bits/ 64 <1785600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1804800000 {
opp-hz = /bits/ 64 <1804800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x6>;
clock-latency-ns = <200000>;
};
opp-1824000000 {
opp-hz = /bits/ 64 <1824000000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x71>;
clock-latency-ns = <200000>;
};
opp-1900800000 {
opp-hz = /bits/ 64 <1900800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x74>;
clock-latency-ns = <200000>;
};
opp-1920000000 {
opp-hz = /bits/ 64 <1920000000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-1977600000 {
opp-hz = /bits/ 64 <1977600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x30>;
clock-latency-ns = <200000>;
};
opp-1996800000 {
opp-hz = /bits/ 64 <1996800000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-2054400000 {
opp-hz = /bits/ 64 <2054400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x30>;
clock-latency-ns = <200000>;
};
opp-2073600000 {
opp-hz = /bits/ 64 <2073600000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-2150400000 {
opp-hz = /bits/ 64 <2150400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x31>;
clock-latency-ns = <200000>;
};
opp-2246400000 {
opp-hz = /bits/ 64 <2246400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
opp-2342400000 {
opp-hz = /bits/ 64 <2342400000>;
opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
};
....
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
....
smem_mem: smem-mem@86000000 {
reg = <0x0 0x86000000 0x0 0x200000>;
no-map;
};
....
};
smem {
compatible = "qcom,smem";
memory-region = <&smem_mem>;
hwlocks = <&tcsr_mutex 3>;
};
soc {
....
qfprom: qfprom@74000 {
compatible = "qcom,qfprom";
reg = <0x00074000 0x8ff>;
#address-cells = <1>;
#size-cells = <1>;
....
speedbin_efuse: speedbin@133 {
reg = <0x133 0x1>;
bits = <5 3>;
};
};
};

View File

@ -11654,6 +11654,13 @@ F: Documentation/devicetree/bindings/media/qcom,camss.txt
F: Documentation/media/v4l-drivers/qcom_camss.rst
F: drivers/media/platform/qcom/camss-8x16/
QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
M: Ilia Lin <ilia.lin@gmail.com>
L: linux-pm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
F: drivers/cpufreq/qcom-cpufreq-kryo.c
QUALCOMM EMAC GIGABIT ETHERNET DRIVER
M: Timur Tabi <timur@codeaurora.org>
L: netdev@vger.kernel.org

View File

@ -124,6 +124,17 @@ config ARM_OMAP2PLUS_CPUFREQ
depends on ARCH_OMAP2PLUS
default ARCH_OMAP2PLUS
config ARM_QCOM_CPUFREQ_KRYO
bool "Qualcomm Kryo based CPUFreq"
depends on ARM64
depends on QCOM_QFPROM
depends on QCOM_SMEM
select PM_OPP
help
This adds the CPUFreq driver for Qualcomm Kryo SoC based boards.
If in doubt, say N.
config ARM_S3C_CPUFREQ
bool
help
@ -264,7 +275,7 @@ config ARM_TANGO_CPUFREQ
default y
config ARM_TEGRA20_CPUFREQ
bool "Tegra20 CPUFreq support"
tristate "Tegra20 CPUFreq support"
depends on ARCH_TEGRA
default y
help

View File

@ -65,6 +65,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o

View File

@ -23,6 +23,8 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include "cpufreq-dt.h"
/* Power management in North Bridge register set */
#define ARMADA_37XX_NB_L0L1 0x18
#define ARMADA_37XX_NB_L2L3 0x1C
@ -56,6 +58,16 @@
*/
#define LOAD_LEVEL_NR 4
struct armada37xx_cpufreq_state {
struct regmap *regmap;
u32 nb_l0l1;
u32 nb_l2l3;
u32 nb_dyn_mod;
u32 nb_cpu_load;
};
static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state;
struct armada_37xx_dvfs {
u32 cpu_freq_max;
u8 divider[LOAD_LEVEL_NR];
@ -136,7 +148,7 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
clk_set_parent(clk, parent);
}
static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base)
static void armada37xx_cpufreq_disable_dvfs(struct regmap *base)
{
unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
mask = ARMADA_37XX_NB_DFS_EN;
@ -162,10 +174,47 @@ static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base)
regmap_update_bits(base, reg, mask, mask);
}
static int armada37xx_cpufreq_suspend(struct cpufreq_policy *policy)
{
struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state;
regmap_read(state->regmap, ARMADA_37XX_NB_L0L1, &state->nb_l0l1);
regmap_read(state->regmap, ARMADA_37XX_NB_L2L3, &state->nb_l2l3);
regmap_read(state->regmap, ARMADA_37XX_NB_CPU_LOAD,
&state->nb_cpu_load);
regmap_read(state->regmap, ARMADA_37XX_NB_DYN_MOD, &state->nb_dyn_mod);
return 0;
}
static int armada37xx_cpufreq_resume(struct cpufreq_policy *policy)
{
struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state;
/* Ensure DVFS is disabled otherwise the following registers are RO */
armada37xx_cpufreq_disable_dvfs(state->regmap);
regmap_write(state->regmap, ARMADA_37XX_NB_L0L1, state->nb_l0l1);
regmap_write(state->regmap, ARMADA_37XX_NB_L2L3, state->nb_l2l3);
regmap_write(state->regmap, ARMADA_37XX_NB_CPU_LOAD,
state->nb_cpu_load);
/*
* NB_DYN_MOD register is the one that actually enable back DVFS if it
* was enabled before the suspend operation. This must be done last
* otherwise other registers are not writable.
*/
regmap_write(state->regmap, ARMADA_37XX_NB_DYN_MOD, state->nb_dyn_mod);
return 0;
}
static int __init armada37xx_cpufreq_driver_init(void)
{
struct cpufreq_dt_platform_data pdata;
struct armada_37xx_dvfs *dvfs;
struct platform_device *pdev;
unsigned long freq;
unsigned int cur_frequency;
struct regmap *nb_pm_base;
struct device *cpu_dev;
@ -207,33 +256,58 @@ static int __init armada37xx_cpufreq_driver_init(void)
}
dvfs = armada_37xx_cpu_freq_info_get(cur_frequency);
if (!dvfs)
if (!dvfs) {
clk_put(clk);
return -EINVAL;
}
armada37xx_cpufreq_state = kmalloc(sizeof(*armada37xx_cpufreq_state),
GFP_KERNEL);
if (!armada37xx_cpufreq_state) {
clk_put(clk);
return -ENOMEM;
}
armada37xx_cpufreq_state->regmap = nb_pm_base;
armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
clk_put(clk);
for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR;
load_lvl++) {
unsigned long freq = cur_frequency / dvfs->divider[load_lvl];
freq = cur_frequency / dvfs->divider[load_lvl];
ret = dev_pm_opp_add(cpu_dev, freq, 0);
if (ret) {
/* clean-up the already added opp before leaving */
while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) {
freq = cur_frequency / dvfs->divider[load_lvl];
dev_pm_opp_remove(cpu_dev, freq);
}
return ret;
}
if (ret)
goto remove_opp;
}
/* Now that everything is setup, enable the DVFS at hardware level */
armada37xx_cpufreq_enable_dvfs(nb_pm_base);
pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
pdata.suspend = armada37xx_cpufreq_suspend;
pdata.resume = armada37xx_cpufreq_resume;
return PTR_ERR_OR_ZERO(pdev);
pdev = platform_device_register_data(NULL, "cpufreq-dt", -1, &pdata,
sizeof(pdata));
ret = PTR_ERR_OR_ZERO(pdev);
if (ret)
goto disable_dvfs;
return 0;
disable_dvfs:
armada37xx_cpufreq_disable_dvfs(nb_pm_base);
remove_opp:
/* clean-up the already added opp before leaving */
while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) {
freq = cur_frequency / dvfs->divider[load_lvl];
dev_pm_opp_remove(cpu_dev, freq);
}
kfree(armada37xx_cpufreq_state);
return ret;
}
/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */
late_initcall(armada37xx_cpufreq_driver_init);

View File

@ -66,8 +66,6 @@ static const struct of_device_id whitelist[] __initconst = {
{ .compatible = "renesas,r8a7792", },
{ .compatible = "renesas,r8a7793", },
{ .compatible = "renesas,r8a7794", },
{ .compatible = "renesas,r8a7795", },
{ .compatible = "renesas,r8a7796", },
{ .compatible = "renesas,sh73a0", },
{ .compatible = "rockchip,rk2928", },
@ -118,6 +116,9 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "nvidia,tegra124", },
{ .compatible = "qcom,apq8096", },
{ .compatible = "qcom,msm8996", },
{ .compatible = "st,stih407", },
{ .compatible = "st,stih410", },

View File

@ -346,8 +346,14 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
if (ret)
return ret;
if (data && data->have_governor_per_policy)
dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
if (data) {
if (data->have_governor_per_policy)
dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
dt_cpufreq_driver.resume = data->resume;
if (data->suspend)
dt_cpufreq_driver.suspend = data->suspend;
}
ret = cpufreq_register_driver(&dt_cpufreq_driver);
if (ret)

View File

@ -12,8 +12,13 @@
#include <linux/types.h>
struct cpufreq_policy;
struct cpufreq_dt_platform_data {
bool have_governor_per_policy;
int (*suspend)(struct cpufreq_policy *policy);
int (*resume)(struct cpufreq_policy *policy);
};
#endif /* __CPUFREQ_DT_H__ */

View File

@ -89,16 +89,7 @@ static void cpufreq_governor_limits(struct cpufreq_policy *policy);
* The mutex locks both lists.
*/
static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list);
static struct srcu_notifier_head cpufreq_transition_notifier_list;
static bool init_cpufreq_transition_notifier_list_called;
static int __init init_cpufreq_transition_notifier_list(void)
{
srcu_init_notifier_head(&cpufreq_transition_notifier_list);
init_cpufreq_transition_notifier_list_called = true;
return 0;
}
pure_initcall(init_cpufreq_transition_notifier_list);
SRCU_NOTIFIER_HEAD_STATIC(cpufreq_transition_notifier_list);
static int off __read_mostly;
static int cpufreq_disabled(void)
@ -300,8 +291,19 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
#endif
}
static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, unsigned int state)
/**
* cpufreq_notify_transition - Notify frequency transition and adjust_jiffies.
* @policy: cpufreq policy to enable fast frequency switching for.
* @freqs: contain details of the frequency update.
* @state: set to CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
*
* This function calls the transition notifiers and the "adjust_jiffies"
* function. It is called twice on all CPU frequency changes that have
* external effects.
*/
static void cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs,
unsigned int state)
{
BUG_ON(irqs_disabled());
@ -313,52 +315,42 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
state, freqs->new);
switch (state) {
case CPUFREQ_PRECHANGE:
/* detect if the driver reported a value as "old frequency"
/*
* Detect if the driver reported a value as "old frequency"
* which is not equal to what the cpufreq core thinks is
* "old frequency".
*/
if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
if ((policy) && (policy->cpu == freqs->cpu) &&
(policy->cur) && (policy->cur != freqs->old)) {
if (policy->cur && (policy->cur != freqs->old)) {
pr_debug("Warning: CPU frequency is %u, cpufreq assumed %u kHz\n",
freqs->old, policy->cur);
freqs->old = policy->cur;
}
}
srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
CPUFREQ_PRECHANGE, freqs);
for_each_cpu(freqs->cpu, policy->cpus) {
srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
CPUFREQ_PRECHANGE, freqs);
}
adjust_jiffies(CPUFREQ_PRECHANGE, freqs);
break;
case CPUFREQ_POSTCHANGE:
adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
pr_debug("FREQ: %lu - CPU: %lu\n",
(unsigned long)freqs->new, (unsigned long)freqs->cpu);
trace_cpu_frequency(freqs->new, freqs->cpu);
cpufreq_stats_record_transition(policy, freqs->new);
srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
CPUFREQ_POSTCHANGE, freqs);
if (likely(policy) && likely(policy->cpu == freqs->cpu))
policy->cur = freqs->new;
break;
}
}
pr_debug("FREQ: %u - CPUs: %*pbl\n", freqs->new,
cpumask_pr_args(policy->cpus));
/**
* cpufreq_notify_transition - call notifier chain and adjust_jiffies
* on frequency transition.
*
* This function calls the transition notifiers and the "adjust_jiffies"
* function. It is called twice on all CPU frequency changes that have
* external effects.
*/
static void cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, unsigned int state)
{
for_each_cpu(freqs->cpu, policy->cpus)
__cpufreq_notify_transition(policy, freqs, state);
for_each_cpu(freqs->cpu, policy->cpus) {
trace_cpu_frequency(freqs->new, freqs->cpu);
srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
CPUFREQ_POSTCHANGE, freqs);
}
cpufreq_stats_record_transition(policy, freqs->new);
policy->cur = freqs->new;
}
}
/* Do post notifications when there are chances that transition has failed */
@ -696,6 +688,8 @@ static ssize_t store_##file_name \
struct cpufreq_policy new_policy; \
\
memcpy(&new_policy, policy, sizeof(*policy)); \
new_policy.min = policy->user_policy.min; \
new_policy.max = policy->user_policy.max; \
\
ret = sscanf(buf, "%u", &new_policy.object); \
if (ret != 1) \
@ -1764,8 +1758,6 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
if (cpufreq_disabled())
return -EINVAL;
WARN_ON(!init_cpufreq_transition_notifier_list_called);
switch (list) {
case CPUFREQ_TRANSITION_NOTIFIER:
mutex_lock(&cpufreq_fast_switch_lock);

View File

@ -1939,13 +1939,51 @@ static int intel_cpufreq_verify_policy(struct cpufreq_policy *policy)
return 0;
}
/* Use of trace in passive mode:
*
* In passive mode the trace core_busy field (also known as the
* performance field, and lablelled as such on the graphs; also known as
* core_avg_perf) is not needed and so is re-assigned to indicate if the
* driver call was via the normal or fast switch path. Various graphs
* output from the intel_pstate_tracer.py utility that include core_busy
* (or performance or core_avg_perf) have a fixed y-axis from 0 to 100%,
* so we use 10 to indicate the the normal path through the driver, and
* 90 to indicate the fast switch path through the driver.
* The scaled_busy field is not used, and is set to 0.
*/
#define INTEL_PSTATE_TRACE_TARGET 10
#define INTEL_PSTATE_TRACE_FAST_SWITCH 90
static void intel_cpufreq_trace(struct cpudata *cpu, unsigned int trace_type, int old_pstate)
{
struct sample *sample;
if (!trace_pstate_sample_enabled())
return;
if (!intel_pstate_sample(cpu, ktime_get()))
return;
sample = &cpu->sample;
trace_pstate_sample(trace_type,
0,
old_pstate,
cpu->pstate.current_pstate,
sample->mperf,
sample->aperf,
sample->tsc,
get_avg_frequency(cpu),
fp_toint(cpu->iowait_boost * 100));
}
static int intel_cpufreq_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct cpudata *cpu = all_cpu_data[policy->cpu];
struct cpufreq_freqs freqs;
int target_pstate;
int target_pstate, old_pstate;
update_turbo_state();
@ -1965,12 +2003,14 @@ static int intel_cpufreq_target(struct cpufreq_policy *policy,
break;
}
target_pstate = intel_pstate_prepare_request(cpu, target_pstate);
old_pstate = cpu->pstate.current_pstate;
if (target_pstate != cpu->pstate.current_pstate) {
cpu->pstate.current_pstate = target_pstate;
wrmsrl_on_cpu(policy->cpu, MSR_IA32_PERF_CTL,
pstate_funcs.get_val(cpu, target_pstate));
}
freqs.new = target_pstate * cpu->pstate.scaling;
intel_cpufreq_trace(cpu, INTEL_PSTATE_TRACE_TARGET, old_pstate);
cpufreq_freq_transition_end(policy, &freqs, false);
return 0;
@ -1980,13 +2020,15 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy,
unsigned int target_freq)
{
struct cpudata *cpu = all_cpu_data[policy->cpu];
int target_pstate;
int target_pstate, old_pstate;
update_turbo_state();
target_pstate = DIV_ROUND_UP(target_freq, cpu->pstate.scaling);
target_pstate = intel_pstate_prepare_request(cpu, target_pstate);
old_pstate = cpu->pstate.current_pstate;
intel_pstate_update_pstate(cpu, target_pstate);
intel_cpufreq_trace(cpu, INTEL_PSTATE_TRACE_FAST_SWITCH, old_pstate);
return target_pstate * cpu->pstate.scaling;
}

View File

@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
/*
* In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
* the CPU frequency subset and voltage value of each OPP varies
* based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
* defines the voltage and frequency value based on the msm-id in SMEM
* and speedbin blown in the efuse combination.
* The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
* to provide the OPP framework with required information.
* This is used to determine the voltage and frequency value for each OPP of
* operating-points-v2 table when it is parsed by the OPP framework.
*/
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smem.h>
#define MSM_ID_SMEM 137
enum _msm_id {
MSM8996V3 = 0xF6ul,
APQ8096V3 = 0x123ul,
MSM8996SG = 0x131ul,
APQ8096SG = 0x138ul,
};
enum _msm8996_version {
MSM8996_V3,
MSM8996_SG,
NUM_OF_MSM8996_VERSIONS,
};
static enum _msm8996_version __init qcom_cpufreq_kryo_get_msm_id(void)
{
size_t len;
u32 *msm_id;
enum _msm8996_version version;
msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
if (IS_ERR(msm_id))
return NUM_OF_MSM8996_VERSIONS;
/* The first 4 bytes are format, next to them is the actual msm-id */
msm_id++;
switch ((enum _msm_id)*msm_id) {
case MSM8996V3:
case APQ8096V3:
version = MSM8996_V3;
break;
case MSM8996SG:
case APQ8096SG:
version = MSM8996_SG;
break;
default:
version = NUM_OF_MSM8996_VERSIONS;
}
return version;
}
static int qcom_cpufreq_kryo_probe(struct platform_device *pdev)
{
struct opp_table *opp_tables[NR_CPUS] = {0};
struct platform_device *cpufreq_dt_pdev;
enum _msm8996_version msm8996_version;
struct nvmem_cell *speedbin_nvmem;
struct device_node *np;
struct device *cpu_dev;
unsigned cpu;
u8 *speedbin;
u32 versions;
size_t len;
int ret;
cpu_dev = get_cpu_device(0);
if (NULL == cpu_dev)
ret = -ENODEV;
msm8996_version = qcom_cpufreq_kryo_get_msm_id();
if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
dev_err(cpu_dev, "Not Snapdragon 820/821!");
return -ENODEV;
}
np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
if (IS_ERR(np))
return PTR_ERR(np);
ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
if (!ret) {
of_node_put(np);
return -ENOENT;
}
speedbin_nvmem = of_nvmem_cell_get(np, NULL);
of_node_put(np);
if (IS_ERR(speedbin_nvmem)) {
dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
PTR_ERR(speedbin_nvmem));
return PTR_ERR(speedbin_nvmem);
}
speedbin = nvmem_cell_read(speedbin_nvmem, &len);
nvmem_cell_put(speedbin_nvmem);
switch (msm8996_version) {
case MSM8996_V3:
versions = 1 << (unsigned int)(*speedbin);
break;
case MSM8996_SG:
versions = 1 << ((unsigned int)(*speedbin) + 4);
break;
default:
BUG();
break;
}
for_each_possible_cpu(cpu) {
cpu_dev = get_cpu_device(cpu);
if (NULL == cpu_dev) {
ret = -ENODEV;
goto free_opp;
}
opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
&versions, 1);
if (IS_ERR(opp_tables[cpu])) {
ret = PTR_ERR(opp_tables[cpu]);
dev_err(cpu_dev, "Failed to set supported hardware\n");
goto free_opp;
}
}
cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
NULL, 0);
if (!IS_ERR(cpufreq_dt_pdev))
return 0;
ret = PTR_ERR(cpufreq_dt_pdev);
dev_err(cpu_dev, "Failed to register platform device\n");
free_opp:
for_each_possible_cpu(cpu) {
if (IS_ERR_OR_NULL(opp_tables[cpu]))
break;
dev_pm_opp_put_supported_hw(opp_tables[cpu]);
}
return ret;
}
static struct platform_driver qcom_cpufreq_kryo_driver = {
.probe = qcom_cpufreq_kryo_probe,
.driver = {
.name = "qcom-cpufreq-kryo",
},
};
static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = {
{ .compatible = "qcom,apq8096", },
{ .compatible = "qcom,msm8996", },
};
/*
* Since the driver depends on smem and nvmem drivers, which may
* return EPROBE_DEFER, all the real activity is done in the probe,
* which may be defered as well. The init here is only registering
* the driver and the platform device.
*/
static int __init qcom_cpufreq_kryo_init(void)
{
struct device_node *np = of_find_node_by_path("/");
const struct of_device_id *match;
int ret;
if (!np)
return -ENODEV;
match = of_match_node(qcom_cpufreq_kryo_match_list, np);
of_node_put(np);
if (!match)
return -ENODEV;
ret = platform_driver_register(&qcom_cpufreq_kryo_driver);
if (unlikely(ret < 0))
return ret;
ret = PTR_ERR_OR_ZERO(platform_device_register_simple(
"qcom-cpufreq-kryo", -1, NULL, 0));
if (0 == ret)
return 0;
platform_driver_unregister(&qcom_cpufreq_kryo_driver);
return ret;
}
module_init(qcom_cpufreq_kryo_init);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
MODULE_LICENSE("GPL v2");

View File

@ -143,7 +143,7 @@ static void s3c2440_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
{
unsigned long clkdiv, camdiv;
s3c_freq_dbg("%s: divsiors: h=%d, p=%d\n", __func__,
s3c_freq_dbg("%s: divisors: h=%d, p=%d\n", __func__,
cfg->divs.h_divisor, cfg->divs.p_divisor);
clkdiv = __raw_readl(S3C2410_CLKDIVN);

View File

@ -252,7 +252,7 @@ EXPORT_SYMBOL_GPL(speedstep_get_frequency);
*********************************************************************/
/* Keep in sync with the x86_cpu_id tables in the different modules */
unsigned int speedstep_detect_processor(void)
enum speedstep_processor speedstep_detect_processor(void)
{
struct cpuinfo_x86 *c = &cpu_data(0);
u32 ebx, msr_lo, msr_hi;

View File

@ -16,16 +16,13 @@
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
static struct cpufreq_frequency_table freq_table[] = {
{ .frequency = 216000 },
@ -39,25 +36,27 @@ static struct cpufreq_frequency_table freq_table[] = {
{ .frequency = CPUFREQ_TABLE_END },
};
#define NUM_CPUS 2
static struct clk *cpu_clk;
static struct clk *pll_x_clk;
static struct clk *pll_p_clk;
static struct clk *emc_clk;
static bool pll_x_prepared;
struct tegra20_cpufreq {
struct device *dev;
struct cpufreq_driver driver;
struct clk *cpu_clk;
struct clk *pll_x_clk;
struct clk *pll_p_clk;
bool pll_x_prepared;
};
static unsigned int tegra_get_intermediate(struct cpufreq_policy *policy,
unsigned int index)
{
unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000;
struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
unsigned int ifreq = clk_get_rate(cpufreq->pll_p_clk) / 1000;
/*
* Don't switch to intermediate freq if:
* - we are already at it, i.e. policy->cur == ifreq
* - index corresponds to ifreq
*/
if ((freq_table[index].frequency == ifreq) || (policy->cur == ifreq))
if (freq_table[index].frequency == ifreq || policy->cur == ifreq)
return 0;
return ifreq;
@ -66,6 +65,7 @@ static unsigned int tegra_get_intermediate(struct cpufreq_policy *policy,
static int tegra_target_intermediate(struct cpufreq_policy *policy,
unsigned int index)
{
struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
int ret;
/*
@ -78,47 +78,37 @@ static int tegra_target_intermediate(struct cpufreq_policy *policy,
* Also, we wouldn't be using pll_x anymore and must not take extra
* reference to it, as it can be disabled now to save some power.
*/
clk_prepare_enable(pll_x_clk);
clk_prepare_enable(cpufreq->pll_x_clk);
ret = clk_set_parent(cpu_clk, pll_p_clk);
ret = clk_set_parent(cpufreq->cpu_clk, cpufreq->pll_p_clk);
if (ret)
clk_disable_unprepare(pll_x_clk);
clk_disable_unprepare(cpufreq->pll_x_clk);
else
pll_x_prepared = true;
cpufreq->pll_x_prepared = true;
return ret;
}
static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
{
struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
unsigned long rate = freq_table[index].frequency;
unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000;
int ret = 0;
/*
* Vote on memory bus frequency based on cpu frequency
* This sets the minimum frequency, display or avp may request higher
*/
if (rate >= 816000)
clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
else if (rate >= 456000)
clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
else
clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */
unsigned int ifreq = clk_get_rate(cpufreq->pll_p_clk) / 1000;
int ret;
/*
* target freq == pll_p, don't need to take extra reference to pll_x_clk
* as it isn't used anymore.
*/
if (rate == ifreq)
return clk_set_parent(cpu_clk, pll_p_clk);
return clk_set_parent(cpufreq->cpu_clk, cpufreq->pll_p_clk);
ret = clk_set_rate(pll_x_clk, rate * 1000);
ret = clk_set_rate(cpufreq->pll_x_clk, rate * 1000);
/* Restore to earlier frequency on error, i.e. pll_x */
if (ret)
pr_err("Failed to change pll_x to %lu\n", rate);
dev_err(cpufreq->dev, "Failed to change pll_x to %lu\n", rate);
ret = clk_set_parent(cpu_clk, pll_x_clk);
ret = clk_set_parent(cpufreq->cpu_clk, cpufreq->pll_x_clk);
/* This shouldn't fail while changing or restoring */
WARN_ON(ret);
@ -126,9 +116,9 @@ static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
* Drop count to pll_x clock only if we switched to intermediate freq
* earlier while transitioning to a target frequency.
*/
if (pll_x_prepared) {
clk_disable_unprepare(pll_x_clk);
pll_x_prepared = false;
if (cpufreq->pll_x_prepared) {
clk_disable_unprepare(cpufreq->pll_x_clk);
cpufreq->pll_x_prepared = false;
}
return ret;
@ -136,81 +126,111 @@ static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
static int tegra_cpu_init(struct cpufreq_policy *policy)
{
struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
int ret;
if (policy->cpu >= NUM_CPUS)
return -EINVAL;
clk_prepare_enable(emc_clk);
clk_prepare_enable(cpu_clk);
clk_prepare_enable(cpufreq->cpu_clk);
/* FIXME: what's the actual transition time? */
ret = cpufreq_generic_init(policy, freq_table, 300 * 1000);
if (ret) {
clk_disable_unprepare(cpu_clk);
clk_disable_unprepare(emc_clk);
clk_disable_unprepare(cpufreq->cpu_clk);
return ret;
}
policy->clk = cpu_clk;
policy->clk = cpufreq->cpu_clk;
policy->suspend_freq = freq_table[0].frequency;
return 0;
}
static int tegra_cpu_exit(struct cpufreq_policy *policy)
{
clk_disable_unprepare(cpu_clk);
clk_disable_unprepare(emc_clk);
struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
clk_disable_unprepare(cpufreq->cpu_clk);
return 0;
}
static struct cpufreq_driver tegra_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.get_intermediate = tegra_get_intermediate,
.target_intermediate = tegra_target_intermediate,
.target_index = tegra_target,
.get = cpufreq_generic_get,
.init = tegra_cpu_init,
.exit = tegra_cpu_exit,
.name = "tegra",
.attr = cpufreq_generic_attr,
.suspend = cpufreq_generic_suspend,
};
static int __init tegra_cpufreq_init(void)
static int tegra20_cpufreq_probe(struct platform_device *pdev)
{
cpu_clk = clk_get_sys(NULL, "cclk");
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
struct tegra20_cpufreq *cpufreq;
int err;
pll_x_clk = clk_get_sys(NULL, "pll_x");
if (IS_ERR(pll_x_clk))
return PTR_ERR(pll_x_clk);
cpufreq = devm_kzalloc(&pdev->dev, sizeof(*cpufreq), GFP_KERNEL);
if (!cpufreq)
return -ENOMEM;
pll_p_clk = clk_get_sys(NULL, "pll_p");
if (IS_ERR(pll_p_clk))
return PTR_ERR(pll_p_clk);
cpufreq->cpu_clk = clk_get_sys(NULL, "cclk");
if (IS_ERR(cpufreq->cpu_clk))
return PTR_ERR(cpufreq->cpu_clk);
emc_clk = clk_get_sys("cpu", "emc");
if (IS_ERR(emc_clk)) {
clk_put(cpu_clk);
return PTR_ERR(emc_clk);
cpufreq->pll_x_clk = clk_get_sys(NULL, "pll_x");
if (IS_ERR(cpufreq->pll_x_clk)) {
err = PTR_ERR(cpufreq->pll_x_clk);
goto put_cpu;
}
return cpufreq_register_driver(&tegra_cpufreq_driver);
cpufreq->pll_p_clk = clk_get_sys(NULL, "pll_p");
if (IS_ERR(cpufreq->pll_p_clk)) {
err = PTR_ERR(cpufreq->pll_p_clk);
goto put_pll_x;
}
cpufreq->dev = &pdev->dev;
cpufreq->driver.get = cpufreq_generic_get;
cpufreq->driver.attr = cpufreq_generic_attr;
cpufreq->driver.init = tegra_cpu_init;
cpufreq->driver.exit = tegra_cpu_exit;
cpufreq->driver.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK;
cpufreq->driver.verify = cpufreq_generic_frequency_table_verify;
cpufreq->driver.suspend = cpufreq_generic_suspend;
cpufreq->driver.driver_data = cpufreq;
cpufreq->driver.target_index = tegra_target;
cpufreq->driver.get_intermediate = tegra_get_intermediate;
cpufreq->driver.target_intermediate = tegra_target_intermediate;
snprintf(cpufreq->driver.name, CPUFREQ_NAME_LEN, "tegra");
err = cpufreq_register_driver(&cpufreq->driver);
if (err)
goto put_pll_p;
platform_set_drvdata(pdev, cpufreq);
return 0;
put_pll_p:
clk_put(cpufreq->pll_p_clk);
put_pll_x:
clk_put(cpufreq->pll_x_clk);
put_cpu:
clk_put(cpufreq->cpu_clk);
return err;
}
static void __exit tegra_cpufreq_exit(void)
static int tegra20_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&tegra_cpufreq_driver);
clk_put(emc_clk);
clk_put(cpu_clk);
struct tegra20_cpufreq *cpufreq = platform_get_drvdata(pdev);
cpufreq_unregister_driver(&cpufreq->driver);
clk_put(cpufreq->pll_p_clk);
clk_put(cpufreq->pll_x_clk);
clk_put(cpufreq->cpu_clk);
return 0;
}
static struct platform_driver tegra20_cpufreq_driver = {
.probe = tegra20_cpufreq_probe,
.remove = tegra20_cpufreq_remove,
.driver = {
.name = "tegra20-cpufreq",
},
};
module_platform_driver(tegra20_cpufreq_driver);
MODULE_ALIAS("platform:tegra20-cpufreq");
MODULE_AUTHOR("Colin Cross <ccross@android.com>");
MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2");
MODULE_DESCRIPTION("NVIDIA Tegra20 cpufreq driver");
MODULE_LICENSE("GPL");
module_init(tegra_cpufreq_init);
module_exit(tegra_cpufreq_exit);

View File

@ -43,9 +43,7 @@
* in srcu_notifier_call_chain(): no cache bounces and no memory barriers.
* As compensation, srcu_notifier_chain_unregister() is rather expensive.
* SRCU notifier chains should be used when the chain will be called very
* often but notifier_blocks will seldom be removed. Also, SRCU notifier
* chains are slightly more difficult to use because they require special
* runtime initialization.
* often but notifier_blocks will seldom be removed.
*/
struct notifier_block;
@ -91,7 +89,7 @@ struct srcu_notifier_head {
(name)->head = NULL; \
} while (0)
/* srcu_notifier_heads must be initialized and cleaned up dynamically */
/* srcu_notifier_heads must be cleaned up dynamically */
extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
#define srcu_cleanup_notifier_head(name) \
cleanup_srcu_struct(&(name)->srcu);
@ -104,7 +102,13 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
.head = NULL }
#define RAW_NOTIFIER_INIT(name) { \
.head = NULL }
/* srcu_notifier_heads cannot be initialized statically */
#define SRCU_NOTIFIER_INIT(name, pcpu) \
{ \
.mutex = __MUTEX_INITIALIZER(name.mutex), \
.head = NULL, \
.srcu = __SRCU_STRUCT_INIT(name.srcu, pcpu), \
}
#define ATOMIC_NOTIFIER_HEAD(name) \
struct atomic_notifier_head name = \
@ -116,6 +120,26 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
struct raw_notifier_head name = \
RAW_NOTIFIER_INIT(name)
#ifdef CONFIG_TREE_SRCU
#define _SRCU_NOTIFIER_HEAD(name, mod) \
static DEFINE_PER_CPU(struct srcu_data, \
name##_head_srcu_data); \
mod struct srcu_notifier_head name = \
SRCU_NOTIFIER_INIT(name, name##_head_srcu_data)
#else
#define _SRCU_NOTIFIER_HEAD(name, mod) \
mod struct srcu_notifier_head name = \
SRCU_NOTIFIER_INIT(name, name)
#endif
#define SRCU_NOTIFIER_HEAD(name) \
_SRCU_NOTIFIER_HEAD(name, /* not static */)
#define SRCU_NOTIFIER_HEAD_STATIC(name) \
_SRCU_NOTIFIER_HEAD(name, static)
#ifdef __KERNEL__
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,

View File

@ -43,7 +43,7 @@ struct srcu_struct {
void srcu_drive_gp(struct work_struct *wp);
#define __SRCU_STRUCT_INIT(name) \
#define __SRCU_STRUCT_INIT(name, __ignored) \
{ \
.srcu_wq = __SWAIT_QUEUE_HEAD_INITIALIZER(name.srcu_wq), \
.srcu_cb_tail = &name.srcu_cb_head, \
@ -56,9 +56,9 @@ void srcu_drive_gp(struct work_struct *wp);
* Tree SRCU, which needs some per-CPU data.
*/
#define DEFINE_SRCU(name) \
struct srcu_struct name = __SRCU_STRUCT_INIT(name)
struct srcu_struct name = __SRCU_STRUCT_INIT(name, name)
#define DEFINE_STATIC_SRCU(name) \
static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name)
void synchronize_srcu(struct srcu_struct *sp);

View File

@ -104,9 +104,9 @@ struct srcu_struct {
#define SRCU_STATE_SCAN1 1
#define SRCU_STATE_SCAN2 2
#define __SRCU_STRUCT_INIT(name) \
#define __SRCU_STRUCT_INIT(name, pcpu_name) \
{ \
.sda = &name##_srcu_data, \
.sda = &pcpu_name, \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \
.srcu_gp_seq_needed = 0 - 1, \
__SRCU_DEP_MAP_INIT(name) \
@ -133,7 +133,7 @@ struct srcu_struct {
*/
#define __DEFINE_SRCU(name, is_static) \
static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data);\
is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name##_srcu_data)
#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */)
#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static)