[PATCH] cpufreq: Add Broadcom BCM2835 CPU frequency control driver
Lubomir Rintel
lkundrak at v3.sk
Sun Oct 11 12:46:55 PDT 2015
This adds a device tree binding for Broadcom BCM2834 CPU frequency
control driven via Raspberry Pi VideoCore 4 firmware interface.
Based on a driver by Dom Cobley.
Signed-off-by: Dom Cobley <popcornmix at gmail.com>
Signed-off-by: Lubomir Rintel <lkundrak at v3.sk>
Cc: "Rafael J. Wysocki" <rjw at sisk.pl>
Cc: Stephen Warren <swarren at wwwdotorg.org>
Cc: Lee Jones <lee at kernel.org>
Cc: Eric Anholt <eric at anholt.net>
Cc: cpufreq at vger.kernel.org
Cc: linux-pm at vger.kernel.org
Cc: linux-rpi-kernel at lists.infradead.org
---
Depends on the RPi Firmware driver submitted on linux-rpi-kernel a while ago.
Can't see it in arm-soc or linux-next yet though (?). Available in branch
'rpi-firmware' of https://github.com/anholt/linux
arch/arm/boot/dts/bcm2835-rpi.dtsi | 5 +
arch/arm/mach-bcm/Kconfig | 1 +
drivers/cpufreq/Kconfig.arm | 9 ++
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/bcm2835-cpufreq.c | 199 +++++++++++++++++++++++++++++++++++++
5 files changed, 215 insertions(+)
create mode 100644 drivers/cpufreq/bcm2835-cpufreq.c
diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index ab5474e..0895307 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -20,6 +20,11 @@
compatible = "raspberrypi,bcm2835-firmware";
mboxes = <&mailbox>;
};
+
+ cpufreq {
+ compatible = "raspberrypi,bcm2835-cpufreq";
+ firmware = <&firmware>;
+ };
};
};
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index 1319c3c..686f950 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -110,6 +110,7 @@ comment "Other Architectures"
config ARCH_BCM2835
bool "Broadcom BCM2835 family" if ARCH_MULTI_V6
select ARCH_REQUIRE_GPIOLIB
+ select ARCH_HAS_CPUFREQ
select ARM_AMBA
select ARM_ERRATA_411920
select ARM_TIMER_SP804
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index cd0391e..2248373 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -227,3 +227,12 @@ config ARM_PXA2xx_CPUFREQ
This add the CPUFreq driver support for Intel PXA2xx SOCs.
If in doubt, say N.
+
+config ARM_BCM2835_CPUFREQ
+ tristate "Raspberry Pi BCM2835 CPUFreq support"
+ depends on RASPBERRYPI_FIRMWARE
+ help
+ This adds the BCM2835 CPU frequency control driver for Raspberry Pi
+ devices.
+
+ If in doubt, say N.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 4134038..4543df9 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o
+obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o
obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o
diff --git a/drivers/cpufreq/bcm2835-cpufreq.c b/drivers/cpufreq/bcm2835-cpufreq.c
new file mode 100644
index 0000000..0fc4cb5
--- /dev/null
+++ b/drivers/cpufreq/bcm2835-cpufreq.c
@@ -0,0 +1,199 @@
+/*
+ * This driver dynamically manages the CPU Frequency of the ARM processor.
+ * Messages are sent to Videocore either setting or requesting the
+ * frequency of the ARM in order to match an appropriate frequency to the
+ * current usage of the processor. The policy which selects the frequency
+ * to use is defined in the kernel .config file, but can be changed during
+ * runtime.
+ *
+ * Copyright 2011 Broadcom Corporation. All rights reserved.
+ * Copyright 2013,2015 Lubomir Rintel
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2, available at
+ * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a
+ * license other than the GPL, without Broadcom's express prior written
+ * consent.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/cpufreq.h>
+#include <linux/dma-mapping.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define VCMSG_SET_CLOCK_RATE 0x00038002
+#define VCMSG_GET_CLOCK_RATE 0x00030002
+#define VCMSG_GET_MIN_CLOCK 0x00030007
+#define VCMSG_GET_MAX_CLOCK 0x00030004
+#define VCMSG_ID_ARM_CLOCK 0x00000003 /* Clock/Voltage ID's */
+
+struct rpi_firmware *fw;
+
+/* tag part of the message */
+struct prop {
+ u32 dev_id; /* the ID of the clock/voltage to get or set */
+ u32 val; /* the value (e.g. rate (in Hz)) to set */
+} __packed;
+
+static u32 bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate)
+{
+ int ret = 0;
+ struct prop msg = {
+ .dev_id = VCMSG_ID_ARM_CLOCK,
+ .val = arm_rate * 1000,
+ };
+
+ /* send the message */
+ ret = rpi_firmware_property(fw, VCMSG_SET_CLOCK_RATE, &msg,
+ sizeof(msg));
+
+ /* check if it was all ok and return the rate in KHz */
+ if (ret) {
+ pr_err("bcm2835_cpufreq: could not set clock rate\n");
+ return 0;
+ }
+
+ return msg.val / 1000;
+}
+
+static u32 bcm2835_cpufreq_get_clock(int tag)
+{
+ int ret = 0;
+ struct prop msg = {
+ .dev_id = VCMSG_ID_ARM_CLOCK,
+ .val = 0,
+ };
+
+ /* send the message */
+ ret = rpi_firmware_property(fw, tag, &msg, sizeof(msg));
+
+ /* check if it was all ok and return the rate in KHz */
+ if (ret) {
+ pr_err("bcm2835_cpufreq: could not get clock %d\n", tag);
+ return 0;
+ }
+
+ return msg.val / 1000;
+}
+
+static int bcm2835_cpufreq_init(struct cpufreq_policy *policy)
+{
+ /* measured value of how long it takes to change frequency */
+ policy->cpuinfo.transition_latency = 355000; /* ns */
+
+ /* now find out what the maximum and minimum frequencies are */
+ policy->min = bcm2835_cpufreq_get_clock(VCMSG_GET_MIN_CLOCK);
+ policy->max = bcm2835_cpufreq_get_clock(VCMSG_GET_MAX_CLOCK);
+ policy->cur = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
+
+ policy->cpuinfo.min_freq = policy->min;
+ policy->cpuinfo.max_freq = policy->max;
+
+ return 0;
+}
+
+static int bcm2835_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int target = target_freq;
+ u32 cur;
+
+ /* if we are above min and using ondemand, then just use max */
+ if (strcmp("ondemand", policy->governor->name) == 0 &&
+ target > policy->min)
+ target = policy->max;
+
+ /* if the frequency is the same, just quit */
+ if (target == policy->cur)
+ return 0;
+
+ /* otherwise were good to set the clock frequency */
+ policy->cur = bcm2835_cpufreq_set_clock(policy->cur, target);
+
+ cur = bcm2835_cpufreq_set_clock(policy->cur, target);
+ if (!cur)
+ return -EINVAL;
+
+ policy->cur = cur;
+ return 0;
+}
+
+static unsigned int bcm2835_cpufreq_get(unsigned int cpu)
+{
+ return bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE);
+}
+
+static int bcm2835_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static struct cpufreq_driver bcm2835_cpufreq = {
+ .name = "bcm2835-cpufreq",
+ .init = bcm2835_cpufreq_init,
+ .verify = bcm2835_cpufreq_verify,
+ .target = bcm2835_cpufreq_target,
+ .get = bcm2835_cpufreq_get
+};
+
+static int bcm2835_cpufreq_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct device_node *fw_node;
+
+ fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
+ if (!fw_node) {
+ dev_err(dev, "no firmware node");
+ return -ENODEV;
+ }
+
+ fw = rpi_firmware_get(fw_node);
+ if (!fw)
+ return -EPROBE_DEFER;
+
+ ret = cpufreq_register_driver(&bcm2835_cpufreq);
+ if (ret) {
+ dev_err(dev, "Could not register cpufreq driver\n");
+ return ret;
+ }
+
+ dev_info(dev, "Broadcom BCM2835 CPU frequency control\n");
+ return 0;
+}
+
+static int bcm2835_cpufreq_remove(struct platform_device *dev)
+{
+ cpufreq_unregister_driver(&bcm2835_cpufreq);
+ return 0;
+}
+
+static const struct of_device_id bcm2835_cpufreq_of_match[] = {
+ { .compatible = "raspberrypi,bcm2835-cpufreq", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_cpufreq_of_match);
+
+static struct platform_driver bcm2835_cpufreq_driver = {
+ .probe = bcm2835_cpufreq_probe,
+ .remove = bcm2835_cpufreq_remove,
+ .driver = {
+ .name = "bcm2835-cpufreq",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_cpufreq_of_match,
+ },
+};
+module_platform_driver(bcm2835_cpufreq_driver);
+
+MODULE_AUTHOR("Dorian Peake and Dom Cobley and Lubomir Rintel");
+MODULE_DESCRIPTION("BCM2835 CPU frequency control driver");
+MODULE_LICENSE("GPL v2");
--
2.4.3
More information about the linux-rpi-kernel
mailing list