From patchwork Thu Feb 2 01:06:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Markus Mayer X-Patchwork-Id: 9550957 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2536060415 for ; Thu, 2 Feb 2017 01:06:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 10BA2203B9 for ; Thu, 2 Feb 2017 01:06:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 054AC24603; Thu, 2 Feb 2017 01:06:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7D527203B9 for ; Thu, 2 Feb 2017 01:06:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751475AbdBBBGd (ORCPT ); Wed, 1 Feb 2017 20:06:33 -0500 Received: from smtp-out-so.shaw.ca ([64.59.136.138]:44872 "EHLO smtp-out-so.shaw.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751289AbdBBBGb (ORCPT ); Wed, 1 Feb 2017 20:06:31 -0500 Received: from triton.mmayer.net ([70.71.160.251]) by shaw.ca with SMTP id Z5r3cu5Tlsa1kZ5r4cJ436; Wed, 01 Feb 2017 18:06:30 -0700 X-Authority-Analysis: v=2.2 cv=W+NIbVek c=1 sm=1 tr=0 a=6xzog4CasRozao6qlzTIAw==:117 a=6xzog4CasRozao6qlzTIAw==:17 a=n2v9WMKugxEA:10 a=Q-fNiiVtAAAA:8 a=BraJ9Q4k4nOvCSIzy4UA:9 a=Fp8MccfUoT0GBdDC_Lng:22 Received: by triton.mmayer.net (Postfix, from userid 501) id 813F03378511; Wed, 1 Feb 2017 17:06:29 -0800 (PST) From: Markus Mayer To: Ralf Baechle , Viresh Kumar , "Rafael J . Wysocki" Cc: Markus Mayer , MIPS Linux Kernel List , Power Management List , Linux Kernel Mailing List Subject: [PATCH 2/3] cpufreq: bmips-cpufreq: CPUfreq driver for Broadcom's BMIPS SoCs Date: Wed, 1 Feb 2017 17:06:00 -0800 Message-Id: <20170202010601.75995-3-code@mmayer.net> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20170202010601.75995-1-code@mmayer.net> References: <20170202010601.75995-1-code@mmayer.net> X-CMAE-Envelope: MS4wfKCKd+JZ6no+AkSoVTPpqcjgqhufEsUifcZANq8HWf7fWQyCrXT8pzj8olADu3fKfn/S2xKMkD2obk69R99DUUl9b0a5y5NrsmpFwpV4HWDlQmGtjs3v DFG9YLNWSusuHifNDoTGLFyXzu60USSFTV3+HpXkMTwyFAQ+Q4F19w4V3S1vO5wXtwAMOD5eyq3X0HH8YbxWzlVSZFwcU5jMlDcQLiS5g3RTOLCSJ9rQVFEZ bSy9KqV/VoSlwt9mDi4XggiVl22/toISOA8Id6xE5GhBvZtLzKUXLkub3v6kpDW/YP64wbMtrM0FnusfzFlrpDfUBHdtl8VoqZd/Crpn7mZXi4DTgZtKPoa5 s3teN/D2OPWaZO68/hz+RkFVvlrZ4A== Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Markus Mayer Add the MIPS CPUfreq driver. This driver currently supports CPUfreq on BMIPS5xxx-based SoCs. Signed-off-by: Markus Mayer --- drivers/cpufreq/Kconfig | 10 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/bmips-cpufreq.c | 205 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 drivers/cpufreq/bmips-cpufreq.c diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index d8b164a..f21fe81 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -271,6 +271,16 @@ config IA64_ACPI_CPUFREQ endif if MIPS +config BMIPS_CPUFREQ + tristate "BMIPS CPUfreq Driver" + help + This option adds a CPUfreq driver for BMIPS processors with + support for configurable CPU frequency. + + For now, BMIPS5 chips are supported (such as the Broadcom 7425). + + If in doubt, say N. + config LOONGSON2_CPUFREQ tristate "Loongson2 CPUFreq Driver" help diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1e46c39..b7b3fc7 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o # Other platform drivers obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o +obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o diff --git a/drivers/cpufreq/bmips-cpufreq.c b/drivers/cpufreq/bmips-cpufreq.c new file mode 100644 index 0000000..c69f382 --- /dev/null +++ b/drivers/cpufreq/bmips-cpufreq.c @@ -0,0 +1,205 @@ +/* + * CPU frequency scaling for Broadcom BMIPS SoCs + * + * Copyright (c) 2017 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +/* for mips_hpt_frequency */ +#include + +#define BMIPS_CPUFREQ_PREFIX "bmips" +#define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq" + +#define TRANSITION_LATENCY (25 * 1000) /* 25 us */ + +#define BMIPS5_CLK_DIV_SET_SHIFT 0x7 +#define BMIPS5_CLK_DIV_SHIFT 0x4 +#define BMIPS5_CLK_DIV_MASK 0xf + +enum bmips_type { + BMIPS5000, + BMIPS5200, +}; + +struct cpufreq_compat { + const char *compatible; + unsigned int bmips_type; + unsigned int clk_mult; + unsigned int max_freqs; +}; + +#define BMIPS(c, t, m, f) { \ + .compatible = c, \ + .bmips_type = (t), \ + .clk_mult = (m), \ + .max_freqs = (f), \ +} + +static struct cpufreq_compat bmips_cpufreq_compat[] = { + BMIPS("brcm,bmips5000", BMIPS5000, 8, 4), + BMIPS("brcm,bmips5200", BMIPS5200, 8, 4), + { } +}; + +static int htp_freq_to_cpu_freq(unsigned int clk_mult) +{ + return mips_hpt_frequency * clk_mult / 1000; +} + +static struct cpufreq_frequency_table * +bmips_cpufreq_get_freq_table(const struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *table; + struct cpufreq_compat *cc; + unsigned long cpu_freq; + int i; + + cc = policy->driver_data; + cpu_freq = htp_freq_to_cpu_freq(cc->clk_mult); + + table = kzalloc((cc->max_freqs + 1) * sizeof(*table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < cc->max_freqs; i++) { + table[i].frequency = cpu_freq / (1 << i); + table[i].driver_data = i; + } + table[i].frequency = CPUFREQ_TABLE_END; + + return table; +} + +static unsigned int bmips_cpufreq_get(unsigned int cpu) +{ + struct cpufreq_policy *policy; + struct cpufreq_compat *cc; + unsigned long freq, cpu_freq; + unsigned int div; + uint32_t mode; + + policy = cpufreq_cpu_get(cpu); + cc = policy->driver_data; + + switch (cc->bmips_type) { + case BMIPS5200: + case BMIPS5000: + mode = read_c0_brcm_mode(); + div = ((mode >> BMIPS5_CLK_DIV_SHIFT) & BMIPS5_CLK_DIV_MASK); + break; + default: + div = 0; + } + + cpu_freq = htp_freq_to_cpu_freq(cc->clk_mult); + freq = cpu_freq / (1 << div); + + return freq; +} + +static int bmips_cpufreq_target_index(struct cpufreq_policy *policy, + unsigned int index) +{ + struct cpufreq_compat *cc; + unsigned int div; + + cc = policy->driver_data; + div = policy->freq_table[index].driver_data; + + switch (cc->bmips_type) { + case BMIPS5200: + case BMIPS5000: + change_c0_brcm_mode(BMIPS5_CLK_DIV_MASK << BMIPS5_CLK_DIV_SHIFT, + (1 << BMIPS5_CLK_DIV_SET_SHIFT) | + (div << BMIPS5_CLK_DIV_SHIFT)); + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +static int bmips_cpufreq_exit(struct cpufreq_policy *policy) +{ + kfree(policy->freq_table); + policy->freq_table = NULL; + + return 0; +} + +static int bmips_cpufreq_init(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *freq_table; + int ret; + + /* Store the compatibility data with the policy. */ + policy->driver_data = cpufreq_get_driver_data(); + + freq_table = bmips_cpufreq_get_freq_table(policy); + if (IS_ERR(freq_table)) { + ret = PTR_ERR(freq_table); + pr_err("%s: couldn't determine frequency table (%d).\n", + BMIPS_CPUFREQ_NAME, ret); + return ret; + } + + ret = cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY); + if (ret) + bmips_cpufreq_exit(policy); + else + pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME); + + return ret; +} + +static struct cpufreq_driver bmips_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = bmips_cpufreq_target_index, + .get = bmips_cpufreq_get, + .init = bmips_cpufreq_init, + .exit = bmips_cpufreq_exit, + .attr = cpufreq_generic_attr, + .name = BMIPS_CPUFREQ_PREFIX, +}; + +static int __init bmips_cpufreq_probe(void) +{ + struct cpufreq_compat *cc; + struct device_node *np; + + for (cc = bmips_cpufreq_compat; cc->compatible; cc++) { + np = of_find_compatible_node(NULL, "cpu", cc->compatible); + if (np) { + of_node_put(np); + bmips_cpufreq_driver.driver_data = cc; + break; + } + } + + /* We hit the guard element of the array. No compatible CPU found. */ + if (!cc->compatible) + return -ENODEV; + + return cpufreq_register_driver(&bmips_cpufreq_driver); +} +device_initcall(bmips_cpufreq_probe); + +MODULE_AUTHOR("Markus Mayer "); +MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs"); +MODULE_LICENSE("GPL");