Blob Blame History Raw
From: Lubomir Rintel <lkundrak@v3.sk>
Date: Mon Sep 5 15:01:00 2016 +0200
Subject: [PATCH] cpufreq: Add Broadcom BCM2835 CPU frequency control driver
Patch-mainline: Not yet, posted on linux-rpi-kernel
References: bsc#989511

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@gmail.com>
Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Fabian Vogt <fvogt@suse.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Stephen Warren <swarren@wwwdotorg.org>
Cc: Lee Jones <lee@kernel.org>
Cc: Eric Anholt <eric@anholt.net>
Cc: cpufreq@vger.kernel.org
Cc: linux-pm@vger.kernel.org
Cc: linux-rpi-kernel@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

--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -31,6 +31,11 @@
 			compatible = "raspberrypi,bcm2835-thermal";
 			firmware = <&firmware>;
 		};
+
+		cpufreq {
+			compatible = "raspberrypi,bcm2835-cpufreq";
+			firmware = <&firmware>;
+		};
 	};
 };
 
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -124,6 +124,7 @@ comment "Other Architectures"
 config ARCH_BCM2835
 	bool "Broadcom BCM2835 family" if ARCH_MULTI_V6 || ARCH_MULTI_V7
 	select ARCH_REQUIRE_GPIOLIB
+	select ARCH_HAS_CPUFREQ
 	select ARM_AMBA
 	select ARM_ERRATA_411920 if ARCH_MULTI_V6
 	select ARM_TIMER_SP804
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -255,3 +255,12 @@ config ACPI_CPPC_CPUFREQ
 	  support for its operation.
 
 	  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.
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_ARM_DT_BL_CPUFREQ)		+= arm_
 
 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
--- /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");