Commit 6754f556103be5bd172263b1075ddbb7157afbad

Authored by Mark Langsdorf
Committed by Rafael J. Wysocki
1 parent 300586778d

cpufreq / highbank: add support for highbank cpufreq

Highbank processors depend on the external ECME to perform voltage
management based on a requested frequency. Communication between the
A9 cores and the ECME happens over the pl320 IPC channel.

Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Showing 6 changed files with 149 additions and 2 deletions Side-by-side Diff

arch/arm/boot/dts/highbank.dts
... ... @@ -37,6 +37,16 @@
37 37 next-level-cache = <&L2>;
38 38 clocks = <&a9pll>;
39 39 clock-names = "cpu";
  40 + operating-points = <
  41 + /* kHz ignored */
  42 + 1300000 1000000
  43 + 1200000 1000000
  44 + 1100000 1000000
  45 + 800000 1000000
  46 + 400000 1000000
  47 + 200000 1000000
  48 + >;
  49 + clock-latency = <100000>;
40 50 };
41 51  
42 52 cpu@901 {
arch/arm/mach-highbank/Kconfig
1 1 config ARCH_HIGHBANK
2 2 bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7
  3 + select ARCH_HAS_CPUFREQ
  4 + select ARCH_HAS_OPP
3 5 select ARCH_WANT_OPTIONAL_GPIOLIB
4 6 select ARM_AMBA
5 7 select ARM_GIC
drivers/cpufreq/Kconfig.arm
... ... @@ -83,4 +83,19 @@
83 83 default y
84 84 help
85 85 This adds the CPUFreq driver support for SPEAr SOCs.
  86 +
  87 +config ARM_HIGHBANK_CPUFREQ
  88 + tristate "Calxeda Highbank-based"
  89 + depends on ARCH_HIGHBANK
  90 + select CPU_FREQ_TABLE
  91 + select GENERIC_CPUFREQ_CPU0
  92 + select PM_OPP
  93 + select REGULATOR
  94 +
  95 + default m
  96 + help
  97 + This adds the CPUFreq driver for Calxeda Highbank SoC
  98 + based boards.
  99 +
  100 + If in doubt, say N.
drivers/cpufreq/Makefile
... ... @@ -51,8 +51,9 @@
51 51 obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o
52 52 obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
53 53 obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
54   -obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
  54 +obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
55 55 obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
  56 +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
56 57  
57 58 ##################################################################################
58 59 # PowerPC platform drivers
drivers/cpufreq/cpufreq-cpu0.c
... ... @@ -182,7 +182,11 @@
182 182 struct device_node *np;
183 183 int ret;
184 184  
185   - np = of_find_node_by_path("/cpus/cpu@0");
  185 + for_each_child_of_node(of_find_node_by_path("/cpus"), np) {
  186 + if (of_get_property(np, "operating-points", NULL))
  187 + break;
  188 + }
  189 +
186 190 if (!np) {
187 191 pr_err("failed to find cpu0 node\n");
188 192 return -ENOENT;
drivers/cpufreq/highbank-cpufreq.c
  1 +/*
  2 + * Copyright (C) 2012 Calxeda, Inc.
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify
  5 + * it under the terms of the GNU General Public License version 2 as
  6 + * published by the Free Software Foundation.
  7 + *
  8 + * This driver provides the clk notifier callbacks that are used when
  9 + * the cpufreq-cpu0 driver changes to frequency to alert the highbank
  10 + * EnergyCore Management Engine (ECME) about the need to change
  11 + * voltage. The ECME interfaces with the actual voltage regulators.
  12 + */
  13 +
  14 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15 +
  16 +#include <linux/kernel.h>
  17 +#include <linux/module.h>
  18 +#include <linux/clk.h>
  19 +#include <linux/cpu.h>
  20 +#include <linux/err.h>
  21 +#include <linux/of.h>
  22 +#include <linux/mailbox.h>
  23 +
  24 +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001
  25 +#define HB_CPUFREQ_IPC_LEN 7
  26 +#define HB_CPUFREQ_VOLT_RETRIES 15
  27 +
  28 +static int hb_voltage_change(unsigned int freq)
  29 +{
  30 + int i;
  31 + u32 msg[HB_CPUFREQ_IPC_LEN];
  32 +
  33 + msg[0] = HB_CPUFREQ_CHANGE_NOTE;
  34 + msg[1] = freq / 1000000;
  35 + for (i = 2; i < HB_CPUFREQ_IPC_LEN; i++)
  36 + msg[i] = 0;
  37 +
  38 + return pl320_ipc_transmit(msg);
  39 +}
  40 +
  41 +static int hb_cpufreq_clk_notify(struct notifier_block *nb,
  42 + unsigned long action, void *hclk)
  43 +{
  44 + struct clk_notifier_data *clk_data = hclk;
  45 + int i = 0;
  46 +
  47 + if (action == PRE_RATE_CHANGE) {
  48 + if (clk_data->new_rate > clk_data->old_rate)
  49 + while (hb_voltage_change(clk_data->new_rate))
  50 + if (i++ > HB_CPUFREQ_VOLT_RETRIES)
  51 + return NOTIFY_BAD;
  52 + } else if (action == POST_RATE_CHANGE) {
  53 + if (clk_data->new_rate < clk_data->old_rate)
  54 + while (hb_voltage_change(clk_data->new_rate))
  55 + if (i++ > HB_CPUFREQ_VOLT_RETRIES)
  56 + return NOTIFY_BAD;
  57 + }
  58 +
  59 + return NOTIFY_DONE;
  60 +}
  61 +
  62 +static struct notifier_block hb_cpufreq_clk_nb = {
  63 + .notifier_call = hb_cpufreq_clk_notify,
  64 +};
  65 +
  66 +static int hb_cpufreq_driver_init(void)
  67 +{
  68 + struct device *cpu_dev;
  69 + struct clk *cpu_clk;
  70 + struct device_node *np;
  71 + int ret;
  72 +
  73 + if (!of_machine_is_compatible("calxeda,highbank"))
  74 + return -ENODEV;
  75 +
  76 + for_each_child_of_node(of_find_node_by_path("/cpus"), np)
  77 + if (of_get_property(np, "operating-points", NULL))
  78 + break;
  79 +
  80 + if (!np) {
  81 + pr_err("failed to find highbank cpufreq node\n");
  82 + return -ENOENT;
  83 + }
  84 +
  85 + cpu_dev = get_cpu_device(0);
  86 + if (!cpu_dev) {
  87 + pr_err("failed to get highbank cpufreq device\n");
  88 + ret = -ENODEV;
  89 + goto out_put_node;
  90 + }
  91 +
  92 + cpu_dev->of_node = np;
  93 +
  94 + cpu_clk = clk_get(cpu_dev, NULL);
  95 + if (IS_ERR(cpu_clk)) {
  96 + ret = PTR_ERR(cpu_clk);
  97 + pr_err("failed to get cpu0 clock: %d\n", ret);
  98 + goto out_put_node;
  99 + }
  100 +
  101 + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb);
  102 + if (ret) {
  103 + pr_err("failed to register clk notifier: %d\n", ret);
  104 + goto out_put_node;
  105 + }
  106 +
  107 +out_put_node:
  108 + of_node_put(np);
  109 + return ret;
  110 +}
  111 +module_init(hb_cpufreq_driver_init);
  112 +
  113 +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>");
  114 +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver");
  115 +MODULE_LICENSE("GPL");