From patchwork Wed Dec 26 15:33:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gregory CLEMENT X-Patchwork-Id: 10743195 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2E04D13BF for ; Wed, 26 Dec 2018 15:34:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1A22A1FFFE for ; Wed, 26 Dec 2018 15:34:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0DBF82860B; Wed, 26 Dec 2018 15:34:40 +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=-3.7 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,URIBL_RHS_DOB autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 8614E1FFFE for ; Wed, 26 Dec 2018 15:34:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=zOoUVZqeC2cpODUjVQ9ustFF4bCPOPHMtTrzTi9FtCI=; b=fqHpqXqgs8HFSP s98NpN+ynx81BoQ4F0JR6S1dVVmJniyHbuCTbQeCQRuvLAjsraSv+pnUdP5BlJNPcsKolSMRGl4CX y2zUZbOA6pPGBccM5OQ8OwWpAskD0t9z0+3sr8G1oR9NSHT+MElImBuouXNlZIhzhaeUmpgYrLgHv mSr3FL6hGX+caa1jlykQPSTftr7O7cZ19tAaOpTR3UrYIN3WSIvdcvcHyyu0TCn6wMZhdaFDaUy6K Y2fY7K5iIpPltZMvpgTveGVIRXJLAQEbe5HMJ4leXGwhkFJPne/bHmeVZqkBbPSnQWIEcUSfpKAML rWrvp2ikVeFVhkaFpfjg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gcBCa-0005Rj-Aj; Wed, 26 Dec 2018 15:34:32 +0000 Received: from mail.bootlin.com ([62.4.15.54]) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gcBCJ-0005Bq-4N for linux-arm-kernel@lists.infradead.org; Wed, 26 Dec 2018 15:34:17 +0000 Received: by mail.bootlin.com (Postfix, from userid 110) id E4323209ED; Wed, 26 Dec 2018 16:34:00 +0100 (CET) Received: from localhost (242.171.71.37.rev.sfr.net [37.71.171.242]) by mail.bootlin.com (Postfix) with ESMTPSA id B12A420A02; Wed, 26 Dec 2018 16:33:50 +0100 (CET) From: Gregory CLEMENT To: "Rafael J. Wysocki" , Viresh Kumar , linux-pm@vger.kernel.org Subject: [PATCH v4 2/2] cpufreq: ap806: add cpufreq driver for Armada 8K Date: Wed, 26 Dec 2018 16:33:42 +0100 Message-Id: <20181226153342.17796-3-gregory.clement@bootlin.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20181226153342.17796-1-gregory.clement@bootlin.com> References: <20181226153342.17796-1-gregory.clement@bootlin.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181226_073415_442422_4A9435B5 X-CRM114-Status: GOOD ( 19.83 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Andrew Lunn , Jason Cooper , Antoine Tenart , Gregory CLEMENT , Maxime Chevallier , Thomas Petazzoni , =?utf-8?q?Miqu=C3=A8l_Rayn?= =?utf-8?q?al?= , linux-arm-kernel@lists.infradead.org, Sebastian Hesselbarth Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Add cpufreq driver for Marvell AP-806 found on Aramda 8K. The AP-806 has DFS (Dynamic Frequency Scaling) with coupled clock domain for two clusters, so this driver will directly use generic cpufreq-dt driver as backend. Based on the work of Omri Itach . Signed-off-by: Gregory CLEMENT --- drivers/cpufreq/Kconfig.arm | 11 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/armada-8k-cpufreq.c | 203 ++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 drivers/cpufreq/armada-8k-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 4e1131ef85ae..7e32a241760d 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -25,6 +25,17 @@ config ARM_ARMADA_37XX_CPUFREQ This adds the CPUFreq driver support for Marvell Armada 37xx SoCs. The Armada 37xx PMU supports 4 frequency and VDD levels. +config ARM_ARMADA_8K_CPUFREQ + tristate "Armada 8K CPUFreq driver" + depends on ARCH_MVEBU && CPUFREQ_DT + help + This enables the CPUFreq driver support for Marvell + Armada8k SOCs. + Armada8K device has the AP806 which supports scaling + to any full integer divider. + + If in doubt, say N. + # big LITTLE core layer and glue drivers config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index d5ee4562ed06..db1564b610ac 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o +obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o diff --git a/drivers/cpufreq/armada-8k-cpufreq.c b/drivers/cpufreq/armada-8k-cpufreq.c new file mode 100644 index 000000000000..80375de4e149 --- /dev/null +++ b/drivers/cpufreq/armada-8k-cpufreq.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * CPUFreq support for Armada 8K + * + * Copyright (C) 2018 Marvell + * + * Omri Itach + * Gregory Clement + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Setup the opps list with the divider for the max frequency, that + * will be filled at runtime. + */ +static const int opps_div[] __initconst = {1, 2, 3, 4}; + +static struct platform_device *armada_8k_pdev; + +struct freq_table { + struct device *cpu_dev; + unsigned int freq[ARRAY_SIZE(opps_div)]; +}; + +/* If the CPUs share the same clock, then they are in the same cluster. */ +static void __init armada_8k_get_sharing_cpus(struct clk *cur_clk, + struct cpumask *cpumask) +{ + int cpu; + + for_each_possible_cpu(cpu) { + struct device *cpu_dev; + struct clk *clk; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_warn("Failed to get cpu%d device\n", cpu); + continue; + } + + clk = clk_get(cpu_dev, 0); + if (IS_ERR(clk)) + pr_warn("Cannot get clock for CPU %d\n", cpu); + else if (clk_is_match(clk, cur_clk)) + cpumask_set_cpu(cpu, cpumask); + + if (!IS_ERR(clk)) + clk_put(clk); + } +} + +static int __init armada_8k_add_opp(struct clk *clk, struct device *cpu_dev, + struct freq_table *freq_tables, + int opps_index) +{ + unsigned int cur_frequency; + unsigned int freq; + int i, ret; + + /* Get nominal (current) CPU frequency. */ + cur_frequency = clk_get_rate(clk); + if (!cur_frequency) { + dev_err(cpu_dev, "Failed to get clock rate for this CPU\n"); + return -EINVAL; + } + + freq_tables[opps_index].cpu_dev = cpu_dev; + + for (i = 0; i < ARRAY_SIZE(opps_div); i++) { + freq = cur_frequency / opps_div[i]; + + ret = dev_pm_opp_add(cpu_dev, freq, 0); + if (ret) + return ret; + + freq_tables[opps_index].freq[i] = freq; + } + + return 0; +} + +static void armada_8k_cpufreq_free_table(struct freq_table *freq_tables) +{ + int opps_index, nb_cpus = num_possible_cpus(); + + for (opps_index = 0 ; opps_index <= nb_cpus; opps_index++) { + int i; + + /* If cpu_dev is NULL then we reached the end of the array */ + if (!freq_tables[opps_index].cpu_dev) + break; + + for (i = 0; i < ARRAY_SIZE(opps_div); i++) { + /* + * A 0Hz frequency is not valid, this meant + * that it was not yet initialized so there is + * no more opp to free + */ + if (freq_tables[opps_index].freq[i] == 0) + break; + + dev_pm_opp_remove(freq_tables[opps_index].cpu_dev, + freq_tables[opps_index].freq[i]); + } + } + + kfree(freq_tables); +} + +static int __init armada_8k_cpufreq_init(void) +{ + int ret = 0, opps_index = 0, cpu, nb_cpus; + struct freq_table *freq_tables; + struct device_node *node; + struct cpumask cpus; + + node = of_find_compatible_node(NULL, NULL, "marvell,ap806-cpu-clock"); + if (!node || !of_device_is_available(node)) + return -ENODEV; + + nb_cpus = num_possible_cpus(); + freq_tables = kcalloc(nb_cpus, sizeof(*freq_tables), GFP_KERNEL); + cpumask_copy(&cpus, cpu_possible_mask); + + /* + * For each CPU, this loop registers the operating points + * supported (which are the nominal CPU frequency and full integer + * divisions of it). + */ + for_each_cpu(cpu, &cpus) { + struct cpumask shared_cpus; + struct device *cpu_dev; + struct clk *clk; + + cpu_dev = get_cpu_device(cpu); + + if (!cpu_dev) { + pr_err("Cannot get CPU %d\n", cpu); + continue; + } + + clk = clk_get(cpu_dev, 0); + + if (IS_ERR(clk)) { + pr_err("Cannot get clock for CPU %d\n", cpu); + ret = PTR_ERR(clk); + goto remove_opp; + } + + ret = armada_8k_add_opp(clk, cpu_dev, freq_tables, opps_index); + if (ret) { + clk_put(clk); + goto remove_opp; + } + + opps_index++; + cpumask_clear(&shared_cpus); + armada_8k_get_sharing_cpus(clk, &shared_cpus); + dev_pm_opp_set_sharing_cpus(cpu_dev, &shared_cpus); + cpumask_andnot(&cpus, &cpus, &shared_cpus); + clk_put(clk); + } + + armada_8k_pdev = platform_device_register_simple("cpufreq-dt", -1, + NULL, 0); + ret = PTR_ERR_OR_ZERO(armada_8k_pdev); + if (ret) + goto remove_opp; + + platform_set_drvdata(armada_8k_pdev, freq_tables); + + return 0; + +remove_opp: + armada_8k_cpufreq_free_table(freq_tables); + return ret; +} +module_init(armada_8k_cpufreq_init); + +static void __exit armada_8k_cpufreq_exit(void) +{ + struct freq_table *freq_tables = platform_get_drvdata(armada_8k_pdev); + + armada_8k_cpufreq_free_table(freq_tables); + platform_device_unregister(armada_8k_pdev); +} +module_exit(armada_8k_cpufreq_exit); + +MODULE_AUTHOR("Gregory Clement "); +MODULE_DESCRIPTION("Armada 8K cpufreq driver"); +MODULE_LICENSE("GPL");