[PATCH] cpufreq: sirf : Add cpufreq for SiRFprimaII and SiRFatlasVI

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Rongjun Ying <Rongjun.Ying@xxxxxxx>

This patch adds support cpufreq for CSR SiRFprimaII and SiRFatlasVI unicore
ARM Cortex-A9 SoCs.

Signed-off-by: Rongjun Ying <Rongjun.Ying@xxxxxxx>
Signed-off-by: Barry Song <Baohua.Song@xxxxxxx>
---
 arch/arm/boot/dts/atlas6.dtsi     |   8 ++
 arch/arm/boot/dts/prima2.dtsi     |   8 ++
 arch/arm/configs/prima2_defconfig |   1 +
 arch/arm/mach-prima2/Kconfig      |   1 +
 drivers/cpufreq/Kconfig.arm       |  11 +++
 drivers/cpufreq/Makefile          |   1 +
 drivers/cpufreq/sirf-cpufreq.c    | 197 ++++++++++++++++++++++++++++++++++++++
 7 files changed, 227 insertions(+)
 create mode 100644 drivers/cpufreq/sirf-cpufreq.c

diff --git a/arch/arm/boot/dts/atlas6.dtsi b/arch/arm/boot/dts/atlas6.dtsi
index 8678e0c..375f865 100644
--- a/arch/arm/boot/dts/atlas6.dtsi
+++ b/arch/arm/boot/dts/atlas6.dtsi
@@ -27,6 +27,14 @@
 			timebase-frequency = <0>;
 			bus-frequency = <0>;
 			clock-frequency = <0>;
+			operating-points = <
+				/* kHz    uV */
+				200000  1025000
+				400000  1025000
+				600000  1050000
+				800000  1100000
+			>;
+			cpufreq_transition_latency = <150000>;
 		};
 	};
 
diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi
index bbeb623..469a465 100644
--- a/arch/arm/boot/dts/prima2.dtsi
+++ b/arch/arm/boot/dts/prima2.dtsi
@@ -29,6 +29,14 @@
 			timebase-frequency = <0>;
 			bus-frequency = <0>;
 			clock-frequency = <0>;
+			operating-points = <
+				/* kHz    uV */
+				200000  1025000
+				400000  1025000
+				664000  1050000
+				800000  1100000
+			>;
+			cpufreq_transition_latency = <150000>;
 		};
 	};
 
diff --git a/arch/arm/configs/prima2_defconfig b/arch/arm/configs/prima2_defconfig
index 002a1ce..513d75e 100644
--- a/arch/arm/configs/prima2_defconfig
+++ b/arch/arm/configs/prima2_defconfig
@@ -17,6 +17,7 @@ CONFIG_SCHED_MC=y
 CONFIG_PREEMPT=y
 CONFIG_AEABI=y
 CONFIG_KEXEC=y
+CONFIG_CPU_FREQ=y
 CONFIG_BINFMT_MISC=y
 CONFIG_PM_RUNTIME=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
diff --git a/arch/arm/mach-prima2/Kconfig b/arch/arm/mach-prima2/Kconfig
index 6988b11..7ddd607 100644
--- a/arch/arm/mach-prima2/Kconfig
+++ b/arch/arm/mach-prima2/Kconfig
@@ -1,5 +1,6 @@
 config ARCH_SIRF
 	bool "CSR SiRF" if ARCH_MULTI_V7
+	select ARCH_HAS_CPUFREQ
 	select ARCH_REQUIRE_GPIOLIB
 	select GENERIC_CLOCKEVENTS
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 0fa204b..4cfbb18 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -220,6 +220,17 @@ config ARM_SA1100_CPUFREQ
 config ARM_SA1110_CPUFREQ
 	bool
 
+config ARM_SIRF_CPUFREQ
+	bool "CSR SiRFprimaII and SiRFatlasVI"
+	depends on ARCH_SIRF
+	select CPU_FREQ_TABLE
+	default y
+	help
+	  This adds the CPUFreq driver for CSR SiRFprimaII and
+	  SiRFatlasVI SoCs.
+
+	  If in doubt, say N.
+
 config ARM_SPEAR_CPUFREQ
 	bool "SPEAr CPUFreq support"
 	depends on PLAT_SPEAR
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index ad5866c..cbfad94 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_ARM_S3C64XX_CPUFREQ)	+= s3c64xx-cpufreq.o
 obj-$(CONFIG_ARM_S5PV210_CPUFREQ)	+= s5pv210-cpufreq.o
 obj-$(CONFIG_ARM_SA1100_CPUFREQ)	+= sa1100-cpufreq.o
 obj-$(CONFIG_ARM_SA1110_CPUFREQ)	+= sa1110-cpufreq.o
+obj-$(CONFIG_ARM_SIRF_CPUFREQ)		+= sirf-cpufreq.o
 obj-$(CONFIG_ARM_SPEAR_CPUFREQ)		+= spear-cpufreq.o
 obj-$(CONFIG_ARM_TEGRA_CPUFREQ)		+= tegra-cpufreq.o
 
diff --git a/drivers/cpufreq/sirf-cpufreq.c b/drivers/cpufreq/sirf-cpufreq.c
new file mode 100644
index 0000000..e851569
--- /dev/null
+++ b/drivers/cpufreq/sirf-cpufreq.c
@@ -0,0 +1,197 @@
+
+/*
+ * CPU frequency scaling support for CSR SiRFSoC
+ *
+ * Copyright (c) 2011-2013 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/cpufreq.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/opp.h>
+
+#define SIRFSOC_MAX_VOLTAGE	1200000
+
+static struct {
+	struct clk			*cpu_clk;
+	struct device			*cpu_dev;
+	struct cpufreq_freqs		freqs;
+	struct cpufreq_frequency_table	*freq_tbl;
+	unsigned int			transition_latency;
+	struct regulator		*regulator;
+} sirf_cpufreq;
+
+static void update_voltage(int min_uV, int max_uV)
+{
+	if (sirf_cpufreq.regulator == NULL)
+		return;
+
+	regulator_set_voltage(sirf_cpufreq.regulator, min_uV, max_uV);
+}
+
+static int sirf_verify_speed(struct cpufreq_policy *policy)
+{
+	if (sirf_cpufreq.freq_tbl)
+		return cpufreq_frequency_table_verify(policy,
+			sirf_cpufreq.freq_tbl);
+	return 0;
+}
+
+static unsigned int sirf_getspeed(unsigned int cpu)
+{
+	return clk_get_rate(sirf_cpufreq.cpu_clk) / 1000;
+}
+
+static int sirf_target(struct cpufreq_policy *policy,
+			  unsigned int target_freq,
+			  unsigned int relation)
+{
+	unsigned long freq, volt = 0;
+	struct opp *opp;
+	unsigned int index, old_index;
+
+	if (!sirf_cpufreq.freq_tbl)
+		return -EINVAL;
+
+	sirf_cpufreq.freqs.old = sirf_getspeed(policy->cpu);
+
+	if (cpufreq_frequency_table_target(policy, sirf_cpufreq.freq_tbl,
+					sirf_cpufreq.freqs.old,
+					relation, &old_index))
+		return -EINVAL;
+
+	if (cpufreq_frequency_table_target(policy, sirf_cpufreq.freq_tbl,
+					   target_freq, relation, &index))
+		return -EINVAL;
+
+	sirf_cpufreq.freqs.new = sirf_cpufreq.freq_tbl[index].frequency;
+	sirf_cpufreq.freqs.cpu = policy->cpu;
+
+	if (sirf_cpufreq.freqs.new == sirf_cpufreq.freqs.old)
+		return 0;
+
+	cpufreq_notify_transition(policy, &sirf_cpufreq.freqs,
+			CPUFREQ_PRECHANGE);
+
+	freq = sirf_cpufreq.freqs.new * 1000;
+	rcu_read_lock();
+	opp = opp_find_freq_ceil(sirf_cpufreq.cpu_dev, &freq);
+	if (IS_ERR(opp)) {
+		rcu_read_unlock();
+		dev_err(sirf_cpufreq.cpu_dev, "%s: unable to find MPU OPP for %d\n",
+				__func__, sirf_cpufreq.freqs.new);
+		return -EINVAL;
+	}
+
+	volt = opp_get_voltage(opp);
+	rcu_read_unlock();
+	/* control regulator: voltage up */
+	if ((sirf_cpufreq.freqs.new > sirf_cpufreq.freqs.old) &&
+		volt)
+		update_voltage(volt, SIRFSOC_MAX_VOLTAGE);
+	clk_set_rate(sirf_cpufreq.cpu_clk, sirf_cpufreq.freqs.new * 1000);
+
+	/* control regulator: voltage down */
+	if ((sirf_cpufreq.freqs.new < sirf_cpufreq.freqs.old) &&
+		volt)
+		update_voltage(volt, SIRFSOC_MAX_VOLTAGE);
+
+	cpufreq_notify_transition(policy, &sirf_cpufreq.freqs,
+			CPUFREQ_POSTCHANGE);
+	return 0;
+}
+
+static int sirf_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+	if (!sirf_cpufreq.freq_tbl)
+		return -EINVAL;
+	policy->cur = policy->min = policy->max = sirf_getspeed(policy->cpu);
+
+	cpufreq_frequency_table_get_attr(sirf_cpufreq.freq_tbl, policy->cpu);
+
+	/*
+	 * set the actual transition latency for
+	 * worstcase with +20% margin.
+	 */
+	policy->cpuinfo.transition_latency = sirf_cpufreq.transition_latency;
+	cpumask_setall(policy->cpus);
+	return cpufreq_frequency_table_cpuinfo(policy, sirf_cpufreq.freq_tbl);
+}
+
+static struct cpufreq_driver sirf_driver = {
+	.flags		= CPUFREQ_STICKY,
+	.verify		= sirf_verify_speed,
+	.target		= sirf_target,
+	.get		= sirf_getspeed,
+	.init		= sirf_cpufreq_cpu_init,
+	.name		= "sirf_cpufreq",
+};
+
+static int __init sirf_cpufreq_init(void)
+{
+	struct device_node *np;
+	int ret;
+
+	sirf_cpufreq.cpu_dev = get_cpu_device(0);
+
+	np = of_find_node_by_path("/cpus/cpu@0");
+	if (!np) {
+		pr_err("No cpu node found");
+		return -ENODEV;
+	}
+
+	sirf_cpufreq.cpu_dev->of_node = np;
+
+	if (of_property_read_u32(np, "cpufreq_transition_latency",
+				&sirf_cpufreq.transition_latency))
+		sirf_cpufreq.transition_latency = CPUFREQ_ETERNAL;
+
+	ret = of_init_opp_table(sirf_cpufreq.cpu_dev);
+	if (ret) {
+		pr_err("Failed init opp table.\n");
+		goto out_put_node;
+	}
+
+	ret = opp_init_cpufreq_table(sirf_cpufreq.cpu_dev,
+		&sirf_cpufreq.freq_tbl);
+	if (ret) {
+		pr_err("Failed init opp cpufreq table.\n");
+		goto out_put_node;
+	}
+
+	sirf_cpufreq.cpu_clk = clk_get_sys("cpu", NULL);
+	if (IS_ERR(sirf_cpufreq.cpu_clk)) {
+		pr_err("Get cpu clock failed.\n");
+		ret = PTR_ERR(sirf_cpufreq.cpu_clk);
+		goto out_free_opp;
+	}
+
+	sirf_cpufreq.regulator = regulator_get(NULL, "vcore");
+	if (IS_ERR(sirf_cpufreq.regulator))
+		sirf_cpufreq.regulator = NULL;
+
+	ret =  cpufreq_register_driver(&sirf_driver);
+	if (!ret)
+		goto out_free_opp;
+
+	pr_err("failed register driver: %d\n", ret);
+	clk_put(sirf_cpufreq.cpu_clk);
+
+out_free_opp:
+	opp_free_cpufreq_table(sirf_cpufreq.cpu_dev, &sirf_cpufreq.freq_tbl);
+out_put_node:
+	of_node_put(np);
+	return ret;
+}
+late_initcall(sirf_cpufreq_init);
+
+MODULE_DESCRIPTION("SiRF SoC cpufreq driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@xxxxxxx>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.3

--
To unsubscribe from this list: send the line "unsubscribe cpufreq" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Devel]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Forum]     [Linux SCSI]

  Powered by Linux