From patchwork Sun Oct 11 19:46:55 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lubomir Rintel X-Patchwork-Id: 7369611 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id F17CB9F302 for ; Sun, 11 Oct 2015 19:54:14 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BD13520784 for ; Sun, 11 Oct 2015 19:54:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 17C9420796 for ; Sun, 11 Oct 2015 19:54:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751424AbbJKTyK (ORCPT ); Sun, 11 Oct 2015 15:54:10 -0400 Received: from shell.v3.sk ([92.60.52.57]:40794 "EHLO shell.v3.sk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751266AbbJKTyJ (ORCPT ); Sun, 11 Oct 2015 15:54:09 -0400 Received: from localhost (localhost [127.0.0.1]) by zimbra.v3.sk (Postfix) with ESMTP id 1A1384478D; Sun, 11 Oct 2015 21:47:08 +0200 (CEST) Received: from shell.v3.sk ([127.0.0.1]) by localhost (zimbra.v3.sk [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id pq2nh5KFb97e; Sun, 11 Oct 2015 21:47:01 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by zimbra.v3.sk (Postfix) with ESMTP id A23684478E; Sun, 11 Oct 2015 21:47:01 +0200 (CEST) X-Virus-Scanned: amavisd-new at zimbra.v3.sk Received: from shell.v3.sk ([127.0.0.1]) by localhost (zimbra.v3.sk [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id jTg0lOQqvhDL; Sun, 11 Oct 2015 21:47:01 +0200 (CEST) Received: from odvarok.lan (ip-89-176-114-212.net.upcbroadband.cz [89.176.114.212]) by zimbra.v3.sk (Postfix) with ESMTPSA id 8D5274478D; Sun, 11 Oct 2015 21:47:00 +0200 (CEST) From: Lubomir Rintel To: linux-rpi-kernel@lists.infradead.org Cc: Lubomir Rintel , Dom Cobley , "Rafael J. Wysocki" , Stephen Warren , Lee Jones , Eric Anholt , cpufreq@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH] cpufreq: Add Broadcom BCM2835 CPU frequency control driver Date: Sun, 11 Oct 2015 21:46:55 +0200 Message-Id: <1444592815-15271-1-git-send-email-lkundrak@v3.sk> X-Mailer: git-send-email 2.4.3 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: Lubomir Rintel Cc: "Rafael J. Wysocki" Cc: Stephen Warren Cc: Lee Jones Cc: Eric Anholt 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 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 +#include +#include +#include +#include +#include +#include +#include + +#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");