* [PATCH 5/6] power: export opp cpufreq functions [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> @ 2012-10-30 21:04 ` Mark Langsdorf 2012-10-31 1:17 ` Nishanth Menon 2012-10-31 0:21 ` [PATCH 0/6] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki ` (11 subsequent siblings) 12 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-10-30 21:04 UTC (permalink / raw) To: linux-kernel; +Cc: Mark Langsdorf, linux-pm Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Cc: linux-pm@vger.kernel.org --- drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..37dc5f4 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/module.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 5/6] power: export opp cpufreq functions 2012-10-30 21:04 ` [PATCH 5/6] power: export opp cpufreq functions Mark Langsdorf @ 2012-10-31 1:17 ` Nishanth Menon 0 siblings, 0 replies; 120+ messages in thread From: Nishanth Menon @ 2012-10-31 1:17 UTC (permalink / raw) To: Mark Langsdorf; +Cc: linux-kernel, linux-pm On 16:04-20121030, Mark Langsdorf wrote: $subject PM / OPP: Also adding info that this allows cpufreq drivers to be used as module might be helpful. > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > Cc: linux-pm@vger.kernel.org Side note: Applies on v3.7-rc3 on rafael's linux-next branch: linux-next 2b7f449 Merge branch 'pm-opp-next' into linux-next on git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git needs a rebase as it probably conflicts with https://patchwork.kernel.org/patch/1582091/ Otherwise, approach: Acked-by: Nishanth Menon <nm@ti.com> -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/6] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> 2012-10-30 21:04 ` [PATCH 5/6] power: export opp cpufreq functions Mark Langsdorf @ 2012-10-31 0:21 ` Rafael J. Wysocki [not found] ` <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com> ` (10 subsequent siblings) 12 siblings, 0 replies; 120+ messages in thread From: Rafael J. Wysocki @ 2012-10-31 0:21 UTC (permalink / raw) To: Mark Langsdorf; +Cc: linux-kernel, Linux PM list On Tuesday, October 30, 2012 04:04:10 PM Mark Langsdorf wrote: > This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) > SoCs. The driver is based on the cpufreq-cpu0 driver. Because of the > unique way that highbank uses the EnergyCore Management Engine to manage > voltages, it was not possible to use the cpufreq-cpu0 driver. I've got patches [3-6/6] only for some reason. Care to resend the other three with direct CCs to me? Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
[parent not found: <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com>]
* [PATCH 5/6 v2] power: export opp cpufreq functions [not found] ` <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com> @ 2012-11-02 18:51 ` Mark Langsdorf 0 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-02 18:51 UTC (permalink / raw) To: linux-kernel; +Cc: cpufreq, Mark Langsdorf, linux-pm These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> Cc: linux-pm@vger.kernel.org Changes from v1: Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. --- drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..37dc5f4 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/module.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (2 preceding siblings ...) [not found] ` <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com> @ 2012-11-06 20:18 ` Mark Langsdorf 2012-11-06 20:18 ` [PATCH 1/6 v3] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (6 more replies) 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf ` (8 subsequent siblings) 12 siblings, 7 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-06 20:18 UTC (permalink / raw) To: linux-kernel, linux-pm, cpufreq, mark.langsdorf This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The driver is based on the cpufreq-cpu0 driver. Because of the unique way that highbank uses the EnergyCore Management Engine to manage voltages, it was not possible to use the cpufreq-cpu0 driver. --Mark Langsdorf ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/6 v3] arm: use devicetree to get smp_twd clock 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2012-11-06 20:18 ` Mark Langsdorf 2012-11-06 20:18 ` [PATCH 2/6 v3] clk, highbank: remove non-bypass reset mode Mark Langsdorf ` (5 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-06 20:18 UTC (permalink / raw) To: linux-kernel, linux-pm, cpufreq, mark.langsdorf; +Cc: Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate Changes from v1 None. arch/arm/kernel/smp_twd.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b22d700..b1fb6e1 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { - struct clk *clk; + struct clk *clk = NULL; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -263,6 +266,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -273,12 +277,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) { struct clock_event_device **this_cpu_clk; - if (!twd_clk) - twd_clk = twd_get_clock(); - - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else + if (IS_ERR_OR_NULL(twd_clk)) twd_calibrate_rate(); __raw_writel(0, twd_base + TWD_TIMER_CONTROL); @@ -349,6 +348,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -383,6 +384,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/6 v3] clk, highbank: remove non-bypass reset mode 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-11-06 20:18 ` [PATCH 1/6 v3] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-11-06 20:18 ` Mark Langsdorf 2012-11-06 20:18 ` [PATCH 3/6 v3] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf ` (4 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-06 20:18 UTC (permalink / raw) To: linux-kernel, linux-pm, cpufreq, mark.langsdorf The highbank clock will glitch if the clock rate is reset without relocking the PLL. Remove the option to attempt reseting without relocking. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v2: None Changes from v1: Removed erroneous reformating. drivers/clk/clk-highbank.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..4f50c42 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -171,7 +171,8 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, writel(reg | HB_PLL_RESET, hbclk->reg); reg &= ~(HB_PLL_DIVF_MASK | HB_PLL_DIVQ_MASK); - reg |= (divf << HB_PLL_DIVF_SHIFT) | (divq << HB_PLL_DIVQ_SHIFT); + reg |= (divf << HB_PLL_DIVF_SHIFT) | + (divq << HB_PLL_DIVQ_SHIFT); writel(reg | HB_PLL_RESET, hbclk->reg); writel(reg, hbclk->reg); @@ -182,8 +183,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 3/6 v3] cpufreq: tolerate inexact values when collecting stats 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-11-06 20:18 ` [PATCH 1/6 v3] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-06 20:18 ` [PATCH 2/6 v3] clk, highbank: remove non-bypass reset mode Mark Langsdorf @ 2012-11-06 20:18 ` Mark Langsdorf 2012-11-06 20:18 ` [PATCH 4/6 v3] arm highbank: add support for pl320 IPC Mark Langsdorf ` (3 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-06 20:18 UTC (permalink / raw) To: linux-kernel, linux-pm, cpufreq, mark.langsdorf Allow frequency values to vary by +/-5000 Hz when collecting stats. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v2: None Changes from v1: Implemented a simple round-up algorithm instead of the over/under method that could cause errors on Intel processors with boost mode. drivers/cpufreq/cpufreq_stats.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 3998316..4e2ea7e 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -158,9 +158,11 @@ static struct attribute_group stats_attr_group = { static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) { int index; - for (index = 0; index < stat->max_state; index++) - if (stat->freq_table[index] == freq) + for (index = 0; index < stat->max_state; index++) { + if ((stat->freq_table[index] < (freq + 5000)) && + (stat->freq_table[index] > (freq - 5000))) return index; + } return -1; } @@ -251,6 +253,8 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, spin_lock(&cpufreq_stats_lock); stat->last_time = get_jiffies_64(); stat->last_index = freq_table_get_index(stat, policy->cur); + if (stat->last_index > stat->max_state) + stat->last_index = stat->max_state - 1; spin_unlock(&cpufreq_stats_lock); cpufreq_cpu_put(data); return 0; -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 4/6 v3] arm highbank: add support for pl320 IPC 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (2 preceding siblings ...) 2012-11-06 20:18 ` [PATCH 3/6 v3] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-11-06 20:18 ` Mark Langsdorf 2012-11-06 20:18 ` [PATCH 5/6 v3] power: export opp cpufreq functions Mark Langsdorf ` (2 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-06 20:18 UTC (permalink / raw) To: linux-kernel, linux-pm, cpufreq, mark.langsdorf; +Cc: Rob Herring From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> --- Changes from v2: None Changes from v1: Removed erroneous changes for cpufreq Kconfig arch/arm/include/asm/pl320-ipc.h | 20 ++ arch/arm/mach-highbank/Kconfig | 2 + arch/arm/mach-highbank/Makefile | 2 + arch/arm/mach-highbank/include/mach/pl320-ipc.h | 20 ++ arch/arm/mach-highbank/pl320-ipc.c | 232 ++++++++++++++++++++++++ 5 files changed, 276 insertions(+) create mode 100644 arch/arm/include/asm/pl320-ipc.h create mode 100644 arch/arm/mach-highbank/include/mach/pl320-ipc.h create mode 100644 arch/arm/mach-highbank/pl320-ipc.c diff --git a/arch/arm/include/asm/pl320-ipc.h b/arch/arm/include/asm/pl320-ipc.h new file mode 100644 index 0000000..a0e58ee --- /dev/null +++ b/arch/arm/include/asm/pl320-ipc.h @@ -0,0 +1,20 @@ +/* + * Copyright 2010 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +int ipc_call_fast(u32 *data); +int ipc_call_slow(u32 *data); + +extern int pl320_ipc_register_notifier(struct notifier_block *nb); +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 0e1d0a4..ee83af6 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -13,3 +13,5 @@ config ARCH_HIGHBANK select HAVE_SMP select SPARSE_IRQ select USE_OF + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP diff --git a/arch/arm/mach-highbank/Makefile b/arch/arm/mach-highbank/Makefile index 3ec8bdd..b894708 100644 --- a/arch/arm/mach-highbank/Makefile +++ b/arch/arm/mach-highbank/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_DEBUG_HIGHBANK_UART) += lluart.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_PM_SLEEP) += pm.o + +obj-y += pl320-ipc.o diff --git a/arch/arm/mach-highbank/include/mach/pl320-ipc.h b/arch/arm/mach-highbank/include/mach/pl320-ipc.h new file mode 100644 index 0000000..a0e58ee --- /dev/null +++ b/arch/arm/mach-highbank/include/mach/pl320-ipc.h @@ -0,0 +1,20 @@ +/* + * Copyright 2010 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +int ipc_call_fast(u32 *data); +int ipc_call_slow(u32 *data); + +extern int pl320_ipc_register_notifier(struct notifier_block *nb); +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); diff --git a/arch/arm/mach-highbank/pl320-ipc.c b/arch/arm/mach-highbank/pl320-ipc.c new file mode 100644 index 0000000..0eb92e4 --- /dev/null +++ b/arch/arm/mach-highbank/pl320-ipc.c @@ -0,0 +1,232 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <asm/pl320-ipc.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_FAST_MBOX 0 +#define IPC_SLOW_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_SPINLOCK(ipc_m0_lock); +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* non-blocking implementation from the A9 side, interrupt safe in theory */ +int ipc_call_fast(u32 *data) +{ + int timeout, ret; + + spin_lock(&ipc_m0_lock); + + __ipc_send(IPC_FAST_MBOX, data); + + for (timeout = 500; timeout > 0; timeout--) { + if (__raw_readl(ipc_base + IPCMxSEND(IPC_FAST_MBOX)) == 0x2) + break; + udelay(100); + } + if (timeout == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_FAST_MBOX, data); +out: + __raw_writel(0, ipc_base + IPCMxSEND(IPC_FAST_MBOX)); + spin_unlock(&ipc_m0_lock); + return ret; +} +EXPORT_SYMBOL(ipc_call_fast); + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int ipc_call_slow(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_SLOW_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_SLOW_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(ipc_call_slow); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_SLOW_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_SLOW_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_FAST_MBOX)); + __raw_writel(0, ipc_base + IPCMxSEND(IPC_SLOW_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init fast mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_FAST_MBOX)); + set_destination(M3_SOURCE, IPC_FAST_MBOX); + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_SLOW_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_SLOW_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_SLOW_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 5/6 v3] power: export opp cpufreq functions 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (3 preceding siblings ...) 2012-11-06 20:18 ` [PATCH 4/6 v3] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-11-06 20:18 ` Mark Langsdorf 2012-11-06 20:18 ` [PATCH 6/6 v3] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-11-07 18:11 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-06 20:18 UTC (permalink / raw) To: linux-kernel, linux-pm, cpufreq, mark.langsdorf These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> --- Changes from v2: None. Changes from v1: Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..37dc5f4 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/module.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 6/6 v3] cpufreq, highbank: add support for highbank cpufreq 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (4 preceding siblings ...) 2012-11-06 20:18 ` [PATCH 5/6 v3] power: export opp cpufreq functions Mark Langsdorf @ 2012-11-06 20:18 ` Mark Langsdorf 2012-11-07 18:11 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-06 20:18 UTC (permalink / raw) To: linux-kernel, linux-pm, cpufreq, mark.langsdorf Cc: devicetree-discuss, Rafael J. Wysocki Highbank processors depend on the external ECME to perform voltage management based on a requested frequency. Communication between the highbank and ECME cores happens over the pl320 IPC channel. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Cc: devicetree-discuss@lists.ozlabs.org Cc: Rafael J. Wysocki <rjw@sisk.pl> --- Changes from v2: Changed transition latency binding in code to match documentation Changes from v1: Added highbank specific Kconfig changes .../bindings/cpufreq/highbank-cpufreq.txt | 53 +++++ arch/arm/Kconfig | 2 + arch/arm/boot/dts/highbank.dts | 10 + drivers/cpufreq/Kconfig.arm | 15 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 229 +++++++++++++++++++++ 6 files changed, 310 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt new file mode 100644 index 0000000..3ec2cec --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt @@ -0,0 +1,53 @@ +Highbank cpufreq driver + +This is cpufreq driver for Calxeda ECX-1000 (highbank) processor. It is based +on the generic cpu0 driver and uses a similar format for bindings. Since +the EnergyCore Management Engine maintains the voltage based on the +frequency, the voltage component of the operating points can be set to any +arbitrary values. + +Both required properties listed below must be defined under node /cpus/cpu@0. + +Required properties: +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt + for details +- clock-latency: Specify the possible maximum transition latency for clock, + in unit of nanoseconds. + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz ignored */ + 790000 1000000 + 396000 1000000 + 198000 1000000 + >; + transition-latency = <200000>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ade7e92..4ed0b7b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -391,6 +391,8 @@ config ARCH_SIRF select PINCTRL select PINCTRL_SIRF select USE_OF + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP help Support for CSR SiRFprimaII/Marco/Polo platforms diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 0c6fc34..7c4c27d 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + clock-latency = <100000>; }; cpu@1 { diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e64..bc3ef55 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,18 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select HAVE_CLK + select PM_OPP + select OF + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1bc90e1..9e8f12a 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..a167073 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * derived from cpufreq-cpu0 by Freescale Semiconductor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/slab.h> +#include <asm/pl320-ipc.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 + +static unsigned int transition_latency; + +static struct device *cpu_dev; +static struct clk *cpu_clk; +static struct cpufreq_frequency_table *freq_table; + +static int hb_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +static unsigned int hb_get_speed(unsigned int cpu) +{ + return clk_get_rate(cpu_clk) / 1000; +} + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[7]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000; + for (i = 2; i < 7; i++) + msg[i] = 0; + + return ipc_call_slow(msg); +} + +static int hb_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + unsigned long freq_Hz; + unsigned int index, cpu; + int ret; + + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &index); + if (ret) { + pr_err("failed to match target freqency %d: %d\n", + target_freq, ret); + return ret; + } + + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); + if (freq_Hz < 0) + freq_Hz = freq_table[index].frequency * 1000; + freqs.new = freq_Hz / 1000; + freqs.old = clk_get_rate(cpu_clk) / 1000; + + if (freqs.old == freqs.new) + return 0; + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } + + pr_debug("%u MHz --> %u MHz\n", freqs.old / 1000, freqs.new / 1000); + + /* scaling up? scale voltage before frequency */ + if (freqs.new > freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + freqs.new = freqs.old; + return -EAGAIN; + } + } + + ret = clk_set_rate(cpu_clk, freqs.new * 1000); + if (ret) { + pr_err("failed to set clock rate: %d\n", ret); + hb_voltage_change(freqs.old); + return ret; + } + + /* scaling down? scale voltage after frequency */ + if (freqs.new < freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + if (clk_set_rate(cpu_clk, freqs.old * 1000)) + pr_err("also failed to reset freq\n"); + freqs.new = freqs.old; + return -EAGAIN; + } + } + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + + return 0; +} + +static int hb_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + + if (policy->cpu != 0) + return -EINVAL; + + ret = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (ret) { + pr_err("invalid frequency table: %d\n", ret); + return ret; + } + + policy->cpuinfo.transition_latency = transition_latency; + policy->cur = clk_get_rate(cpu_clk) / 1000; + + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; + cpumask_setall(policy->cpus); + + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + + return 0; +} + +static int hb_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + + return 0; +} + +static struct freq_attr *hb_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver hb_cpufreq_driver = { + .flags = CPUFREQ_STICKY, + .verify = hb_verify_speed, + .target = hb_set_target, + .get = hb_get_speed, + .init = hb_cpufreq_init, + .exit = hb_cpufreq_exit, + .name = "highbank-cpufreq", + .attr = hb_cpufreq_attr, +}; + +static int __devinit hb_cpufreq_driver_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = of_init_opp_table(cpu_dev); + if (ret) { + pr_err("failed to init OPP table: %d\n", ret); + goto out_put_node; + } + + ret = opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table: %d\n", ret); + goto out_put_node; + } + + if (of_property_read_u32(np, "transition-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + ret = cpufreq_register_driver(&hb_cpufreq_driver); + if (ret) { + pr_err("failed register driver: %d\n", ret); + goto out_free_table; + } + + of_node_put(np); + return 0; + +out_free_table: + opp_free_cpufreq_table(cpu_dev, &freq_table); +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (5 preceding siblings ...) 2012-11-06 20:18 ` [PATCH 6/6 v3] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf @ 2012-11-07 18:11 ` Mark Langsdorf 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:11 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, cpufreq@vger.kernel.org On 11/06/2012 02:18 PM, Mark Langsdorf wrote: > This patch series adds cpufreq support for the Calxeda > ECX-1000 (highbank) SoCs. The driver is based on the > cpufreq-cpu0 driver. Because of the unique way that > highbank uses the EnergyCore Management Engine to manage > voltages, it was not possible to use the cpufreq-cpu0 driver. I accidentally sent out old versions of the patches. Please ignore the v3 series and look at the v4 instead. --Mark Langsdorf ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/6 v4] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (3 preceding siblings ...) 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2012-11-07 18:32 ` Mark Langsdorf 2012-11-07 18:32 ` [PATCH 1/6 v4] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (6 more replies) 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf ` (7 subsequent siblings) 12 siblings, 7 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:32 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The driver is based on the cpufreq-cpu0 driver. Because of the unique way that highbank uses the EnergyCore Management Engine to manage voltages, it was not possible to use the cpufreq-cpu0 driver. --Mark Langsdorf ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/6 v4] arm: use devicetree to get smp_twd clock 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf @ 2012-11-07 18:32 ` Mark Langsdorf 2012-11-07 18:32 ` [PATCH 2/6 v4] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (5 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:32 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm; +Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v3 No longer setting *clk to NULL in twd_get_clock() Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate Changes from v1 None. arch/arm/kernel/smp_twd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b22d700..af46b80 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -263,6 +266,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -273,12 +277,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) { struct clock_event_device **this_cpu_clk; - if (!twd_clk) - twd_clk = twd_get_clock(); - - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else + if (IS_ERR_OR_NULL(twd_clk)) twd_calibrate_rate(); __raw_writel(0, twd_base + TWD_TIMER_CONTROL); @@ -349,6 +348,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -383,6 +384,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/6 v4] clk, highbank: Prevent glitches in non-bypass reset mode 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf 2012-11-07 18:32 ` [PATCH 1/6 v4] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-11-07 18:32 ` Mark Langsdorf 2012-11-12 21:24 ` Mike Turquette 2012-11-07 18:32 ` [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf ` (4 subsequent siblings) 6 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:32 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm; +Cc: Mark Langsdorf, Rob Herring, mturquette The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to preven glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: mturquette@linaro.org --- Changes from v3 Changelog text and patch name now correspond to the actual patch was clk, highbank: remove non-bypass reset mode Changes from v2 None Changes from v1: Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 2/6 v4] clk, highbank: Prevent glitches in non-bypass reset mode 2012-11-07 18:32 ` [PATCH 2/6 v4] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-11-12 21:24 ` Mike Turquette 2012-11-12 21:35 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Mike Turquette @ 2012-11-12 21:24 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm; +Cc: Mark Langsdorf, Rob Herring Quoting Mark Langsdorf (2012-11-07 10:32:42) > The highbank clock will glitch with the current code if the > clock rate is reset without relocking the PLL. Program the PLL > correctly to preven glitches. > > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > Cc: mturquette@linaro.org Hi Mark, Looks fine to me. I seem to be missing the rest of this series in my mail. Did you want me to take only this patch (2/6) into clk-next or were you only looking for my ACK? Regards, Mike > --- > Changes from v3 > Changelog text and patch name now correspond to the actual patch > was clk, highbank: remove non-bypass reset mode > Changes from v2 > None > Changes from v1: > Removed erroneous reformating. > > drivers/clk/clk-highbank.c | 2 ++ > 1 file changed, 2 insertions(+) > > diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c > index 52fecad..3a0b723 100644 > --- a/drivers/clk/clk-highbank.c > +++ b/drivers/clk/clk-highbank.c > @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, > reg |= HB_PLL_EXT_ENA; > reg &= ~HB_PLL_EXT_BYPASS; > } else { > + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); > reg &= ~HB_PLL_DIVQ_MASK; > reg |= divq << HB_PLL_DIVQ_SHIFT; > + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); > } > writel(reg, hbclk->reg); > > -- > 1.7.11.7 ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 2/6 v4] clk, highbank: Prevent glitches in non-bypass reset mode 2012-11-12 21:24 ` Mike Turquette @ 2012-11-12 21:35 ` Mark Langsdorf 0 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-12 21:35 UTC (permalink / raw) To: Mike Turquette Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, Rob Herring On 11/12/2012 03:24 PM, Mike Turquette wrote: > Quoting Mark Langsdorf (2012-11-07 10:32:42) >> The highbank clock will glitch with the current code if the >> clock rate is reset without relocking the PLL. Program the PLL >> correctly to preven glitches. >> >> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> >> Signed-off-by: Rob Herring <rob.herring@calxeda.com> >> Cc: mturquette@linaro.org > > Hi Mark, > > Looks fine to me. > > I seem to be missing the rest of this series in my mail. Did you want > me to take only this patch (2/6) into clk-next or were you only looking > for my ACK? The entire series enables highbank cpufreq. Would you normally take this patch through cpufreq-next with an ACK or directly through clk-next? --Mark Langsdorf Calxeda, Inc. >> --- >> Changes from v3 >> Changelog text and patch name now correspond to the actual patch >> was clk, highbank: remove non-bypass reset mode >> Changes from v2 >> None >> Changes from v1: >> Removed erroneous reformating. >> >> drivers/clk/clk-highbank.c | 2 ++ >> 1 file changed, 2 insertions(+) >> >> diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c >> index 52fecad..3a0b723 100644 >> --- a/drivers/clk/clk-highbank.c >> +++ b/drivers/clk/clk-highbank.c >> @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, >> reg |= HB_PLL_EXT_ENA; >> reg &= ~HB_PLL_EXT_BYPASS; >> } else { >> + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); >> reg &= ~HB_PLL_DIVQ_MASK; >> reg |= divq << HB_PLL_DIVQ_SHIFT; >> + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); >> } >> writel(reg, hbclk->reg); >> >> -- >> 1.7.11.7 ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf 2012-11-07 18:32 ` [PATCH 1/6 v4] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-07 18:32 ` [PATCH 2/6 v4] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-11-07 18:32 ` Mark Langsdorf 2012-11-11 16:38 ` Borislav Petkov 2012-11-07 18:32 ` [PATCH 4/6 v4] arm highbank: add support for pl320 IPC Mark Langsdorf ` (3 subsequent siblings) 6 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:32 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm; +Cc: Mark Langsdorf, MyungJoo Ham When collecting stats, if a frequency doesn't match the table, go through the table again with both the search frequency and table values shifted left by 10 bits. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Cc: MyungJoo Ham <myungjoo.ham@gmail.com> --- Changes from v3, v2 None Changes from v1: Implemented a simple round-up algorithm instead of the over/under method that could cause errors on Intel processors with boost mode. drivers/cpufreq/cpufreq_stats.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 3998316..30aee36 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -161,6 +161,9 @@ static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) for (index = 0; index < stat->max_state; index++) if (stat->freq_table[index] == freq) return index; + for (index = 0; index < stat->max_state; index++) + if ((stat->freq_table[index] >> 10) == (freq >> 10)) + return index; return -1; } @@ -251,6 +254,8 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, spin_lock(&cpufreq_stats_lock); stat->last_time = get_jiffies_64(); stat->last_index = freq_table_get_index(stat, policy->cur); + if (stat->last_index > stat->max_state) + stat->last_index = stat->max_state - 1; spin_unlock(&cpufreq_stats_lock); cpufreq_cpu_put(data); return 0; -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-07 18:32 ` [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-11-11 16:38 ` Borislav Petkov 2012-11-12 16:35 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Borislav Petkov @ 2012-11-11 16:38 UTC (permalink / raw) To: Mark Langsdorf; +Cc: linux-kernel, cpufreq, linux-pm, MyungJoo Ham On Wed, Nov 07, 2012 at 12:32:43PM -0600, Mark Langsdorf wrote: > When collecting stats, if a frequency doesn't match the table, go through > the table again with both the search frequency and table values shifted > left by 10 bits. Why would that second pass succeed? And why is this in generic code (I'm assuming this is a Calxeda-specific case)? -- Regards/Gruss, Boris. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-11 16:38 ` Borislav Petkov @ 2012-11-12 16:35 ` Mark Langsdorf 2012-11-13 16:24 ` Borislav Petkov 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-12 16:35 UTC (permalink / raw) To: Borislav Petkov Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, MyungJoo Ham On 11/11/2012 10:38 AM, Borislav Petkov wrote: > On Wed, Nov 07, 2012 at 12:32:43PM -0600, Mark Langsdorf wrote: >> When collecting stats, if a frequency doesn't match the table, go through >> the table again with both the search frequency and table values shifted >> left by 10 bits. > > Why would that second pass succeed? It's effectively a divide by 1024 and minimizes any jitter in the measured frequency value. > And why is this in generic code (I'm assuming this is a Calxeda-specific > case)? The function is buried pretty deep in the cpufreq_stat code. It didn't seem appropriate to make it a function pointer as part of struct cpufreq_driver. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-12 16:35 ` Mark Langsdorf @ 2012-11-13 16:24 ` Borislav Petkov 2012-11-13 16:33 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Borislav Petkov @ 2012-11-13 16:24 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, MyungJoo Ham On Mon, Nov 12, 2012 at 10:35:49AM -0600, Mark Langsdorf wrote: > The function is buried pretty deep in the cpufreq_stat code. It didn't > seem appropriate to make it a function pointer as part of struct > cpufreq_driver. Right, what's cpufreq-speak for if (Calxeda) shift by 10 ? Better yet, add a flag or a bitfield called "minimize_jitter" or similar and set it only on your hardware... Thanks. -- Regards/Gruss, Boris. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-13 16:24 ` Borislav Petkov @ 2012-11-13 16:33 ` Mark Langsdorf 2012-11-13 19:13 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-13 16:33 UTC (permalink / raw) To: Borislav Petkov Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, MyungJoo Ham On 11/13/2012 10:24 AM, Borislav Petkov wrote: > On Mon, Nov 12, 2012 at 10:35:49AM -0600, Mark Langsdorf wrote: >> The function is buried pretty deep in the cpufreq_stat code. It didn't >> seem appropriate to make it a function pointer as part of struct >> cpufreq_driver. > > Right, what's cpufreq-speak for > > if (Calxeda) > shift by 10 > > ? > > Better yet, add a flag or a bitfield called "minimize_jitter" or similar > and set it only on your hardware... Doing it in two passes has a similar effect: systems that have exact frequencies will get caught in the first pass when the values match. But adding a flag makes sense. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* RE: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-13 16:33 ` Mark Langsdorf @ 2012-11-13 19:13 ` Mark Langsdorf 2012-11-17 14:50 ` Borislav Petkov 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-13 19:13 UTC (permalink / raw) To: Mark Langsdorf, Borislav Petkov Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, MyungJoo Ham From: linux-pm-owner@vger.kernel.org [linux-pm-owner@vger.kernel.org] On Behalf Of Mark Langsdorf [mark.langsdorf@calxeda.com] > On 11/13/2012 10:24 AM, Borislav Petkov wrote: >> On Mon, Nov 12, 2012 at 10:35:49AM -0600, Mark Langsdorf wrote: >>> The function is buried pretty deep in the cpufreq_stat code. It didn't >>> seem appropriate to make it a function pointer as part of struct >>> cpufreq_driver. >> >> Better yet, add a flag or a bitfield called "minimize_jitter" or similar >> and set it only on your hardware... > > Doing it in two passes has a similar effect: systems that have exact > frequencies will get caught in the first pass when the values match. But > adding a flag makes sense. I went back and looked at implementing this suggestion. Although cpufreq_driver has a flag field, no part of cpufreq_driver is directly passed to the cpufreq_stat code. Only cpufreq_policy is. It's cleaner to do passes of the while loop than to copy the cpufreq_driver.flag field into cpufreq_policy and then store it again in cpufreq_stats. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-13 19:13 ` Mark Langsdorf @ 2012-11-17 14:50 ` Borislav Petkov 2012-11-24 10:05 ` Rafael J. Wysocki 0 siblings, 1 reply; 120+ messages in thread From: Borislav Petkov @ 2012-11-17 14:50 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, MyungJoo Ham On Tue, Nov 13, 2012 at 02:13:38PM -0500, Mark Langsdorf wrote: > Although cpufreq_driver has a flag field, no part of cpufreq_driver > is directly passed to the cpufreq_stat code. Only cpufreq_policy > is. It's cleaner to do passes of the while loop than to copy the > cpufreq_driver.flag field into cpufreq_policy and then store it again > in cpufreq_stats. That maybe so but this newly added loop which is only Calxeda-relevant is called in cpufreq_stat_notifier_trans, which is the frequency change notifier call, AFAICT. So each cpufreq driver will be paying that small and needless penalty now for nothing and on each frequency change. Which adds to the kernel-wide bloat and we absolutely don't want that. So you probably need to find a slick way of detecting calxeda hw somewhere along the init path of cpufreq_stats_init and set a hw-specific flag instead of adding that cost to each driver. Thanks. -- Regards/Gruss, Boris. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-17 14:50 ` Borislav Petkov @ 2012-11-24 10:05 ` Rafael J. Wysocki 2012-11-26 13:57 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Rafael J. Wysocki @ 2012-11-24 10:05 UTC (permalink / raw) To: Mark Langsdorf Cc: Borislav Petkov, linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, MyungJoo Ham On Saturday, November 17, 2012 03:50:48 PM Borislav Petkov wrote: > On Tue, Nov 13, 2012 at 02:13:38PM -0500, Mark Langsdorf wrote: > > Although cpufreq_driver has a flag field, no part of cpufreq_driver > > is directly passed to the cpufreq_stat code. Only cpufreq_policy > > is. It's cleaner to do passes of the while loop than to copy the > > cpufreq_driver.flag field into cpufreq_policy and then store it again > > in cpufreq_stats. > > That maybe so but this newly added loop which is only Calxeda-relevant > is called in cpufreq_stat_notifier_trans, which is the frequency change > notifier call, AFAICT. > > So each cpufreq driver will be paying that small and needless penalty > now for nothing and on each frequency change. Which adds to the > kernel-wide bloat and we absolutely don't want that. > > So you probably need to find a slick way of detecting calxeda hw > somewhere along the init path of cpufreq_stats_init and set a > hw-specific flag instead of adding that cost to each driver. Mark, I suppose you'd like me to take this series for v3.8, but the above comment from Boris has to be addressed for that. Thanks, Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-24 10:05 ` Rafael J. Wysocki @ 2012-11-26 13:57 ` Mark Langsdorf 2012-11-26 15:25 ` Rafael J. Wysocki 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-26 13:57 UTC (permalink / raw) To: Rafael J. Wysocki Cc: Borislav Petkov, linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, MyungJoo Ham On 11/24/2012 04:05 AM, Rafael J. Wysocki wrote: > On Saturday, November 17, 2012 03:50:48 PM Borislav Petkov wrote: >> On Tue, Nov 13, 2012 at 02:13:38PM -0500, Mark Langsdorf wrote: >>> Although cpufreq_driver has a flag field, no part of cpufreq_driver >>> is directly passed to the cpufreq_stat code. Only cpufreq_policy >>> is. It's cleaner to do passes of the while loop than to copy the >>> cpufreq_driver.flag field into cpufreq_policy and then store it again >>> in cpufreq_stats. >> >> That maybe so but this newly added loop which is only Calxeda-relevant >> is called in cpufreq_stat_notifier_trans, which is the frequency change >> notifier call, AFAICT. Drivers only go through the loop if they can't find an exact frequency. So every driver that isn't Calxeda shouldn't see the issue. >> So you probably need to find a slick way of detecting calxeda hw >> somewhere along the init path of cpufreq_stats_init and set a >> hw-specific flag instead of adding that cost to each driver. > > Mark, I suppose you'd like me to take this series for v3.8, but the above > comment from Boris has to be addressed for that. I think I'd rather drop this particular patch and not have cpufreq_stat support for Highbank. Redesigning it to meet Boris' requirements is going to take more time than I currently have available. Would it be acceptable to drop this patch and fix the issues with patches 4 and 6 to get the series in? --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats 2012-11-26 13:57 ` Mark Langsdorf @ 2012-11-26 15:25 ` Rafael J. Wysocki 0 siblings, 0 replies; 120+ messages in thread From: Rafael J. Wysocki @ 2012-11-26 15:25 UTC (permalink / raw) To: linux-pm Cc: Mark Langsdorf, Borislav Petkov, linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, MyungJoo Ham On Monday, November 26, 2012 07:57:30 AM Mark Langsdorf wrote: > On 11/24/2012 04:05 AM, Rafael J. Wysocki wrote: > > On Saturday, November 17, 2012 03:50:48 PM Borislav Petkov wrote: > >> On Tue, Nov 13, 2012 at 02:13:38PM -0500, Mark Langsdorf wrote: > >>> Although cpufreq_driver has a flag field, no part of cpufreq_driver > >>> is directly passed to the cpufreq_stat code. Only cpufreq_policy > >>> is. It's cleaner to do passes of the while loop than to copy the > >>> cpufreq_driver.flag field into cpufreq_policy and then store it again > >>> in cpufreq_stats. > >> > >> That maybe so but this newly added loop which is only Calxeda-relevant > >> is called in cpufreq_stat_notifier_trans, which is the frequency change > >> notifier call, AFAICT. > > Drivers only go through the loop if they can't find an exact frequency. > So every driver that isn't Calxeda shouldn't see the issue. > > >> So you probably need to find a slick way of detecting calxeda hw > >> somewhere along the init path of cpufreq_stats_init and set a > >> hw-specific flag instead of adding that cost to each driver. > > > > Mark, I suppose you'd like me to take this series for v3.8, but the above > > comment from Boris has to be addressed for that. > > I think I'd rather drop this particular patch and not have cpufreq_stat > support for Highbank. Redesigning it to meet Boris' requirements is > going to take more time than I currently have available. > > Would it be acceptable to drop this patch and fix the issues with > patches 4 and 6 to get the series in? Yes, it would, but please resubmit ASAP. Thanks, Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/6 v4] arm highbank: add support for pl320 IPC 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf ` (2 preceding siblings ...) 2012-11-07 18:32 ` [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-11-07 18:32 ` Mark Langsdorf 2012-11-14 14:03 ` Rob Herring 2012-11-07 18:32 ` [PATCH 5/6 v4] power: export opp cpufreq functions Mark Langsdorf ` (2 subsequent siblings) 6 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:32 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm; +Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> --- Changes from v3, v2 None Changes from v1 Removed erroneous changes for cpufreq Kconfig arch/arm/include/asm/pl320-ipc.h | 20 ++ arch/arm/mach-highbank/Makefile | 2 + arch/arm/mach-highbank/include/mach/pl320-ipc.h | 20 ++ arch/arm/mach-highbank/pl320-ipc.c | 232 ++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 arch/arm/include/asm/pl320-ipc.h create mode 100644 arch/arm/mach-highbank/include/mach/pl320-ipc.h create mode 100644 arch/arm/mach-highbank/pl320-ipc.c diff --git a/arch/arm/include/asm/pl320-ipc.h b/arch/arm/include/asm/pl320-ipc.h new file mode 100644 index 0000000..a0e58ee --- /dev/null +++ b/arch/arm/include/asm/pl320-ipc.h @@ -0,0 +1,20 @@ +/* + * Copyright 2010 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +int ipc_call_fast(u32 *data); +int ipc_call_slow(u32 *data); + +extern int pl320_ipc_register_notifier(struct notifier_block *nb); +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); diff --git a/arch/arm/mach-highbank/Makefile b/arch/arm/mach-highbank/Makefile index 3ec8bdd..b894708 100644 --- a/arch/arm/mach-highbank/Makefile +++ b/arch/arm/mach-highbank/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_DEBUG_HIGHBANK_UART) += lluart.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_PM_SLEEP) += pm.o + +obj-y += pl320-ipc.o diff --git a/arch/arm/mach-highbank/include/mach/pl320-ipc.h b/arch/arm/mach-highbank/include/mach/pl320-ipc.h new file mode 100644 index 0000000..a0e58ee --- /dev/null +++ b/arch/arm/mach-highbank/include/mach/pl320-ipc.h @@ -0,0 +1,20 @@ +/* + * Copyright 2010 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +int ipc_call_fast(u32 *data); +int ipc_call_slow(u32 *data); + +extern int pl320_ipc_register_notifier(struct notifier_block *nb); +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); diff --git a/arch/arm/mach-highbank/pl320-ipc.c b/arch/arm/mach-highbank/pl320-ipc.c new file mode 100644 index 0000000..0eb92e4 --- /dev/null +++ b/arch/arm/mach-highbank/pl320-ipc.c @@ -0,0 +1,232 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <asm/pl320-ipc.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_FAST_MBOX 0 +#define IPC_SLOW_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_SPINLOCK(ipc_m0_lock); +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* non-blocking implementation from the A9 side, interrupt safe in theory */ +int ipc_call_fast(u32 *data) +{ + int timeout, ret; + + spin_lock(&ipc_m0_lock); + + __ipc_send(IPC_FAST_MBOX, data); + + for (timeout = 500; timeout > 0; timeout--) { + if (__raw_readl(ipc_base + IPCMxSEND(IPC_FAST_MBOX)) == 0x2) + break; + udelay(100); + } + if (timeout == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_FAST_MBOX, data); +out: + __raw_writel(0, ipc_base + IPCMxSEND(IPC_FAST_MBOX)); + spin_unlock(&ipc_m0_lock); + return ret; +} +EXPORT_SYMBOL(ipc_call_fast); + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int ipc_call_slow(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_SLOW_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_SLOW_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(ipc_call_slow); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_SLOW_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_SLOW_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_FAST_MBOX)); + __raw_writel(0, ipc_base + IPCMxSEND(IPC_SLOW_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init fast mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_FAST_MBOX)); + set_destination(M3_SOURCE, IPC_FAST_MBOX); + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_SLOW_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_SLOW_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_SLOW_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 4/6 v4] arm highbank: add support for pl320 IPC 2012-11-07 18:32 ` [PATCH 4/6 v4] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-11-14 14:03 ` Rob Herring 0 siblings, 0 replies; 120+ messages in thread From: Rob Herring @ 2012-11-14 14:03 UTC (permalink / raw) To: Mark Langsdorf; +Cc: linux-kernel, cpufreq, linux-pm On 11/07/2012 12:32 PM, Mark Langsdorf wrote: > From: Rob Herring <rob.herring@calxeda.com> > > The pl320 IPC allows for interprocessor communication between the highbank A9 > and the EnergyCore Management Engine. The pl320 implements a straightforward > mailbox protocol. > > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > --- > Changes from v3, v2 > None > Changes from v1 > Removed erroneous changes for cpufreq Kconfig > > arch/arm/include/asm/pl320-ipc.h | 20 ++ asm/hardware/ is probably more appropriate. > arch/arm/mach-highbank/Makefile | 2 + > arch/arm/mach-highbank/include/mach/pl320-ipc.h | 20 ++ Need to delete this file. > arch/arm/mach-highbank/pl320-ipc.c | 232 ++++++++++++++++++++++++ > 4 files changed, 274 insertions(+) > create mode 100644 arch/arm/include/asm/pl320-ipc.h > create mode 100644 arch/arm/mach-highbank/include/mach/pl320-ipc.h > create mode 100644 arch/arm/mach-highbank/pl320-ipc.c > > diff --git a/arch/arm/include/asm/pl320-ipc.h b/arch/arm/include/asm/pl320-ipc.h > new file mode 100644 > index 0000000..a0e58ee > --- /dev/null > +++ b/arch/arm/include/asm/pl320-ipc.h > @@ -0,0 +1,20 @@ > +/* > + * Copyright 2010 Calxeda, Inc. Update copyright. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > +int ipc_call_fast(u32 *data); We should get rid of fast and slow channels and just have a single tx channel as it is all the same and we don't use the fast channel. > +int ipc_call_slow(u32 *data); > + > +extern int pl320_ipc_register_notifier(struct notifier_block *nb); > +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); > diff --git a/arch/arm/mach-highbank/Makefile b/arch/arm/mach-highbank/Makefile > index 3ec8bdd..b894708 100644 > --- a/arch/arm/mach-highbank/Makefile > +++ b/arch/arm/mach-highbank/Makefile > @@ -7,3 +7,5 @@ obj-$(CONFIG_DEBUG_HIGHBANK_UART) += lluart.o > obj-$(CONFIG_SMP) += platsmp.o > obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o > obj-$(CONFIG_PM_SLEEP) += pm.o > + > +obj-y += pl320-ipc.o > diff --git a/arch/arm/mach-highbank/include/mach/pl320-ipc.h b/arch/arm/mach-highbank/include/mach/pl320-ipc.h > new file mode 100644 > index 0000000..a0e58ee > --- /dev/null > +++ b/arch/arm/mach-highbank/include/mach/pl320-ipc.h > @@ -0,0 +1,20 @@ > +/* > + * Copyright 2010 Calxeda, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > +int ipc_call_fast(u32 *data); > +int ipc_call_slow(u32 *data); > + > +extern int pl320_ipc_register_notifier(struct notifier_block *nb); > +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); > diff --git a/arch/arm/mach-highbank/pl320-ipc.c b/arch/arm/mach-highbank/pl320-ipc.c > new file mode 100644 > index 0000000..0eb92e4 > --- /dev/null > +++ b/arch/arm/mach-highbank/pl320-ipc.c > @@ -0,0 +1,232 @@ > +/* > + * Copyright 2012 Calxeda, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > +#include <linux/types.h> > +#include <linux/err.h> > +#include <linux/delay.h> > +#include <linux/export.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/completion.h> > +#include <linux/mutex.h> > +#include <linux/notifier.h> > +#include <linux/spinlock.h> > +#include <linux/device.h> > +#include <linux/amba/bus.h> > + > +#include <asm/pl320-ipc.h> > + > +#define IPCMxSOURCE(m) ((m) * 0x40) > +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) > +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) > +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) > +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) > +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) > +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) > +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) > +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) > +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) > + > +#define IPCMMIS(irq) (((irq) * 8) + 0x800) > +#define IPCMRIS(irq) (((irq) * 8) + 0x804) > + > +#define MBOX_MASK(n) (1 << (n)) > +#define IPC_FAST_MBOX 0 > +#define IPC_SLOW_MBOX 1 > +#define IPC_RX_MBOX 2 > + > +#define CHAN_MASK(n) (1 << (n)) > +#define A9_SOURCE 1 > +#define M3_SOURCE 0 > + > +static void __iomem *ipc_base; > +static int ipc_irq; > +static DEFINE_SPINLOCK(ipc_m0_lock); > +static DEFINE_MUTEX(ipc_m1_lock); > +static DECLARE_COMPLETION(ipc_completion); > +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); > + > +static inline void set_destination(int source, int mbox) > +{ > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); > +} > + > +static inline void clear_destination(int source, int mbox) > +{ > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); > +} > + > +static void __ipc_send(int mbox, u32 *data) > +{ > + int i; > + for (i = 0; i < 7; i++) > + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); > + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); > +} > + > +static u32 __ipc_rcv(int mbox, u32 *data) > +{ > + int i; > + for (i = 0; i < 7; i++) > + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); > + return data[1]; > +} > + > +/* non-blocking implementation from the A9 side, interrupt safe in theory */ > +int ipc_call_fast(u32 *data) > +{ > + int timeout, ret; > + > + spin_lock(&ipc_m0_lock); > + > + __ipc_send(IPC_FAST_MBOX, data); > + > + for (timeout = 500; timeout > 0; timeout--) { > + if (__raw_readl(ipc_base + IPCMxSEND(IPC_FAST_MBOX)) == 0x2) > + break; > + udelay(100); > + } > + if (timeout == 0) { > + ret = -ETIMEDOUT; > + goto out; > + } > + > + ret = __ipc_rcv(IPC_FAST_MBOX, data); > +out: > + __raw_writel(0, ipc_base + IPCMxSEND(IPC_FAST_MBOX)); > + spin_unlock(&ipc_m0_lock); > + return ret; > +} > +EXPORT_SYMBOL(ipc_call_fast); > + > +/* blocking implmentation from the A9 side, not usuable in interrupts! */ > +int ipc_call_slow(u32 *data) > +{ > + int ret; > + > + mutex_lock(&ipc_m1_lock); > + > + init_completion(&ipc_completion); > + __ipc_send(IPC_SLOW_MBOX, data); > + ret = wait_for_completion_timeout(&ipc_completion, > + msecs_to_jiffies(1000)); > + if (ret == 0) { > + ret = -ETIMEDOUT; > + goto out; > + } > + > + ret = __ipc_rcv(IPC_SLOW_MBOX, data); > +out: > + mutex_unlock(&ipc_m1_lock); > + return ret; > +} > +EXPORT_SYMBOL(ipc_call_slow); > + > +irqreturn_t ipc_handler(int irq, void *dev) > +{ > + u32 irq_stat; > + u32 data[7]; > + > + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); > + if (irq_stat & MBOX_MASK(IPC_SLOW_MBOX)) { > + __raw_writel(0, ipc_base + IPCMxSEND(IPC_SLOW_MBOX)); > + complete(&ipc_completion); > + } > + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { > + __ipc_rcv(IPC_RX_MBOX, data); > + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); > + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); > + } > + > + return IRQ_HANDLED; > +} > + > +int pl320_ipc_register_notifier(struct notifier_block *nb) > +{ > + return atomic_notifier_chain_register(&ipc_notifier, nb); > +} > + > +int pl320_ipc_unregister_notifier(struct notifier_block *nb) > +{ > + return atomic_notifier_chain_unregister(&ipc_notifier, nb); > +} > + > +static int __devinit pl320_probe(struct amba_device *adev, > + const struct amba_id *id) > +{ > + int ret; > + > + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); > + if (ipc_base == NULL) > + return -ENOMEM; > + > + __raw_writel(0, ipc_base + IPCMxSEND(IPC_FAST_MBOX)); > + __raw_writel(0, ipc_base + IPCMxSEND(IPC_SLOW_MBOX)); > + > + ipc_irq = adev->irq[0]; > + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); > + if (ret < 0) > + goto err; > + > + /* Init fast mailbox */ > + __raw_writel(CHAN_MASK(A9_SOURCE), > + ipc_base + IPCMxSOURCE(IPC_FAST_MBOX)); > + set_destination(M3_SOURCE, IPC_FAST_MBOX); > + > + /* Init slow mailbox */ > + __raw_writel(CHAN_MASK(A9_SOURCE), > + ipc_base + IPCMxSOURCE(IPC_SLOW_MBOX)); > + __raw_writel(CHAN_MASK(M3_SOURCE), > + ipc_base + IPCMxDSET(IPC_SLOW_MBOX)); > + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), > + ipc_base + IPCMxMSET(IPC_SLOW_MBOX)); > + > + /* Init receive mailbox */ > + __raw_writel(CHAN_MASK(M3_SOURCE), > + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); > + __raw_writel(CHAN_MASK(A9_SOURCE), > + ipc_base + IPCMxDSET(IPC_RX_MBOX)); > + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), > + ipc_base + IPCMxMSET(IPC_RX_MBOX)); > + > + return 0; > +err: > + iounmap(ipc_base); > + return ret; > +} > + > +static struct amba_id pl320_ids[] = { > + { > + .id = 0x00041320, > + .mask = 0x000fffff, > + }, > + { 0, 0 }, > +}; > + > +static struct amba_driver pl320_driver = { > + .drv = { > + .name = "pl320", > + }, > + .id_table = pl320_ids, > + .probe = pl320_probe, > +}; > + > +static int __init ipc_init(void) > +{ > + return amba_driver_register(&pl320_driver); > +} > +module_init(ipc_init); > ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 5/6 v4] power: export opp cpufreq functions 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf ` (3 preceding siblings ...) 2012-11-07 18:32 ` [PATCH 4/6 v4] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-11-07 18:32 ` Mark Langsdorf [not found] ` <1352313166-28980-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 2012-11-24 10:07 ` [PATCH 0/6 v4] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:32 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm; +Cc: Mark Langsdorf These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> --- Changes from v3 includes linux/export.h instead of module.h Changes from v2 None. Changes from v1 Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..4062ec3 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/export.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
[parent not found: <1352313166-28980-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>]
* [PATCH 6/6 v4] cpufreq, highbank: add support for highbank cpufreq [not found] ` <1352313166-28980-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> @ 2012-11-07 18:32 ` Mark Langsdorf [not found] ` <1352313166-28980-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-07 18:32 UTC (permalink / raw) To: linux-kernel-u79uwXL29TY76Z2rM5mHXA, cpufreq-u79uwXL29TY76Z2rM5mHXA, linux-pm-u79uwXL29TY76Z2rM5mHXA Cc: Rafael J. Wysocki, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Langsdorf Highbank processors depend on the external ECME to perform voltage management based on a requested frequency. Communication between the highbank and ECME cores happens over the pl320 IPC channel. Signed-off-by: Mark Langsdorf <mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org Cc: Rafael J. Wysocki <rjw-KKrjLPT3xs0@public.gmane.org> --- Changes from v3 None Changes from v2 Changed transition latency binding in code to match documentation Changes from v1 Added highbank specific Kconfig changes .../bindings/cpufreq/highbank-cpufreq.txt | 53 +++++ arch/arm/Kconfig | 2 + arch/arm/boot/dts/highbank.dts | 10 + arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 15 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 229 +++++++++++++++++++++ 7 files changed, 312 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt new file mode 100644 index 0000000..3ec2cec --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt @@ -0,0 +1,53 @@ +Highbank cpufreq driver + +This is cpufreq driver for Calxeda ECX-1000 (highbank) processor. It is based +on the generic cpu0 driver and uses a similar format for bindings. Since +the EnergyCore Management Engine maintains the voltage based on the +frequency, the voltage component of the operating points can be set to any +arbitrary values. + +Both required properties listed below must be defined under node /cpus/cpu@0. + +Required properties: +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt + for details +- transition-latency: Specify the possible maximum transition latency for clock, + in unit of nanoseconds. + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz ignored */ + 790000 1000000 + 396000 1000000 + 198000 1000000 + >; + transition-latency = <200000>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ade7e92..4ed0b7b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -391,6 +391,8 @@ config ARCH_SIRF select PINCTRL select PINCTRL_SIRF select USE_OF + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP help Support for CSR SiRFprimaII/Marco/Polo platforms diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 0c6fc34..7c4c27d 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + transition-latency = <100000>; }; cpu@1 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 0e1d0a4..ee83af6 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -13,3 +13,5 @@ config ARCH_HIGHBANK select HAVE_SMP select SPARSE_IRQ select USE_OF + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e64..bc3ef55 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,18 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select HAVE_CLK + select PM_OPP + select OF + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1bc90e1..9e8f12a 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..a167073 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * derived from cpufreq-cpu0 by Freescale Semiconductor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/slab.h> +#include <asm/pl320-ipc.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 + +static unsigned int transition_latency; + +static struct device *cpu_dev; +static struct clk *cpu_clk; +static struct cpufreq_frequency_table *freq_table; + +static int hb_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +static unsigned int hb_get_speed(unsigned int cpu) +{ + return clk_get_rate(cpu_clk) / 1000; +} + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[7]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000; + for (i = 2; i < 7; i++) + msg[i] = 0; + + return ipc_call_slow(msg); +} + +static int hb_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + unsigned long freq_Hz; + unsigned int index, cpu; + int ret; + + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &index); + if (ret) { + pr_err("failed to match target freqency %d: %d\n", + target_freq, ret); + return ret; + } + + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); + if (freq_Hz < 0) + freq_Hz = freq_table[index].frequency * 1000; + freqs.new = freq_Hz / 1000; + freqs.old = clk_get_rate(cpu_clk) / 1000; + + if (freqs.old == freqs.new) + return 0; + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } + + pr_debug("%u MHz --> %u MHz\n", freqs.old / 1000, freqs.new / 1000); + + /* scaling up? scale voltage before frequency */ + if (freqs.new > freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + freqs.new = freqs.old; + return -EAGAIN; + } + } + + ret = clk_set_rate(cpu_clk, freqs.new * 1000); + if (ret) { + pr_err("failed to set clock rate: %d\n", ret); + hb_voltage_change(freqs.old); + return ret; + } + + /* scaling down? scale voltage after frequency */ + if (freqs.new < freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + if (clk_set_rate(cpu_clk, freqs.old * 1000)) + pr_err("also failed to reset freq\n"); + freqs.new = freqs.old; + return -EAGAIN; + } + } + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + + return 0; +} + +static int hb_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + + if (policy->cpu != 0) + return -EINVAL; + + ret = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (ret) { + pr_err("invalid frequency table: %d\n", ret); + return ret; + } + + policy->cpuinfo.transition_latency = transition_latency; + policy->cur = clk_get_rate(cpu_clk) / 1000; + + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; + cpumask_setall(policy->cpus); + + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + + return 0; +} + +static int hb_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + + return 0; +} + +static struct freq_attr *hb_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver hb_cpufreq_driver = { + .flags = CPUFREQ_STICKY, + .verify = hb_verify_speed, + .target = hb_set_target, + .get = hb_get_speed, + .init = hb_cpufreq_init, + .exit = hb_cpufreq_exit, + .name = "highbank-cpufreq", + .attr = hb_cpufreq_attr, +}; + +static int __devinit hb_cpufreq_driver_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = of_init_opp_table(cpu_dev); + if (ret) { + pr_err("failed to init OPP table: %d\n", ret); + goto out_put_node; + } + + ret = opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table: %d\n", ret); + goto out_put_node; + } + + if (of_property_read_u32(np, "transition-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + ret = cpufreq_register_driver(&hb_cpufreq_driver); + if (ret) { + pr_err("failed register driver: %d\n", ret); + goto out_free_table; + } + + of_node_put(np); + return 0; + +out_free_table: + opp_free_cpufreq_table(cpu_dev, &freq_table); +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
[parent not found: <1352313166-28980-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>]
* Re: [PATCH 6/6 v4] cpufreq, highbank: add support for highbank cpufreq [not found] ` <1352313166-28980-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> @ 2012-11-07 18:51 ` Rob Herring 0 siblings, 0 replies; 120+ messages in thread From: Rob Herring @ 2012-11-07 18:51 UTC (permalink / raw) To: Mark Langsdorf Cc: Rafael J. Wysocki, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, linux-kernel-u79uwXL29TY76Z2rM5mHXA, cpufreq-u79uwXL29TY76Z2rM5mHXA, linux-pm-u79uwXL29TY76Z2rM5mHXA On 11/07/2012 12:32 PM, Mark Langsdorf wrote: > Highbank processors depend on the external ECME to perform voltage > management based on a requested frequency. Communication between the > highbank and ECME cores happens over the pl320 IPC channel. > > Signed-off-by: Mark Langsdorf <mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> > Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org > Cc: Rafael J. Wysocki <rjw-KKrjLPT3xs0@public.gmane.org> > --- > Changes from v3 > None > Changes from v2 > Changed transition latency binding in code to match documentation > Changes from v1 > Added highbank specific Kconfig changes > > .../bindings/cpufreq/highbank-cpufreq.txt | 53 +++++ > arch/arm/Kconfig | 2 + > arch/arm/boot/dts/highbank.dts | 10 + > arch/arm/mach-highbank/Kconfig | 2 + > drivers/cpufreq/Kconfig.arm | 15 ++ > drivers/cpufreq/Makefile | 1 + > drivers/cpufreq/highbank-cpufreq.c | 229 +++++++++++++++++++++ > 7 files changed, 312 insertions(+) > create mode 100644 Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt > create mode 100644 drivers/cpufreq/highbank-cpufreq.c > > diff --git a/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt > new file mode 100644 > index 0000000..3ec2cec > --- /dev/null > +++ b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt > @@ -0,0 +1,53 @@ > +Highbank cpufreq driver > + > +This is cpufreq driver for Calxeda ECX-1000 (highbank) processor. It is based > +on the generic cpu0 driver and uses a similar format for bindings. Since > +the EnergyCore Management Engine maintains the voltage based on the > +frequency, the voltage component of the operating points can be set to any > +arbitrary values. > + > +Both required properties listed below must be defined under node /cpus/cpu@0. > + > +Required properties: > +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt > + for details > +- transition-latency: Specify the possible maximum transition latency for clock, > + in unit of nanoseconds. > + > +Examples: > + > +cpus { > + #address-cells = <1>; > + #size-cells = <0>; > + > + cpu@0 { > + compatible = "arm,cortex-a9"; > + reg = <0>; > + next-level-cache = <&L2>; > + operating-points = < > + /* kHz ignored */ > + 790000 1000000 > + 396000 1000000 > + 198000 1000000 > + >; > + transition-latency = <200000>; > + }; > + > + cpu@1 { > + compatible = "arm,cortex-a9"; > + reg = <1>; > + next-level-cache = <&L2>; > + }; > + > + cpu@2 { > + compatible = "arm,cortex-a9"; > + reg = <2>; > + next-level-cache = <&L2>; > + }; > + > + cpu@3 { > + compatible = "arm,cortex-a9"; > + reg = <3>; > + next-level-cache = <&L2>; > + }; > +}; > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index ade7e92..4ed0b7b 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -391,6 +391,8 @@ config ARCH_SIRF > select PINCTRL > select PINCTRL_SIRF > select USE_OF > + select ARCH_HAS_CPUFREQ > + select ARCH_HAS_OPP This hunk needs to be removed. > help > Support for CSR SiRFprimaII/Marco/Polo platforms > > diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts > index 0c6fc34..7c4c27d 100644 > --- a/arch/arm/boot/dts/highbank.dts > +++ b/arch/arm/boot/dts/highbank.dts > @@ -36,6 +36,16 @@ > next-level-cache = <&L2>; > clocks = <&a9pll>; > clock-names = "cpu"; > + operating-points = < > + /* kHz ignored */ > + 1300000 1000000 > + 1200000 1000000 > + 1100000 1000000 > + 800000 1000000 > + 400000 1000000 > + 200000 1000000 > + >; > + transition-latency = <100000>; > }; > > cpu@1 { > diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig > index 0e1d0a4..ee83af6 100644 > --- a/arch/arm/mach-highbank/Kconfig > +++ b/arch/arm/mach-highbank/Kconfig > @@ -13,3 +13,5 @@ config ARCH_HIGHBANK > select HAVE_SMP > select SPARSE_IRQ > select USE_OF > + select ARCH_HAS_CPUFREQ > + select ARCH_HAS_OPP Sort these alphabetically. > diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm > index 5961e64..bc3ef55 100644 > --- a/drivers/cpufreq/Kconfig.arm > +++ b/drivers/cpufreq/Kconfig.arm > @@ -76,3 +76,18 @@ config ARM_EXYNOS5250_CPUFREQ > help > This adds the CPUFreq driver for Samsung EXYNOS5250 > SoC. > + > +config ARM_HIGHBANK_CPUFREQ > + tristate "Calxeda Highbank-based" > + depends on ARCH_HIGHBANK > + select CPU_FREQ_TABLE > + select HAVE_CLK ARCH_HIGHBANK already selects this. > + select PM_OPP > + select OF And this. > + default m > + help > + This adds the CPUFreq driver for Calxeda Highbank SoC > + based boards. > + > + If in doubt, say N. > + > diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile > index 1bc90e1..9e8f12a 100644 > --- a/drivers/cpufreq/Makefile > +++ b/drivers/cpufreq/Makefile > @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o > obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o > obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o > obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o > +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o > > ################################################################################## > # PowerPC platform drivers > diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c > new file mode 100644 > index 0000000..a167073 > --- /dev/null > +++ b/drivers/cpufreq/highbank-cpufreq.c > @@ -0,0 +1,229 @@ > +/* > + * Copyright (C) 2012 Calxeda, Inc. > + * > + * derived from cpufreq-cpu0 by Freescale Semiconductor > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/clk.h> > +#include <linux/cpu.h> > +#include <linux/cpufreq.h> > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/opp.h> > +#include <linux/slab.h> > +#include <asm/pl320-ipc.h> > + > +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 > + > +static unsigned int transition_latency; > + > +static struct device *cpu_dev; > +static struct clk *cpu_clk; > +static struct cpufreq_frequency_table *freq_table; > + > +static int hb_verify_speed(struct cpufreq_policy *policy) > +{ > + return cpufreq_frequency_table_verify(policy, freq_table); > +} > + > +static unsigned int hb_get_speed(unsigned int cpu) > +{ > + return clk_get_rate(cpu_clk) / 1000; > +} > + > +static int hb_voltage_change(unsigned int freq) > +{ > + int i; > + u32 msg[7]; > + > + msg[0] = HB_CPUFREQ_CHANGE_NOTE; > + msg[1] = freq / 1000; > + for (i = 2; i < 7; i++) > + msg[i] = 0; > + > + return ipc_call_slow(msg); > +} > + > +static int hb_set_target(struct cpufreq_policy *policy, > + unsigned int target_freq, unsigned int relation) > +{ > + struct cpufreq_freqs freqs; > + unsigned long freq_Hz; > + unsigned int index, cpu; > + int ret; > + > + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, > + relation, &index); > + if (ret) { > + pr_err("failed to match target freqency %d: %d\n", > + target_freq, ret); > + return ret; > + } > + > + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); > + if (freq_Hz < 0) > + freq_Hz = freq_table[index].frequency * 1000; > + freqs.new = freq_Hz / 1000; > + freqs.old = clk_get_rate(cpu_clk) / 1000; > + > + if (freqs.old == freqs.new) > + return 0; > + > + for_each_online_cpu(cpu) { > + freqs.cpu = cpu; > + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); > + } > + > + pr_debug("%u MHz --> %u MHz\n", freqs.old / 1000, freqs.new / 1000); > + > + /* scaling up? scale voltage before frequency */ > + if (freqs.new > freqs.old) { > + ret = hb_voltage_change(freqs.new); > + if (ret) { > + freqs.new = freqs.old; > + return -EAGAIN; > + } > + } > + > + ret = clk_set_rate(cpu_clk, freqs.new * 1000); > + if (ret) { > + pr_err("failed to set clock rate: %d\n", ret); > + hb_voltage_change(freqs.old); > + return ret; > + } > + > + /* scaling down? scale voltage after frequency */ > + if (freqs.new < freqs.old) { > + ret = hb_voltage_change(freqs.new); > + if (ret) { > + if (clk_set_rate(cpu_clk, freqs.old * 1000)) > + pr_err("also failed to reset freq\n"); > + freqs.new = freqs.old; > + return -EAGAIN; > + } > + } > + > + for_each_online_cpu(cpu) { > + freqs.cpu = cpu; > + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); > + } > + > + return 0; > +} > + > +static int hb_cpufreq_init(struct cpufreq_policy *policy) > +{ > + int ret; > + > + if (policy->cpu != 0) > + return -EINVAL; > + > + ret = cpufreq_frequency_table_cpuinfo(policy, freq_table); > + if (ret) { > + pr_err("invalid frequency table: %d\n", ret); > + return ret; > + } > + > + policy->cpuinfo.transition_latency = transition_latency; > + policy->cur = clk_get_rate(cpu_clk) / 1000; > + > + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; > + cpumask_setall(policy->cpus); > + > + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); > + > + return 0; > +} > + > +static int hb_cpufreq_exit(struct cpufreq_policy *policy) > +{ > + cpufreq_frequency_table_put_attr(policy->cpu); > + > + return 0; > +} > + > +static struct freq_attr *hb_cpufreq_attr[] = { > + &cpufreq_freq_attr_scaling_available_freqs, > + NULL, > +}; > + > +static struct cpufreq_driver hb_cpufreq_driver = { > + .flags = CPUFREQ_STICKY, > + .verify = hb_verify_speed, > + .target = hb_set_target, > + .get = hb_get_speed, > + .init = hb_cpufreq_init, > + .exit = hb_cpufreq_exit, > + .name = "highbank-cpufreq", > + .attr = hb_cpufreq_attr, > +}; > + > +static int __devinit hb_cpufreq_driver_init(void) > +{ > + struct device_node *np; > + int ret; > + > + np = of_find_node_by_path("/cpus/cpu@0"); > + if (!np) { > + pr_err("failed to find highbank cpufreq node\n"); > + return -ENOENT; > + } > + > + cpu_dev = get_cpu_device(0); > + if (!cpu_dev) { > + pr_err("failed to get highbank cpufreq device\n"); > + ret = -ENODEV; > + goto out_put_node; > + } > + > + cpu_dev->of_node = np; > + > + cpu_clk = clk_get(cpu_dev, NULL); > + if (IS_ERR(cpu_clk)) { > + ret = PTR_ERR(cpu_clk); > + pr_err("failed to get cpu0 clock: %d\n", ret); > + goto out_put_node; > + } > + > + ret = of_init_opp_table(cpu_dev); > + if (ret) { > + pr_err("failed to init OPP table: %d\n", ret); > + goto out_put_node; > + } > + > + ret = opp_init_cpufreq_table(cpu_dev, &freq_table); > + if (ret) { > + pr_err("failed to init cpufreq table: %d\n", ret); > + goto out_put_node; > + } > + > + if (of_property_read_u32(np, "transition-latency", &transition_latency)) > + transition_latency = CPUFREQ_ETERNAL; > + > + ret = cpufreq_register_driver(&hb_cpufreq_driver); > + if (ret) { > + pr_err("failed register driver: %d\n", ret); > + goto out_free_table; > + } > + > + of_node_put(np); > + return 0; > + > +out_free_table: > + opp_free_cpufreq_table(cpu_dev, &freq_table); > +out_put_node: > + of_node_put(np); > + return ret; > +} > +late_initcall(hb_cpufreq_driver_init); > + > +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>"); > +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); > +MODULE_LICENSE("GPL"); > ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/6 v4] cpufreq: add support for Calxeda ECX-1000 (highbank) 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf ` (5 preceding siblings ...) [not found] ` <1352313166-28980-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> @ 2012-11-24 10:07 ` Rafael J. Wysocki 6 siblings, 0 replies; 120+ messages in thread From: Rafael J. Wysocki @ 2012-11-24 10:07 UTC (permalink / raw) To: Mark Langsdorf; +Cc: linux-kernel, cpufreq, linux-pm Hi Mark, On Wednesday, November 07, 2012 12:32:40 PM Mark Langsdorf wrote: > This patch series adds cpufreq support for the Calxeda > ECX-1000 (highbank) SoCs. The driver is based on the > cpufreq-cpu0 driver. Because of the unique way that > highbank uses the EnergyCore Management Engine to manage > voltages, it was not possible to use the cpufreq-cpu0 driver. Please address the reviewers' comments for patches [3-4/6] and [6/6]. Thanks, Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (4 preceding siblings ...) 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf @ 2012-11-27 15:04 ` Mark Langsdorf 2012-11-27 15:04 ` [PATCH 1/6 v5] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (6 more replies) 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf ` (6 subsequent siblings) 12 siblings, 7 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The driver is based on the cpufreq-cpu0 driver. Because of the unique way that highbank uses the EnergyCore Management Engine to manage voltages, it was not possible to use the cpufreq-cpu0 driver. --Mark Langsdorf ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/6 v5] arm: use devicetree to get smp_twd clock 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf @ 2012-11-27 15:04 ` Mark Langsdorf 2012-11-27 15:04 ` [PATCH 2/6 v5] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (5 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v4 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b22d700..af46b80 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -263,6 +266,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -273,12 +277,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) { struct clock_event_device **this_cpu_clk; - if (!twd_clk) - twd_clk = twd_get_clock(); - - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else + if (IS_ERR_OR_NULL(twd_clk)) twd_calibrate_rate(); __raw_writel(0, twd_base + TWD_TIMER_CONTROL); @@ -349,6 +348,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -383,6 +384,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/6 v5] clk, highbank: Prevent glitches in non-bypass reset mode 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf 2012-11-27 15:04 ` [PATCH 1/6 v5] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-11-27 15:04 ` Mark Langsdorf 2012-11-27 18:15 ` Mike Turquette 2012-11-27 15:04 ` [PATCH 3/6 v5] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf ` (4 subsequent siblings) 6 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, mturquette The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: mturquette@linaro.org --- Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1: Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 2/6 v5] clk, highbank: Prevent glitches in non-bypass reset mode 2012-11-27 15:04 ` [PATCH 2/6 v5] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-11-27 18:15 ` Mike Turquette 0 siblings, 0 replies; 120+ messages in thread From: Mike Turquette @ 2012-11-27 18:15 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm@vger.kernel.org, linux-arm-kernel, Rob Herring On Tue, Nov 27, 2012 at 7:04 AM, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote: > > The highbank clock will glitch with the current code if the > clock rate is reset without relocking the PLL. Program the PLL > correctly to prevent glitches. > > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > Cc: mturquette@linaro.org Acked-by: Mike Turquette <mturquette@linaro.org> Regards, Mike > --- > Changes from v4 > None. > Changes from v3 > Changelog text and patch name now correspond to the actual patch. > was clk, highbank: remove non-bypass reset mode. > Changes from v2 > None. > Changes from v1: > Removed erroneous reformating. > > drivers/clk/clk-highbank.c | 2 ++ > 1 file changed, 2 insertions(+) > > diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c > index 52fecad..3a0b723 100644 > --- a/drivers/clk/clk-highbank.c > +++ b/drivers/clk/clk-highbank.c > @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, > reg |= HB_PLL_EXT_ENA; > reg &= ~HB_PLL_EXT_BYPASS; > } else { > + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); > reg &= ~HB_PLL_DIVQ_MASK; > reg |= divq << HB_PLL_DIVQ_SHIFT; > + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); > } > writel(reg, hbclk->reg); > > -- > 1.7.11.7 > ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 3/6 v5] cpufreq: tolerate inexact values when collecting stats 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf 2012-11-27 15:04 ` [PATCH 1/6 v5] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-27 15:04 ` [PATCH 2/6 v5] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-11-27 15:04 ` Mark Langsdorf 2012-11-27 15:04 ` [PATCH 4/6 v5] arm highbank: add support for pl320 IPC Mark Langsdorf ` (3 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf This patch is withdrawn due to a need for severe rework. Changes from v4 Withdrawn. Changes from v3, v2 None. Changes from v1 Implemented a simple round-up algorithm instead of the over/under method that could cause errors on Intel processors with boost mode. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/6 v5] arm highbank: add support for pl320 IPC 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf ` (2 preceding siblings ...) 2012-11-27 15:04 ` [PATCH 3/6 v5] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-11-27 15:04 ` Mark Langsdorf 2012-11-27 16:12 ` Thomas Petazzoni 2012-11-27 15:04 ` [PATCH 5/6 v5] power: export opp cpufreq functions Mark Langsdorf ` (2 subsequent siblings) 6 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, Omar Ramirez Luna, Arnd Bergmann From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. This patch depends on Omar Ramirez Luna's <omar.luna@linaro.org> mailbox driver patch series. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 4 + drivers/mailbox/pl320-ipc.c | 197 +++++++++++++++++++++++++++++++++++++++++ include/linux/mailbox.h | 19 +++- 5 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 drivers/mailbox/Makefile create mode 100644 drivers/mailbox/pl320-ipc.c diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 0e1d0a4..2896881 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index be8cac0..e89fdb4 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -34,4 +34,13 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile new file mode 100644 index 0000000..c9f14c3 --- /dev/null +++ b/drivers/mailbox/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_OMAP1_MBOX) += mailbox.o mailbox-omap1.o +obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox.o mailbox-omap2.o +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o + diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..33eb97f --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,197 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h index e8e4131..ac50a03 100644 --- a/include/linux/mailbox.h +++ b/include/linux/mailbox.h @@ -1,4 +1,16 @@ -/* mailbox.h */ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ typedef u32 mbox_msg_t; struct omap_mbox; @@ -20,3 +32,8 @@ void omap_mbox_save_ctx(struct omap_mbox *mbox); void omap_mbox_restore_ctx(struct omap_mbox *mbox); void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); + +int ipc_transmit(u32 *data); + +extern int pl320_ipc_register_notifier(struct notifier_block *nb); +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 4/6 v5] arm highbank: add support for pl320 IPC 2012-11-27 15:04 ` [PATCH 4/6 v5] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-11-27 16:12 ` Thomas Petazzoni 2012-11-27 19:53 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Thomas Petazzoni @ 2012-11-27 16:12 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel, Omar Ramirez Luna, Arnd Bergmann, Rob Herring Dear Mark Langsdorf, On Tue, 27 Nov 2012 09:04:32 -0600, Mark Langsdorf wrote: > +int ipc_transmit(u32 *data); ipc_transmit() looks to me like a way to generic name to be exposed to the entire kernel. > +extern int pl320_ipc_register_notifier(struct notifier_block *nb); > +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); Why some "extern" here? You don't have these for the other functions in this header file. Thomas -- Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 4/6 v5] arm highbank: add support for pl320 IPC 2012-11-27 16:12 ` Thomas Petazzoni @ 2012-11-27 19:53 ` Mark Langsdorf 0 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 19:53 UTC (permalink / raw) To: Thomas Petazzoni Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Omar Ramirez Luna, Arnd Bergmann, Rob Herring On 11/27/2012 10:12 AM, Thomas Petazzoni wrote: > Dear Mark Langsdorf, > > On Tue, 27 Nov 2012 09:04:32 -0600, Mark Langsdorf wrote: > >> +int ipc_transmit(u32 *data); > > ipc_transmit() looks to me like a way to generic name to be exposed to > the entire kernel. Good point. Changed to pl320_ipc_transmit() >> +extern int pl320_ipc_register_notifier(struct notifier_block *nb); >> +extern int pl320_ipc_unregister_notifier(struct notifier_block *nb); > > Why some "extern" here? You don't have these for the other functions in > this header file. Nice catch. Fixed to export them in cpufreq. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 5/6 v5] power: export opp cpufreq functions 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf ` (3 preceding siblings ...) 2012-11-27 15:04 ` [PATCH 4/6 v5] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-11-27 15:04 ` Mark Langsdorf [not found] ` <1354028674-23685-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 2012-11-27 19:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> --- Changes from v4 None. Changes from v3 includes linux/export.h instead of module.h. Changes from v2 None. Changes from v1 Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..4062ec3 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/export.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
[parent not found: <1354028674-23685-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>]
* [PATCH 6/6 v5] cpufreq, highbank: add support for highbank cpufreq [not found] ` <1354028674-23685-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> @ 2012-11-27 15:04 ` Mark Langsdorf 0 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw) To: linux-kernel-u79uwXL29TY76Z2rM5mHXA, cpufreq-u79uwXL29TY76Z2rM5mHXA, linux-pm-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Langsdorf 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-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org --- Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. .../bindings/cpufreq/highbank-cpufreq.txt | 53 +++++ arch/arm/boot/dts/highbank.dts | 10 + arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 13 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 229 +++++++++++++++++++++ 6 files changed, 308 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt new file mode 100644 index 0000000..1d5a836 --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt @@ -0,0 +1,53 @@ +Highbank cpufreq driver + +This is cpufreq driver for Calxeda ECX-1000 (highbank) processor. It is based +on the generic cpu0 driver and uses a similar format for bindings. Since +the EnergyCore Management Engine maintains the voltage based on the +frequency, the voltage component of the operating points can be set to any +arbitrary values. + +Both required properties listed below must be defined under node /cpus/cpu@0. + +Required properties: +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt + for details +- transition-latency: Specify the possible maximum transition latency for clock, + in unit of nanoseconds. + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz ignored */ + 790000 1000000 + 396000 1000000 + 198000 1000000 + >; + transition-latency = <200000>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; +}; diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 0c6fc34..8624c94 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + transition-latency = <100000>; }; cpu@1 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2896881..b7862da 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000 (Highbank)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e64..7a8bcdc 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,16 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select PM_OPP + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1bc90e1..9e8f12a 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..f2ac342 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * derived from cpufreq-cpu0 by Freescale Semiconductor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/slab.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 + +static unsigned int transition_latency; + +static struct device *cpu_dev; +static struct clk *cpu_clk; +static struct cpufreq_frequency_table *freq_table; + +static int hb_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +static unsigned int hb_get_speed(unsigned int cpu) +{ + return clk_get_rate(cpu_clk) / 1000; +} + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[7]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000; + for (i = 2; i < 7; i++) + msg[i] = 0; + + return ipc_transmit(msg); +} + +static int hb_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + unsigned long freq_Hz; + unsigned int index, cpu; + int ret; + + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &index); + if (ret) { + pr_err("failed to match target freqency %d: %d\n", + target_freq, ret); + return ret; + } + + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); + if (freq_Hz < 0) + freq_Hz = freq_table[index].frequency * 1000; + freqs.new = freq_Hz / 1000; + freqs.old = clk_get_rate(cpu_clk) / 1000; + + if (freqs.old == freqs.new) + return 0; + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } + + pr_debug("%u MHz --> %u MHz\n", freqs.old / 1000, freqs.new / 1000); + + /* scaling up? scale voltage before frequency */ + if (freqs.new > freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + freqs.new = freqs.old; + return -EAGAIN; + } + } + + ret = clk_set_rate(cpu_clk, freqs.new * 1000); + if (ret) { + pr_err("failed to set clock rate: %d\n", ret); + hb_voltage_change(freqs.old); + return ret; + } + + /* scaling down? scale voltage after frequency */ + if (freqs.new < freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + if (clk_set_rate(cpu_clk, freqs.old * 1000)) + pr_err("also failed to reset freq\n"); + freqs.new = freqs.old; + return -EAGAIN; + } + } + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + + return 0; +} + +static int hb_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + + if (policy->cpu != 0) + return -EINVAL; + + ret = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (ret) { + pr_err("invalid frequency table: %d\n", ret); + return ret; + } + + policy->cpuinfo.transition_latency = transition_latency; + policy->cur = clk_get_rate(cpu_clk) / 1000; + + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; + cpumask_setall(policy->cpus); + + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + + return 0; +} + +static int hb_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + + return 0; +} + +static struct freq_attr *hb_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver hb_cpufreq_driver = { + .flags = CPUFREQ_STICKY, + .verify = hb_verify_speed, + .target = hb_set_target, + .get = hb_get_speed, + .init = hb_cpufreq_init, + .exit = hb_cpufreq_exit, + .name = "highbank-cpufreq", + .attr = hb_cpufreq_attr, +}; + +static int __devinit hb_cpufreq_driver_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = of_init_opp_table(cpu_dev); + if (ret) { + pr_err("failed to init OPP table: %d\n", ret); + goto out_put_node; + } + + ret = opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table: %d\n", ret); + goto out_put_node; + } + + if (of_property_read_u32(np, "transition-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + ret = cpufreq_register_driver(&hb_cpufreq_driver); + if (ret) { + pr_err("failed register driver: %d\n", ret); + goto out_free_table; + } + + of_node_put(np); + return 0; + +out_free_table: + opp_free_cpufreq_table(cpu_dev, &freq_table); +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf ` (5 preceding siblings ...) [not found] ` <1354028674-23685-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> @ 2012-11-27 19:04 ` Rafael J. Wysocki 6 siblings, 0 replies; 120+ messages in thread From: Rafael J. Wysocki @ 2012-11-27 19:04 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-pm, linux-kernel, cpufreq, linux-arm-kernel, Thomas Petazzoni On Tuesday, November 27, 2012 09:04:28 AM Mark Langsdorf wrote: > This patch series adds cpufreq support for the Calxeda > ECX-1000 (highbank) SoCs. The driver is based on the > cpufreq-cpu0 driver. Because of the unique way that > highbank uses the EnergyCore Management Engine to manage > voltages, it was not possible to use the cpufreq-cpu0 driver. Well, Thomas still seems to be unhappy with [4/6], although those don't seem to be essential points. Can you please address his comments? I can't promise I'll take it to my first pull request during the upcoming merge window, but it looks like there will be a second one anyway. Thanks, Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/6 v6] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (5 preceding siblings ...) 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf @ 2012-11-27 20:04 ` Mark Langsdorf 2012-11-27 20:04 ` [PATCH 1/6 v6] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (5 more replies) 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (5 subsequent siblings) 12 siblings, 6 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The driver is based on the cpufreq-cpu0 driver. Because of the unique way that highbank uses the EnergyCore Management Engine to manage voltages, it was not possible to use the cpufreq-cpu0 driver. --Mark Langsdorf ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/6 v6] arm: use devicetree to get smp_twd clock 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf @ 2012-11-27 20:04 ` Mark Langsdorf 2012-11-27 20:04 ` [PATCH 2/6 v6] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (4 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v4, v5 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b22d700..af46b80 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -263,6 +266,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -273,12 +277,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) { struct clock_event_device **this_cpu_clk; - if (!twd_clk) - twd_clk = twd_get_clock(); - - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else + if (IS_ERR_OR_NULL(twd_clk)) twd_calibrate_rate(); __raw_writel(0, twd_base + TWD_TIMER_CONTROL); @@ -349,6 +348,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -383,6 +384,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/6 v6] clk, highbank: Prevent glitches in non-bypass reset mode 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf 2012-11-27 20:04 ` [PATCH 1/6 v6] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-11-27 20:04 ` Mark Langsdorf 2012-11-27 20:04 ` [PATCH 3/6 v6] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf ` (3 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Mike Turquette <mturquette@linaro.org> --- Changes from v5 Added Mike Turquette's ack. Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1: Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 3/6 v6] cpufreq: tolerate inexact values when collecting stats 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf 2012-11-27 20:04 ` [PATCH 1/6 v6] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-27 20:04 ` [PATCH 2/6 v6] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-11-27 20:04 ` Mark Langsdorf 2012-11-27 20:04 ` [PATCH 4/6 v6] arm highbank: add support for pl320 IPC Mark Langsdorf ` (2 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf This patch is withdrawn due to a need for severe rework. Changes from v4 Withdrawn. Changes from v3, v2 None. Changes from v1 Implemented a simple round-up algorithm instead of the over/under method that could cause errors on Intel processors with boost mode. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/6 v6] arm highbank: add support for pl320 IPC 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf ` (2 preceding siblings ...) 2012-11-27 20:04 ` [PATCH 3/6 v6] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-11-27 20:04 ` Mark Langsdorf 2012-11-27 20:04 ` [PATCH 5/6 v6] power: export opp cpufreq functions Mark Langsdorf [not found] ` <1354046672-7392-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Omar Ramirez Luna, Arnd Bergmann, Rob Herring, Mark Langsdorf From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. This patch depends on Omar Ramirez Luna's <omar.luna@linaro.org> mailbox driver patch series. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v5 Renamed ipc_transmit() to pl320_ipc_transmit(). Properly exported pl320_ipc_{un}register_notifier(). Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 4 + drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ include/linux/mailbox.h | 19 +++- 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 drivers/mailbox/Makefile create mode 100644 drivers/mailbox/pl320-ipc.c diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 0e1d0a4..2896881 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index be8cac0..e89fdb4 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -34,4 +34,13 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile new file mode 100644 index 0000000..c9f14c3 --- /dev/null +++ b/drivers/mailbox/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_OMAP1_MBOX) += mailbox.o mailbox-omap1.o +obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox.o mailbox-omap2.o +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o + diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..1a9d8e4 --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int pl320_ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(pl320_ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_register_notifier); + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h index e8e4131..e7829e5 100644 --- a/include/linux/mailbox.h +++ b/include/linux/mailbox.h @@ -1,4 +1,16 @@ -/* mailbox.h */ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ typedef u32 mbox_msg_t; struct omap_mbox; @@ -20,3 +32,8 @@ void omap_mbox_save_ctx(struct omap_mbox *mbox); void omap_mbox_restore_ctx(struct omap_mbox *mbox); void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); + +int pl320_ipc_transmit(u32 *data); +int pl320_ipc_register_notifier(struct notifier_block *nb); +int pl320_ipc_unregister_notifier(struct notifier_block *nb); + -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 5/6 v6] power: export opp cpufreq functions 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf ` (3 preceding siblings ...) 2012-11-27 20:04 ` [PATCH 4/6 v6] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-11-27 20:04 ` Mark Langsdorf [not found] ` <1354046672-7392-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> --- Changes from v4, v5 None. Changes from v3 includes linux/export.h instead of module.h. Changes from v2 None. Changes from v1 Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..4062ec3 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/export.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
[parent not found: <1354046672-7392-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>]
* [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq [not found] ` <1354046672-7392-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> @ 2012-11-27 20:04 ` Mark Langsdorf [not found] ` <1354046672-7392-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw) To: linux-kernel-u79uwXL29TY76Z2rM5mHXA, cpufreq-u79uwXL29TY76Z2rM5mHXA, linux-pm-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Langsdorf 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-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org --- Changes from v5 Changed ipc_transmit() to pl320_ipc_transmit(). Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. .../bindings/cpufreq/highbank-cpufreq.txt | 53 +++++ arch/arm/boot/dts/highbank.dts | 10 + arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 13 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 229 +++++++++++++++++++++ 6 files changed, 308 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt new file mode 100644 index 0000000..1d5a836 --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/highbank-cpufreq.txt @@ -0,0 +1,53 @@ +Highbank cpufreq driver + +This is cpufreq driver for Calxeda ECX-1000 (highbank) processor. It is based +on the generic cpu0 driver and uses a similar format for bindings. Since +the EnergyCore Management Engine maintains the voltage based on the +frequency, the voltage component of the operating points can be set to any +arbitrary values. + +Both required properties listed below must be defined under node /cpus/cpu@0. + +Required properties: +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt + for details +- transition-latency: Specify the possible maximum transition latency for clock, + in unit of nanoseconds. + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz ignored */ + 790000 1000000 + 396000 1000000 + 198000 1000000 + >; + transition-latency = <200000>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; +}; diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 0c6fc34..8624c94 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + transition-latency = <100000>; }; cpu@1 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2896881..b7862da 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000 (Highbank)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e64..7a8bcdc 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,16 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select PM_OPP + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1bc90e1..9e8f12a 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..878d3ff --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * derived from cpufreq-cpu0 by Freescale Semiconductor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/slab.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 + +static unsigned int transition_latency; + +static struct device *cpu_dev; +static struct clk *cpu_clk; +static struct cpufreq_frequency_table *freq_table; + +static int hb_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +static unsigned int hb_get_speed(unsigned int cpu) +{ + return clk_get_rate(cpu_clk) / 1000; +} + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[7]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000; + for (i = 2; i < 7; i++) + msg[i] = 0; + + return pl320_ipc_transmit(msg); +} + +static int hb_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + unsigned long freq_Hz; + unsigned int index, cpu; + int ret; + + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &index); + if (ret) { + pr_err("failed to match target freqency %d: %d\n", + target_freq, ret); + return ret; + } + + freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); + if (freq_Hz < 0) + freq_Hz = freq_table[index].frequency * 1000; + freqs.new = freq_Hz / 1000; + freqs.old = clk_get_rate(cpu_clk) / 1000; + + if (freqs.old == freqs.new) + return 0; + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } + + pr_debug("%u MHz --> %u MHz\n", freqs.old / 1000, freqs.new / 1000); + + /* scaling up? scale voltage before frequency */ + if (freqs.new > freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + freqs.new = freqs.old; + return -EAGAIN; + } + } + + ret = clk_set_rate(cpu_clk, freqs.new * 1000); + if (ret) { + pr_err("failed to set clock rate: %d\n", ret); + hb_voltage_change(freqs.old); + return ret; + } + + /* scaling down? scale voltage after frequency */ + if (freqs.new < freqs.old) { + ret = hb_voltage_change(freqs.new); + if (ret) { + if (clk_set_rate(cpu_clk, freqs.old * 1000)) + pr_err("also failed to reset freq\n"); + freqs.new = freqs.old; + return -EAGAIN; + } + } + + for_each_online_cpu(cpu) { + freqs.cpu = cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + + return 0; +} + +static int hb_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + + if (policy->cpu != 0) + return -EINVAL; + + ret = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (ret) { + pr_err("invalid frequency table: %d\n", ret); + return ret; + } + + policy->cpuinfo.transition_latency = transition_latency; + policy->cur = clk_get_rate(cpu_clk) / 1000; + + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; + cpumask_setall(policy->cpus); + + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + + return 0; +} + +static int hb_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + + return 0; +} + +static struct freq_attr *hb_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver hb_cpufreq_driver = { + .flags = CPUFREQ_STICKY, + .verify = hb_verify_speed, + .target = hb_set_target, + .get = hb_get_speed, + .init = hb_cpufreq_init, + .exit = hb_cpufreq_exit, + .name = "highbank-cpufreq", + .attr = hb_cpufreq_attr, +}; + +static int __devinit hb_cpufreq_driver_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = of_init_opp_table(cpu_dev); + if (ret) { + pr_err("failed to init OPP table: %d\n", ret); + goto out_put_node; + } + + ret = opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table: %d\n", ret); + goto out_put_node; + } + + if (of_property_read_u32(np, "transition-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + ret = cpufreq_register_driver(&hb_cpufreq_driver); + if (ret) { + pr_err("failed register driver: %d\n", ret); + goto out_free_table; + } + + of_node_put(np); + return 0; + +out_free_table: + opp_free_cpufreq_table(cpu_dev, &freq_table); +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
[parent not found: <1354046672-7392-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>]
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq [not found] ` <1354046672-7392-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> @ 2012-11-28 2:32 ` Shawn Guo 2012-11-28 13:16 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Shawn Guo @ 2012-11-28 2:32 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, linux-kernel-u79uwXL29TY76Z2rM5mHXA, cpufreq-u79uwXL29TY76Z2rM5mHXA, linux-pm-u79uwXL29TY76Z2rM5mHXA On Tue, Nov 27, 2012 at 02:04:32PM -0600, Mark Langsdorf wrote: > 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. ... > +static int hb_voltage_change(unsigned int freq) > +{ > + int i; > + u32 msg[7]; > + > + msg[0] = HB_CPUFREQ_CHANGE_NOTE; > + msg[1] = freq / 1000; > + for (i = 2; i < 7; i++) > + msg[i] = 0; > + > + return pl320_ipc_transmit(msg); Is it possible to have this handled inside clk_set_rate() call of cpu clock? Shawn > +} ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 2:32 ` Shawn Guo @ 2012-11-28 13:16 ` Mark Langsdorf 2012-11-28 14:58 ` Shawn Guo 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-28 13:16 UTC (permalink / raw) To: Shawn Guo Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org On 11/27/2012 08:32 PM, Shawn Guo wrote: > On Tue, Nov 27, 2012 at 02:04:32PM -0600, Mark Langsdorf wrote: >> 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. > > ... > >> +static int hb_voltage_change(unsigned int freq) >> +{ >> + int i; >> + u32 msg[7]; >> + >> + msg[0] = HB_CPUFREQ_CHANGE_NOTE; >> + msg[1] = freq / 1000; >> + for (i = 2; i < 7; i++) >> + msg[i] = 0; >> + >> + return pl320_ipc_transmit(msg); > > Is it possible to have this handled inside clk_set_rate() call of cpu > clock? Standard practice is to have cpufreq_set_target() handle voltage transitions and leave clk_set_rate() handle the frequency changes. I'd have to move most of the logic of hb_set_target() into clk_highbank.c:clk_pll_set_rate() and then add extra logic for when cpufreq is not enabled/loaded. I don't think the clk maintainers would take that patch, either. So no. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 13:16 ` Mark Langsdorf @ 2012-11-28 14:58 ` Shawn Guo 2012-11-28 15:17 ` Shawn Guo 0 siblings, 1 reply; 120+ messages in thread From: Shawn Guo @ 2012-11-28 14:58 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org On Wed, Nov 28, 2012 at 07:16:12AM -0600, Mark Langsdorf wrote: > Standard practice is to have cpufreq_set_target() handle voltage > transitions and leave clk_set_rate() handle the frequency changes. The standard practice is to have cpufreq_set_target() handle both voltage and frequency transitions, while voltage is handled by regulator and frequency by clk API. > I'd > have to move most of the logic of hb_set_target() into > clk_highbank.c:clk_pll_set_rate() and then add extra logic for when > cpufreq is not enabled/loaded. You only need to move hb_voltage_change() into cpu clock's .set_rate() hook with no need of checking if cpufreq is enabled or not. > I don't think the clk maintainers would > take that patch, either. This is all handled platform clock specific .set_rate() hook. I doubt it will concern clk maintainers at all, especially when doing so we will avoid another cpufreq driver by just using cpufreq-cpu0 driver. Shawn ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 14:58 ` Shawn Guo @ 2012-11-28 15:17 ` Shawn Guo 2012-11-28 15:01 ` Mark Langsdorf [not found] ` <20121128151741.GC28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> 0 siblings, 2 replies; 120+ messages in thread From: Shawn Guo @ 2012-11-28 15:17 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org On Wed, Nov 28, 2012 at 10:58:02PM +0800, Shawn Guo wrote: > On Wed, Nov 28, 2012 at 07:16:12AM -0600, Mark Langsdorf wrote: > > I'd > > have to move most of the logic of hb_set_target() into > > clk_highbank.c:clk_pll_set_rate() and then add extra logic for when > > cpufreq is not enabled/loaded. > > You only need to move hb_voltage_change() into cpu clock's .set_rate() > hook with no need of checking if cpufreq is enabled or not. > Need to also check whether frequency or voltage should be changed first in .set_rate() though. Shawn ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 15:17 ` Shawn Guo @ 2012-11-28 15:01 ` Mark Langsdorf [not found] ` <20121128151741.GC28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> 1 sibling, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-28 15:01 UTC (permalink / raw) To: Shawn Guo Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org On 11/28/2012 09:17 AM, Shawn Guo wrote: > On Wed, Nov 28, 2012 at 10:58:02PM +0800, Shawn Guo wrote: >> On Wed, Nov 28, 2012 at 07:16:12AM -0600, Mark Langsdorf wrote: >>> I'd >>> have to move most of the logic of hb_set_target() into >>> clk_highbank.c:clk_pll_set_rate() and then add extra logic for when >>> cpufreq is not enabled/loaded. >> >> You only need to move hb_voltage_change() into cpu clock's .set_rate() >> hook with no need of checking if cpufreq is enabled or not. >> > Need to also check whether frequency or voltage should be changed first > in .set_rate() though. Yes, that's entirely what I meant when I said that I would need to move most of the hb_set_target() logic into .set_rate(). I would also need to account for retries if the voltage set operation fails, which it sometimes does. The ECME handles changing the voltage but doesn't look like a voltage regulator. Amongst other things, by design it doesn't export meaningful voltage information to Linux. I tried to get cpufreq-clk0 to work with the Highbank design and it ended up being much easier and more sane to create a separate driver. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
[parent not found: <20121128151741.GC28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>]
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq [not found] ` <20121128151741.GC28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> @ 2012-11-28 16:01 ` Mike Turquette 2012-11-28 16:18 ` Mark Langsdorf 2012-11-29 1:51 ` Shawn Guo 0 siblings, 2 replies; 120+ messages in thread From: Mike Turquette @ 2012-11-28 16:01 UTC (permalink / raw) To: Shawn Guo, Mark Langsdorf Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org, cpufreq-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org, linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org Quoting Shawn Guo (2012-11-28 07:17:44) > On Wed, Nov 28, 2012 at 10:58:02PM +0800, Shawn Guo wrote: > > On Wed, Nov 28, 2012 at 07:16:12AM -0600, Mark Langsdorf wrote: > > > I'd > > > have to move most of the logic of hb_set_target() into > > > clk_highbank.c:clk_pll_set_rate() and then add extra logic for when > > > cpufreq is not enabled/loaded. > > > > You only need to move hb_voltage_change() into cpu clock's .set_rate() > > hook with no need of checking if cpufreq is enabled or not. > > > Need to also check whether frequency or voltage should be changed first > in .set_rate() though. > > Shawn > The notifiers in the clk framework might be a better place for this than just simply hacking the logic into the .set_rate callback. I haven't looked at the definition of hb_voltage_change but does the call graph make any clk api calls? Are you talking over i2c to a regulator? If so then you'll probably hit the same reentrancy problem I hit when trying to make a general solution. Regards, Mike > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 16:01 ` Mike Turquette @ 2012-11-28 16:18 ` Mark Langsdorf 2012-11-28 21:05 ` Mike Turquette 2012-11-29 1:51 ` Shawn Guo 1 sibling, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-11-28 16:18 UTC (permalink / raw) To: Mike Turquette Cc: Shawn Guo, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org, linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org On 11/28/2012 10:01 AM, Mike Turquette wrote: > Quoting Shawn Guo (2012-11-28 07:17:44) >> On Wed, Nov 28, 2012 at 10:58:02PM +0800, Shawn Guo wrote: >>> On Wed, Nov 28, 2012 at 07:16:12AM -0600, Mark Langsdorf wrote: >>>> I'd >>>> have to move most of the logic of hb_set_target() into >>>> clk_highbank.c:clk_pll_set_rate() and then add extra logic for when >>>> cpufreq is not enabled/loaded. >>> >>> You only need to move hb_voltage_change() into cpu clock's .set_rate() >>> hook with no need of checking if cpufreq is enabled or not. >>> >> Need to also check whether frequency or voltage should be changed first >> in .set_rate() though. >> >> Shawn >> > > The notifiers in the clk framework might be a better place for this than > just simply hacking the logic into the .set_rate callback. Unless the clk notifiers are different than the cpufreq notifiers, they don't handle returning error conditions very well. And given that the voltage change operation can fail (though it almost always succeeds on a retry) I need to be able to handle and detect that error condition. > I haven't looked at the definition of hb_voltage_change but does the > call graph make any clk api calls? Are you talking over i2c to a > regulator? If so then you'll probably hit the same reentrancy problem I > hit when trying to make a general solution. I'm talking over a pl320 Interprocessor Communication Mailbox to a separate core running it's own RTOS. The RTOS might speak i2c to a regulator but it's a black box to me. hb_voltage_change() doesn't make any clk api calls. It changes the voltages, and then hb_set_target() makes clk api calls to change the frequency. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 16:18 ` Mark Langsdorf @ 2012-11-28 21:05 ` Mike Turquette 2012-11-29 0:24 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Mike Turquette @ 2012-11-28 21:05 UTC (permalink / raw) To: Mark Langsdorf Cc: Shawn Guo, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org, linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org Quoting Mark Langsdorf (2012-11-28 08:18:35) > On 11/28/2012 10:01 AM, Mike Turquette wrote: > > Quoting Shawn Guo (2012-11-28 07:17:44) > >> On Wed, Nov 28, 2012 at 10:58:02PM +0800, Shawn Guo wrote: > >>> On Wed, Nov 28, 2012 at 07:16:12AM -0600, Mark Langsdorf wrote: > >>>> I'd > >>>> have to move most of the logic of hb_set_target() into > >>>> clk_highbank.c:clk_pll_set_rate() and then add extra logic for when > >>>> cpufreq is not enabled/loaded. > >>> > >>> You only need to move hb_voltage_change() into cpu clock's .set_rate() > >>> hook with no need of checking if cpufreq is enabled or not. > >>> > >> Need to also check whether frequency or voltage should be changed first > >> in .set_rate() though. > >> > >> Shawn > >> > > > > The notifiers in the clk framework might be a better place for this than > > just simply hacking the logic into the .set_rate callback. > > Unless the clk notifiers are different than the cpufreq notifiers, they > don't handle returning error conditions very well. And given that the > voltage change operation can fail (though it almost always succeeds on a > retry) I need to be able to handle and detect that error condition. > The notifier handler can handle the case where the transition fails (and needs to be retried). Also you should check out the clk notifiers. I think they handle failure decently. If a notifer returns an error code then everything unrolls and the clk_set_rate operation aborts. Regards, Mike > > I haven't looked at the definition of hb_voltage_change but does the > > call graph make any clk api calls? Are you talking over i2c to a > > regulator? If so then you'll probably hit the same reentrancy problem I > > hit when trying to make a general solution. > > I'm talking over a pl320 Interprocessor Communication Mailbox to a > separate core running it's own RTOS. The RTOS might speak i2c to a > regulator but it's a black box to me. > > hb_voltage_change() doesn't make any clk api calls. It changes the > voltages, and then hb_set_target() makes clk api calls to change the > frequency. > > --Mark Langsdorf > Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 21:05 ` Mike Turquette @ 2012-11-29 0:24 ` Mark Langsdorf 0 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-11-29 0:24 UTC (permalink / raw) To: Mike Turquette Cc: Shawn Guo, linux-arm-kernel@lists.infradead.org, devicetree-discuss@lists.ozlabs.org, linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org On 11/28/2012 03:05 PM, Mike Turquette wrote: > Quoting Mark Langsdorf (2012-11-28 08:18:35) >> On 11/28/2012 10:01 AM, Mike Turquette wrote: >>> Quoting Shawn Guo (2012-11-28 07:17:44) >>>> On Wed, Nov 28, 2012 at 10:58:02PM +0800, Shawn Guo wrote: >>>>> On Wed, Nov 28, 2012 at 07:16:12AM -0600, Mark Langsdorf wrote: >>>>>> I'd >>>>>> have to move most of the logic of hb_set_target() into >>>>>> clk_highbank.c:clk_pll_set_rate() and then add extra logic for when >>>>>> cpufreq is not enabled/loaded. >>>>> >>>>> You only need to move hb_voltage_change() into cpu clock's .set_rate() >>>>> hook with no need of checking if cpufreq is enabled or not. >>>>> >>>> Need to also check whether frequency or voltage should be changed first >>>> in .set_rate() though. >>>> >>>> Shawn >>>> >>> >>> The notifiers in the clk framework might be a better place for this than >>> just simply hacking the logic into the .set_rate callback. >> >> Unless the clk notifiers are different than the cpufreq notifiers, they >> don't handle returning error conditions very well. And given that the >> voltage change operation can fail (though it almost always succeeds on a >> retry) I need to be able to handle and detect that error condition. > > The notifier handler can handle the case where the transition fails (and > needs to be retried). > > Also you should check out the clk notifiers. I think they handle > failure decently. If a notifer returns an error code then everything > unrolls and the clk_set_rate operation aborts. Thanks for the pointer. The clk notifier calls seem to be working with cpufreq-cpu0. I did enough surgery on the code that I want to run a lot of stress tests before I resubmit. I'll try to have something for Tuesday. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq 2012-11-28 16:01 ` Mike Turquette 2012-11-28 16:18 ` Mark Langsdorf @ 2012-11-29 1:51 ` Shawn Guo [not found] ` <20121129015133.GD28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> 1 sibling, 1 reply; 120+ messages in thread From: Shawn Guo @ 2012-11-29 1:51 UTC (permalink / raw) To: Mike Turquette Cc: Mark Langsdorf, linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, cpufreq-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org > The notifiers in the clk framework might be a better place for this than > just simply hacking the logic into the .set_rate callback. Ah, right. How did I forget about that nice piece? > I haven't looked at the definition of hb_voltage_change but does the > call graph make any clk api calls? Are you talking over i2c to a > regulator? If so then you'll probably hit the same reentrancy problem I > hit when trying to make a general solution. So, how is your "reentrancy in the common clk framework" series[1] going on? Haven't seen any update since August. Shawn [1] http://thread.gmane.org/gmane.linux.ports.arm.kernel/182198 ^ permalink raw reply [flat|nested] 120+ messages in thread
[parent not found: <20121129015133.GD28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>]
* Re: [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq [not found] ` <20121129015133.GD28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> @ 2012-11-29 4:34 ` Mike Turquette 0 siblings, 0 replies; 120+ messages in thread From: Mike Turquette @ 2012-11-29 4:34 UTC (permalink / raw) To: Shawn Guo Cc: Mark Langsdorf, linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, cpufreq-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org Quoting Shawn Guo (2012-11-28 17:51:36) > > The notifiers in the clk framework might be a better place for this than > > just simply hacking the logic into the .set_rate callback. > > Ah, right. How did I forget about that nice piece? > > > I haven't looked at the definition of hb_voltage_change but does the > > call graph make any clk api calls? Are you talking over i2c to a > > regulator? If so then you'll probably hit the same reentrancy problem I > > hit when trying to make a general solution. > > So, how is your "reentrancy in the common clk framework" series[1] > going on? Haven't seen any update since August. > I've begun to look at a dvfs api that builds on top of the clock framework, as opposed to using clk_set_rate as the dvfs api itself. This eliminates the need for reentrancy, at least for the dvfs case. I'll post more when I have it. Honestly the reentrancy stuff was just too ugly. I might try again some day but for now I'm thinking a less radical approach deserves consideration. Thanks, Mike > Shawn > > [1] http://thread.gmane.org/gmane.linux.ports.arm.kernel/182198 ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (6 preceding siblings ...) 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf @ 2012-12-04 14:33 ` Mark Langsdorf 2012-12-04 14:33 ` [PATCH 1/6 v7] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (5 more replies) 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (4 subsequent siblings) 12 siblings, 6 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on the ECX-1000 manages the voltage for the part and communications with Linux through a pl320 mailbox. clk notifications are used to control when to send messages to the ECME. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/6 v7] arm: use devicetree to get smp_twd clock 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2012-12-04 14:33 ` Mark Langsdorf 2012-12-04 14:33 ` [PATCH 2/6 v7] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (4 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v4, v5, v6 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b22d700..af46b80 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -263,6 +266,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -273,12 +277,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) { struct clock_event_device **this_cpu_clk; - if (!twd_clk) - twd_clk = twd_get_clock(); - - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else + if (IS_ERR_OR_NULL(twd_clk)) twd_calibrate_rate(); __raw_writel(0, twd_base + TWD_TIMER_CONTROL); @@ -349,6 +348,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -383,6 +384,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/6 v7] clk, highbank: Prevent glitches in non-bypass reset mode 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-12-04 14:33 ` [PATCH 1/6 v7] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-12-04 14:33 ` Mark Langsdorf 2012-12-04 14:33 ` [PATCH 3/6 v7] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf ` (3 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Mike Turquette <mturquette@linaro.org> --- Changes from v6 None. Changes from v5 Added Mike Turquette's ack. Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1: Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 3/6 v7] cpufreq: tolerate inexact values when collecting stats 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-12-04 14:33 ` [PATCH 1/6 v7] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-12-04 14:33 ` [PATCH 2/6 v7] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-12-04 14:33 ` Mark Langsdorf 2012-12-04 14:34 ` [PATCH 4/6 v7] arm highbank: add support for pl320 IPC Mark Langsdorf ` (2 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf This patch is withdrawn due to a need for severe rework. Changes from v4 Withdrawn. Changes from v3, v2 None. Changes from v1 Implemented a simple round-up algorithm instead of the over/under method that could cause errors on Intel processors with boost mode. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/6 v7] arm highbank: add support for pl320 IPC 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (2 preceding siblings ...) 2012-12-04 14:33 ` [PATCH 3/6 v7] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-12-04 14:34 ` Mark Langsdorf 2012-12-04 14:34 ` [PATCH 5/6 v7] power: export opp cpufreq functions Mark Langsdorf 2012-12-04 14:34 ` [PATCH 6/6 v7] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-04 14:34 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Omar Ramirez Luna, Arnd Bergmann, Rob Herring, Mark Langsdorf From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. This patch depends on Omar Ramirez Luna's <omar.luna@linaro.org> mailbox driver patch series. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v6 None. Changes from v5 Renamed ipc_transmit() to pl320_ipc_transmit(). Properly exported pl320_ipc_{un}register_notifier(). Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 4 + drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ include/linux/mailbox.h | 19 +++- 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 drivers/mailbox/Makefile create mode 100644 drivers/mailbox/pl320-ipc.c diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 0e1d0a4..2896881 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index be8cac0..e89fdb4 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -34,4 +34,13 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile new file mode 100644 index 0000000..c9f14c3 --- /dev/null +++ b/drivers/mailbox/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_OMAP1_MBOX) += mailbox.o mailbox-omap1.o +obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox.o mailbox-omap2.o +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o + diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..1a9d8e4 --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int pl320_ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(pl320_ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_register_notifier); + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h index e8e4131..e7829e5 100644 --- a/include/linux/mailbox.h +++ b/include/linux/mailbox.h @@ -1,4 +1,16 @@ -/* mailbox.h */ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ typedef u32 mbox_msg_t; struct omap_mbox; @@ -20,3 +32,8 @@ void omap_mbox_save_ctx(struct omap_mbox *mbox); void omap_mbox_restore_ctx(struct omap_mbox *mbox); void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); + +int pl320_ipc_transmit(u32 *data); +int pl320_ipc_register_notifier(struct notifier_block *nb); +int pl320_ipc_unregister_notifier(struct notifier_block *nb); + -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 5/6 v7] power: export opp cpufreq functions 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (3 preceding siblings ...) 2012-12-04 14:34 ` [PATCH 4/6 v7] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-12-04 14:34 ` Mark Langsdorf 2012-12-04 14:34 ` [PATCH 6/6 v7] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-04 14:34 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> --- Changes from v4, v5, v6 None. Changes from v3 includes linux/export.h instead of module.h. Changes from v2 None. Changes from v1 Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..4062ec3 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/export.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 6/6 v7] cpufreq, highbank: add support for highbank cpufreq 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (4 preceding siblings ...) 2012-12-04 14:34 ` [PATCH 5/6 v7] power: export opp cpufreq functions Mark Langsdorf @ 2012-12-04 14:34 ` Mark Langsdorf 2012-12-04 16:21 ` Shawn Guo 5 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-12-04 14:34 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, shawn.guo, mturquette 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> Cc: shawn.guo@linaro.org Cc: mturquette@ti.com --- Changes from v6 Removed devicetree bindings documentation. Restructured driver to use clk notifications. Core driver logic is now cpufreq-clk0. Changes from v5 Changed ipc_transmit() to pl320_ipc_transmit(). Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. arch/arm/boot/dts/highbank.dts | 10 ++++ arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 16 ++++++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 106 +++++++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 0c6fc34..7c4c27d 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + clock-latency = <100000>; }; cpu@1 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2896881..b7862da 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000 (Highbank)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e64..7aaac9f 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,19 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select GENERIC_CPUFREQ_CPU0 + select PM_OPP + select REGULATOR + + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1bc90e1..9e8f12a 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..25ef437 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * derived from cpufreq-cpu0 by Freescale Semiconductor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/slab.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 + +static struct device *cpu_dev; +static struct clk *cpu_clk; + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[7]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000000; + for (i = 2; i < 7; i++) + msg[i] = 0; + + return pl320_ipc_transmit(msg); +} + +static int hb_cpufreq_clk_notify(struct notifier_block *nb, + unsigned long action, void *hclk) +{ + struct clk_notifier_data *clk_data = hclk; + int i = 0; + + if (action == PRE_RATE_CHANGE) { + if (clk_data->new_rate > clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > 15) + return NOTIFY_STOP; + } else if (action == POST_RATE_CHANGE) + if (clk_data->new_rate < clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > 15) + break; + + return NOTIFY_DONE; +} + +static struct notifier_block hb_cpufreq_clk_nb = { + .notifier_call = hb_cpufreq_clk_notify, +}; + +static int __devinit hb_cpufreq_driver_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); + if (ret) { + pr_err("failed to register clk notifier: %d\n", ret); + goto out_put_node; + } + +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v7] cpufreq, highbank: add support for highbank cpufreq 2012-12-04 14:34 ` [PATCH 6/6 v7] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf @ 2012-12-04 16:21 ` Shawn Guo 0 siblings, 0 replies; 120+ messages in thread From: Shawn Guo @ 2012-12-04 16:21 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel, mturquette On Tue, Dec 04, 2012 at 08:34:02AM -0600, Mark Langsdorf wrote: > 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> > Cc: shawn.guo@linaro.org > Cc: mturquette@ti.com > --- > Changes from v6 > Removed devicetree bindings documentation. > Restructured driver to use clk notifications. > Core driver logic is now cpufreq-clk0. Great. It saves some codes :) > Changes from v5 > Changed ipc_transmit() to pl320_ipc_transmit(). > Changes from v4 > Removed erroneous changes to arch/arm/Kconfig. > Removed unnecessary changes to drivers/cpufreq/Kconfig.arm > Alphabetized additions to arch/arm/mach-highbank/Kconfig > Changed ipc call and header to match new ipc location in > drivers/mailbox. > Changes from v3 > None. > Changes from v2 > Changed transition latency binding in code to match documentation. > Changes from v1 > Added highbank specific Kconfig changes. > > arch/arm/boot/dts/highbank.dts | 10 ++++ > arch/arm/mach-highbank/Kconfig | 2 + > drivers/cpufreq/Kconfig.arm | 16 ++++++ > drivers/cpufreq/Makefile | 1 + > drivers/cpufreq/highbank-cpufreq.c | 106 +++++++++++++++++++++++++++++++++++++ > 5 files changed, 135 insertions(+) > create mode 100644 drivers/cpufreq/highbank-cpufreq.c > > diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts > index 0c6fc34..7c4c27d 100644 > --- a/arch/arm/boot/dts/highbank.dts > +++ b/arch/arm/boot/dts/highbank.dts > @@ -36,6 +36,16 @@ > next-level-cache = <&L2>; > clocks = <&a9pll>; > clock-names = "cpu"; > + operating-points = < > + /* kHz ignored */ > + 1300000 1000000 > + 1200000 1000000 > + 1100000 1000000 > + 800000 1000000 > + 400000 1000000 > + 200000 1000000 > + >; > + clock-latency = <100000>; > }; > > cpu@1 { > diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig > index 2896881..b7862da 100644 > --- a/arch/arm/mach-highbank/Kconfig > +++ b/arch/arm/mach-highbank/Kconfig > @@ -1,5 +1,7 @@ > config ARCH_HIGHBANK > bool "Calxeda ECX-1000 (Highbank)" if ARCH_MULTI_V7 > + select ARCH_HAS_CPUFREQ > + select ARCH_HAS_OPP > select ARCH_WANT_OPTIONAL_GPIOLIB > select ARM_AMBA > select ARM_GIC > diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm > index 5961e64..7aaac9f 100644 > --- a/drivers/cpufreq/Kconfig.arm > +++ b/drivers/cpufreq/Kconfig.arm > @@ -76,3 +76,19 @@ config ARM_EXYNOS5250_CPUFREQ > help > This adds the CPUFreq driver for Samsung EXYNOS5250 > SoC. > + > +config ARM_HIGHBANK_CPUFREQ > + tristate "Calxeda Highbank-based" > + depends on ARCH_HIGHBANK > + select CPU_FREQ_TABLE > + select GENERIC_CPUFREQ_CPU0 > + select PM_OPP > + select REGULATOR > + > + default m > + help > + This adds the CPUFreq driver for Calxeda Highbank SoC > + based boards. > + > + If in doubt, say N. > + > diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile > index 1bc90e1..9e8f12a 100644 > --- a/drivers/cpufreq/Makefile > +++ b/drivers/cpufreq/Makefile > @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o > obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o > obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o > obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o > +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o > > ################################################################################## > # PowerPC platform drivers > diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c > new file mode 100644 > index 0000000..25ef437 > --- /dev/null > +++ b/drivers/cpufreq/highbank-cpufreq.c > @@ -0,0 +1,106 @@ > +/* > + * Copyright (C) 2012 Calxeda, Inc. > + * > + * derived from cpufreq-cpu0 by Freescale Semiconductor It's not any more, right? > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/clk.h> > +#include <linux/cpu.h> > +#include <linux/err.h> > +#include <linux/of.h> > +#include <linux/opp.h> Do you need this header? > +#include <linux/slab.h> > +#include <linux/mailbox.h> > + > +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 > + > +static struct device *cpu_dev; > +static struct clk *cpu_clk; These two now can be local variables hb_cpufreq_driver_init(). > + > +static int hb_voltage_change(unsigned int freq) > +{ > + int i; > + u32 msg[7]; > + > + msg[0] = HB_CPUFREQ_CHANGE_NOTE; > + msg[1] = freq / 1000000; > + for (i = 2; i < 7; i++) > + msg[i] = 0; > + > + return pl320_ipc_transmit(msg); > +} > + > +static int hb_cpufreq_clk_notify(struct notifier_block *nb, > + unsigned long action, void *hclk) > +{ > + struct clk_notifier_data *clk_data = hclk; > + int i = 0; > + > + if (action == PRE_RATE_CHANGE) { > + if (clk_data->new_rate > clk_data->old_rate) > + while (hb_voltage_change(clk_data->new_rate)) > + if (i++ > 15) > + return NOTIFY_STOP; > + } else if (action == POST_RATE_CHANGE) Add a {} pair for else block or remove {} for if? > + if (clk_data->new_rate < clk_data->old_rate) > + while (hb_voltage_change(clk_data->new_rate)) > + if (i++ > 15) > + break; > + > + return NOTIFY_DONE; > +} > + > +static struct notifier_block hb_cpufreq_clk_nb = { > + .notifier_call = hb_cpufreq_clk_notify, > +}; > + > +static int __devinit hb_cpufreq_driver_init(void) Isn't there a big series removing __devinit from the kernel tree as CONFIG_HOTPLUG is going away? Shawn > +{ > + struct device_node *np; > + int ret; > + > + np = of_find_node_by_path("/cpus/cpu@0"); > + if (!np) { > + pr_err("failed to find highbank cpufreq node\n"); > + return -ENOENT; > + } > + > + cpu_dev = get_cpu_device(0); > + if (!cpu_dev) { > + pr_err("failed to get highbank cpufreq device\n"); > + ret = -ENODEV; > + goto out_put_node; > + } > + > + cpu_dev->of_node = np; > + > + cpu_clk = clk_get(cpu_dev, NULL); > + if (IS_ERR(cpu_clk)) { > + ret = PTR_ERR(cpu_clk); > + pr_err("failed to get cpu0 clock: %d\n", ret); > + goto out_put_node; > + } > + > + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); > + if (ret) { > + pr_err("failed to register clk notifier: %d\n", ret); > + goto out_put_node; > + } > + > +out_put_node: > + of_node_put(np); > + return ret; > +} > +late_initcall(hb_cpufreq_driver_init); > + > +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); > +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); > +MODULE_LICENSE("GPL"); > -- > 1.7.11.7 > ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (7 preceding siblings ...) 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2012-12-05 16:48 ` Mark Langsdorf 2012-12-05 16:48 ` [PATCH 1/6 v8] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (6 more replies) 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf ` (3 subsequent siblings) 12 siblings, 7 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on the ECX-1000 manages the voltage for the part and communications with Linux through a pl320 mailbox. clk notifications are used to control when to send messages to the ECME. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/6 v8] arm: use devicetree to get smp_twd clock 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2012-12-05 16:48 ` Mark Langsdorf 2012-12-05 16:48 ` [PATCH 2/6 v8] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (5 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v4, v5, v6, v7 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b22d700..af46b80 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -263,6 +266,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -273,12 +277,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) { struct clock_event_device **this_cpu_clk; - if (!twd_clk) - twd_clk = twd_get_clock(); - - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else + if (IS_ERR_OR_NULL(twd_clk)) twd_calibrate_rate(); __raw_writel(0, twd_base + TWD_TIMER_CONTROL); @@ -349,6 +348,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -383,6 +384,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/6 v8] clk, highbank: Prevent glitches in non-bypass reset mode 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-12-05 16:48 ` [PATCH 1/6 v8] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-12-05 16:48 ` Mark Langsdorf 2012-12-05 18:02 ` Mike Turquette 2012-12-05 16:48 ` [PATCH 3/6 v8] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf ` (4 subsequent siblings) 6 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, mturquette The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: mturquette@linaro.org --- Changes from v6, v7 None. Changes from v5 Added Mike Turquette's ack. Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1 Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 2/6 v8] clk, highbank: Prevent glitches in non-bypass reset mode 2012-12-05 16:48 ` [PATCH 2/6 v8] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-12-05 18:02 ` Mike Turquette 0 siblings, 0 replies; 120+ messages in thread From: Mike Turquette @ 2012-12-05 18:02 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm@vger.kernel.org, linux-arm-kernel, Rob Herring On Wed, Dec 5, 2012 at 8:48 AM, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote: > The highbank clock will glitch with the current code if the > clock rate is reset without relocking the PLL. Program the PLL > correctly to prevent glitches. > > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > Cc: mturquette@linaro.org > --- > Changes from v6, v7 > None. > Changes from v5 > Added Mike Turquette's ack. It appears that my Ack has not been added to this version of the patch. Regards, Mike > Changes from v4 > None. > Changes from v3 > Changelog text and patch name now correspond to the actual patch. > was clk, highbank: remove non-bypass reset mode. > Changes from v2 > None. > Changes from v1 > Removed erroneous reformating. > > drivers/clk/clk-highbank.c | 2 ++ > 1 file changed, 2 insertions(+) > > diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c > index 52fecad..3a0b723 100644 > --- a/drivers/clk/clk-highbank.c > +++ b/drivers/clk/clk-highbank.c > @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, > reg |= HB_PLL_EXT_ENA; > reg &= ~HB_PLL_EXT_BYPASS; > } else { > + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); > reg &= ~HB_PLL_DIVQ_MASK; > reg |= divq << HB_PLL_DIVQ_SHIFT; > + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); > } > writel(reg, hbclk->reg); > > -- > 1.7.11.7 > ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 3/6 v8] cpufreq: tolerate inexact values when collecting stats 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-12-05 16:48 ` [PATCH 1/6 v8] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-12-05 16:48 ` [PATCH 2/6 v8] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-12-05 16:48 ` Mark Langsdorf 2012-12-05 16:48 ` [PATCH 4/6 v8] arm highbank: add support for pl320 IPC Mark Langsdorf ` (3 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf This patch is withdrawn due to a need for severe rework. Changes from v4 Withdrawn. Changes from v3, v2 None. Changes from v1 Implemented a simple round-up algorithm instead of the over/under method that could cause errors on Intel processors with boost mode. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/6 v8] arm highbank: add support for pl320 IPC 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (2 preceding siblings ...) 2012-12-05 16:48 ` [PATCH 3/6 v8] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-12-05 16:48 ` Mark Langsdorf 2012-12-05 16:48 ` [PATCH 5/6 v8] power: export opp cpufreq functions Mark Langsdorf ` (2 subsequent siblings) 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, Omar Ramirez Luna, Arnd Bergmann From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. This patch depends on Omar Ramirez Luna's <omar.luna@linaro.org> mailbox driver patch series. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v6, v7 None. Changes from v5 Renamed ipc_transmit() to pl320_ipc_transmit(). Properly exported pl320_ipc_{un}register_notifier(). Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 4 + drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ include/linux/mailbox.h | 19 +++- 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 drivers/mailbox/Makefile create mode 100644 drivers/mailbox/pl320-ipc.c diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 0e1d0a4..2896881 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index be8cac0..e89fdb4 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -34,4 +34,13 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile new file mode 100644 index 0000000..c9f14c3 --- /dev/null +++ b/drivers/mailbox/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_OMAP1_MBOX) += mailbox.o mailbox-omap1.o +obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox.o mailbox-omap2.o +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o + diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..1a9d8e4 --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int pl320_ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(pl320_ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_register_notifier); + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h index e8e4131..e7829e5 100644 --- a/include/linux/mailbox.h +++ b/include/linux/mailbox.h @@ -1,4 +1,16 @@ -/* mailbox.h */ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ typedef u32 mbox_msg_t; struct omap_mbox; @@ -20,3 +32,8 @@ void omap_mbox_save_ctx(struct omap_mbox *mbox); void omap_mbox_restore_ctx(struct omap_mbox *mbox); void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); + +int pl320_ipc_transmit(u32 *data); +int pl320_ipc_register_notifier(struct notifier_block *nb); +int pl320_ipc_unregister_notifier(struct notifier_block *nb); + -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 5/6 v8] power: export opp cpufreq functions 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (3 preceding siblings ...) 2012-12-05 16:48 ` [PATCH 4/6 v8] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-12-05 16:48 ` Mark Langsdorf 2012-12-05 16:48 ` [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-12-27 13:12 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki 6 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> --- Changes from v4, v5, v6, v7 None. Changes from v3 includes linux/export.h instead of module.h. Changes from v2 None. Changes from v1 Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..4062ec3 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/export.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (4 preceding siblings ...) 2012-12-05 16:48 ` [PATCH 5/6 v8] power: export opp cpufreq functions Mark Langsdorf @ 2012-12-05 16:48 ` Mark Langsdorf 2012-12-05 18:49 ` Mike Turquette 2012-12-06 9:37 ` Shawn Guo 2012-12-27 13:12 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki 6 siblings, 2 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, shawn.guo, mturquette 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> Cc: shawn.guo@linaro.org Cc: mturquette@linaro.org --- Changes from v7 Removed old attribution to cpufreq-cpu0. Added some description in the documentation. Made cpu_dev, cpu_clk into local variables. Removed __devinit. Removed some unneeded includes. Added a brace to clarify some nested if logic. Changes from v6 Removed devicetree bindings documentation. Restructured driver to use clk notifications. Core driver logic is now cpufreq-clk0. Changes from v5 Changed ipc_transmit() to pl320_ipc_transmit(). Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. arch/arm/boot/dts/highbank.dts | 10 ++++ arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 16 ++++++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 102 +++++++++++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+) create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 0c6fc34..7c4c27d 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + clock-latency = <100000>; }; cpu@1 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2896881..b7862da 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000 (Highbank)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e64..7aaac9f 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,19 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select GENERIC_CPUFREQ_CPU0 + select PM_OPP + select REGULATOR + + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1bc90e1..9e8f12a 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..1f28fa6 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[7]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000000; + for (i = 2; i < 7; i++) + msg[i] = 0; + + return pl320_ipc_transmit(msg); +} + +static int hb_cpufreq_clk_notify(struct notifier_block *nb, + unsigned long action, void *hclk) +{ + struct clk_notifier_data *clk_data = hclk; + int i = 0; + + if (action == PRE_RATE_CHANGE) { + if (clk_data->new_rate > clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > 15) + return NOTIFY_STOP; + } else if (action == POST_RATE_CHANGE) { + if (clk_data->new_rate < clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > 15) + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block hb_cpufreq_clk_nb = { + .notifier_call = hb_cpufreq_clk_notify, +}; + +static int hb_cpufreq_driver_init(void) +{ + struct device *cpu_dev; + struct clk *cpu_clk; + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); + if (ret) { + pr_err("failed to register clk notifier: %d\n", ret); + goto out_put_node; + } + +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq 2012-12-05 16:48 ` [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf @ 2012-12-05 18:49 ` Mike Turquette 2012-12-05 22:09 ` Mark Langsdorf 2012-12-06 9:37 ` Shawn Guo 1 sibling, 1 reply; 120+ messages in thread From: Mike Turquette @ 2012-12-05 18:49 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm@vger.kernel.org, linux-arm-kernel, Shawn Guo On Wed, Dec 5, 2012 at 8:48 AM, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote: > diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c > new file mode 100644 > index 0000000..1f28fa6 > --- /dev/null > +++ b/drivers/cpufreq/highbank-cpufreq.c > @@ -0,0 +1,102 @@ Looks pretty good to me. Some tedious nitpicks and discussion below. <snip> > +static int hb_voltage_change(unsigned int freq) > +{ > + int i; > + u32 msg[7]; > + > + msg[0] = HB_CPUFREQ_CHANGE_NOTE; > + msg[1] = freq / 1000000; > + for (i = 2; i < 7; i++) > + msg[i] = 0; > + > + return pl320_ipc_transmit(msg); > +} > + > +static int hb_cpufreq_clk_notify(struct notifier_block *nb, > + unsigned long action, void *hclk) > +{ > + struct clk_notifier_data *clk_data = hclk; > + int i = 0; > + > + if (action == PRE_RATE_CHANGE) { > + if (clk_data->new_rate > clk_data->old_rate) > + while (hb_voltage_change(clk_data->new_rate)) > + if (i++ > 15) There are a few magic numbers here. How about something like: #define HB_VOLT_CHANGE_MAX_TRIES 15 Maybe do the same for the i2c message length? > + return NOTIFY_STOP; How about NOTIFY_BAD? It more clearly signals that an error has occurred. You could also return notifier_from_errno(-ETIMEDOUT) here if you prefer but that would only be for the sake of readability. clk_set_rate doesn't actually return the notifier error code in the event of a notifier abort. > + } else if (action == POST_RATE_CHANGE) { > + if (clk_data->new_rate < clk_data->old_rate) > + while (hb_voltage_change(clk_data->new_rate)) > + if (i++ > 15) > + break; Same as above. It is true that the clock framework does nothing with post-rate change notifier aborts but that might change in the future. > + } > + > + return NOTIFY_DONE; > +} > + > +static struct notifier_block hb_cpufreq_clk_nb = { > + .notifier_call = hb_cpufreq_clk_notify, > +}; > + Do you have any plans to convert your voltage change routine over to the regulator framework? Likewise do you plan to use the OPP library in the future? I can understand if you do not do that since your regulator/dvfs programming model makes things very simple for you. The reason I bring this up is that I did float a patch a while back for a generalized dvfs notifier handler. The prereqs for using it are 1) ccf, 2) regulator fwk, 3) opp definitions. Here is the patch: https://github.com/mturquette/linux/commit/05a280bbc0819a6858d73088a632666f0c7f68a4 And an example usage in the OMAP CPUfreq driver: https://github.com/mturquette/linux/commit/958f10bb98a293aa912e7eb9cd6edbdc51c1c04a I understand if this approach incurs too much software overhead for you but I wanted to throw it out there. It might working nicely in the cpufreq-cpu0 driver or some other "generic" CPUfreq driver for implementing DVFS. Regards, Mike ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq 2012-12-05 18:49 ` Mike Turquette @ 2012-12-05 22:09 ` Mark Langsdorf 0 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-05 22:09 UTC (permalink / raw) To: Mike Turquette Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Shawn Guo On 12/05/2012 12:49 PM, Mike Turquette wrote: > On Wed, Dec 5, 2012 at 8:48 AM, Mark Langsdorf > <mark.langsdorf@calxeda.com> wrote: >> diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c >> new file mode 100644 >> index 0000000..1f28fa6 >> --- /dev/null >> +++ b/drivers/cpufreq/highbank-cpufreq.c >> @@ -0,0 +1,102 @@ > > Looks pretty good to me. Some tedious nitpicks and discussion below. > <snip> > >> +static int hb_voltage_change(unsigned int freq) >> +{ >> + int i; >> + u32 msg[7]; >> + >> + msg[0] = HB_CPUFREQ_CHANGE_NOTE; >> + msg[1] = freq / 1000000; >> + for (i = 2; i < 7; i++) >> + msg[i] = 0; >> + >> + return pl320_ipc_transmit(msg); >> +} >> + >> +static int hb_cpufreq_clk_notify(struct notifier_block *nb, >> + unsigned long action, void *hclk) >> +{ >> + struct clk_notifier_data *clk_data = hclk; >> + int i = 0; >> + >> + if (action == PRE_RATE_CHANGE) { >> + if (clk_data->new_rate > clk_data->old_rate) >> + while (hb_voltage_change(clk_data->new_rate)) >> + if (i++ > 15) > > There are a few magic numbers here. How about something like: > > #define HB_VOLT_CHANGE_MAX_TRIES 15 > > Maybe do the same for the i2c message length? Fixed. >> + return NOTIFY_STOP; > > How about NOTIFY_BAD? It more clearly signals that an error has occurred. > Same as above. It is true that the clock framework does nothing with > post-rate change notifier aborts but that might change in the future. Changed and added. >> + } >> + >> + return NOTIFY_DONE; >> +} >> + >> +static struct notifier_block hb_cpufreq_clk_nb = { >> + .notifier_call = hb_cpufreq_clk_notify, >> +}; >> + > > Do you have any plans to convert your voltage change routine over to > the regulator framework? Likewise do you plan to use the OPP library > in the future? I can understand if you do not do that since your > regulator/dvfs programming model makes things very simple for you. I looked at treating the ECME as a voltage regulator, but it was a very bad fit. The ECME has a certain amount of intelligence built into it and corporate plans are to treat voltage control as a black box. The current solution is actually nicely generic from my perspective. The clk notifiers guarantee we can make the voltage changes at the right time regardless of the underlying cpufreq driver implementation. I don't think we need more until we get into cpufreq QoS issues, and even then I'd want to stick with something like the current structure. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq 2012-12-05 16:48 ` [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-12-05 18:49 ` Mike Turquette @ 2012-12-06 9:37 ` Shawn Guo 1 sibling, 0 replies; 120+ messages in thread From: Shawn Guo @ 2012-12-06 9:37 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel, mturquette On Wed, Dec 05, 2012 at 10:48:41AM -0600, Mark Langsdorf wrote: > 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> > Cc: shawn.guo@linaro.org Reviewed-by: Shawn Guo <shawn.guo@linaro.org> ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (5 preceding siblings ...) 2012-12-05 16:48 ` [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf @ 2012-12-27 13:12 ` Rafael J. Wysocki 2012-12-27 13:28 ` Mark Langsdorf 6 siblings, 1 reply; 120+ messages in thread From: Rafael J. Wysocki @ 2012-12-27 13:12 UTC (permalink / raw) To: Mark Langsdorf; +Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel On Wednesday, December 05, 2012 10:48:35 AM Mark Langsdorf wrote: > This patch series adds cpufreq support for the Calxeda > ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on > the ECX-1000 manages the voltage for the part and communications with > Linux through a pl320 mailbox. clk notifications are used to control > when to send messages to the ECME. If you want me to handle this, can you please resend the whole patchset (with all of the comments addressed, if possible)? Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* RE: [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) 2012-12-27 13:12 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki @ 2012-12-27 13:28 ` Mark Langsdorf 2012-12-27 14:43 ` Rafael J. Wysocki 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-12-27 13:28 UTC (permalink / raw) To: Rafael J. Wysocki Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org I thought I had addressed all the issues with the v9 version. The only comments I got on it was Mike's reviewed-by and I didn't think that warranted a resend. If you do want me to send it again, do you want it updated to current top-of-tree? That may take a bit, since there's an unrelated bug that's preventing my test box from booting. --Mark Langsdorf Calxeda, Inc. ________________________________________ From: Rafael J. Wysocki [rjw@sisk.pl] Sent: Thursday, December 27, 2012 7:12 AM To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org; cpufreq@vger.kernel.org; linux-pm@vger.kernel.org; linux-arm-kernel@lists.infradead.org Subject: Re: [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) On Wednesday, December 05, 2012 10:48:35 AM Mark Langsdorf wrote: > This patch series adds cpufreq support for the Calxeda > ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on > the ECX-1000 manages the voltage for the part and communications with > Linux through a pl320 mailbox. clk notifications are used to control > when to send messages to the ECME. If you want me to handle this, can you please resend the whole patchset (with all of the comments addressed, if possible)? Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) 2012-12-27 13:28 ` Mark Langsdorf @ 2012-12-27 14:43 ` Rafael J. Wysocki 0 siblings, 0 replies; 120+ messages in thread From: Rafael J. Wysocki @ 2012-12-27 14:43 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org On Thursday, December 27, 2012 08:28:22 AM Mark Langsdorf wrote: > I thought I had addressed all the issues with the v9 version. The only > comments I got on it was Mike's reviewed-by and I didn't think that > warranted a resend. > > If you do want me to send it again, do you want it updated to current > top-of-tree? Yes, please. > That may take a bit, since there's an unrelated bug that's preventing my > test box from booting. Sure. Thanks, Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/6 v9] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (8 preceding siblings ...) 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2012-12-06 22:42 ` Mark Langsdorf 2012-12-06 22:42 ` [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (5 more replies) 2013-01-04 16:35 ` [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (2 subsequent siblings) 12 siblings, 6 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on the ECX-1000 manages the voltage for the part and communications with Linux through a pl320 mailbox. clk notifications are used to control when to send messages to the ECME. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf @ 2012-12-06 22:42 ` Mark Langsdorf 2012-12-07 14:55 ` Thiago Farina 2012-12-27 5:11 ` Prashant Gaikwad 2012-12-06 22:42 ` [PATCH 2/6 v9] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (4 subsequent siblings) 5 siblings, 2 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v4, v5, v6, v7, v8 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index b22d700..af46b80 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -263,6 +266,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -273,12 +277,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) { struct clock_event_device **this_cpu_clk; - if (!twd_clk) - twd_clk = twd_get_clock(); - - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else + if (IS_ERR_OR_NULL(twd_clk)) twd_calibrate_rate(); __raw_writel(0, twd_base + TWD_TIMER_CONTROL); @@ -349,6 +348,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -383,6 +384,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock 2012-12-06 22:42 ` [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-12-07 14:55 ` Thiago Farina 2012-12-27 5:11 ` Prashant Gaikwad 1 sibling, 0 replies; 120+ messages in thread From: Thiago Farina @ 2012-12-07 14:55 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel, Rob Herring On Thu, Dec 6, 2012 at 8:42 PM, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote: > From: Rob Herring <rob.herring@calxeda.com> > > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > --- > Changes from v4, v5, v6, v7, v8 Why you keep sending these emails without real changes? Is this a rebase process or what? I saw at least two other emails where you do the same thing. Thank you! ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock 2012-12-06 22:42 ` [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-12-07 14:55 ` Thiago Farina @ 2012-12-27 5:11 ` Prashant Gaikwad 1 sibling, 0 replies; 120+ messages in thread From: Prashant Gaikwad @ 2012-12-27 5:11 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Rob Herring On Friday 07 December 2012 04:12 AM, Mark Langsdorf wrote: > From: Rob Herring <rob.herring@calxeda.com> > > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > --- > Changes from v4, v5, v6, v7, v8 > None. > Changes from v3 > No longer setting *clk to NULL in twd_get_clock(). > Changes from v2 > Turned the check for the node pointer into an if-then-else statement. > Removed the second, redundant clk_get_rate. > Changes from v1 > None. > > arch/arm/kernel/smp_twd.c | 19 +++++++++++-------- > 1 file changed, 11 insertions(+), 8 deletions(-) Hi Mark, What is the status of this patch? Regards, PrashantG > diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c > index b22d700..af46b80 100644 > --- a/arch/arm/kernel/smp_twd.c > +++ b/arch/arm/kernel/smp_twd.c > @@ -237,12 +237,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) > return IRQ_NONE; > } > ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 2/6 v9] clk, highbank: Prevent glitches in non-bypass reset mode 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf 2012-12-06 22:42 ` [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2012-12-06 22:42 ` Mark Langsdorf 2012-12-06 22:42 ` [PATCH 3/6 v9] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf ` (3 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Mike Turquette <mturquette@linaro.org> --- Changes from v6, v7, v8 None. Changes from v5 Added Mike Turquette's ack. Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1 Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 3/6 v9] cpufreq: tolerate inexact values when collecting stats 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf 2012-12-06 22:42 ` [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-12-06 22:42 ` [PATCH 2/6 v9] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2012-12-06 22:42 ` Mark Langsdorf 2012-12-06 22:42 ` [PATCH 4/6 v9] arm highbank: add support for pl320 IPC Mark Langsdorf ` (2 subsequent siblings) 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf This patch is withdrawn due to a need for severe rework. Changes from v4 Withdrawn. Changes from v3, v2 None. Changes from v1 Implemented a simple round-up algorithm instead of the over/under method that could cause errors on Intel processors with boost mode. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/6 v9] arm highbank: add support for pl320 IPC 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf ` (2 preceding siblings ...) 2012-12-06 22:42 ` [PATCH 3/6 v9] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf @ 2012-12-06 22:42 ` Mark Langsdorf 2012-12-06 22:42 ` [PATCH 5/6 v9] power: export opp cpufreq functions Mark Langsdorf 2012-12-06 22:42 ` [PATCH 6/6 v9] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, Omar Ramirez Luna, Arnd Bergmann From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. This patch depends on Omar Ramirez Luna's <omar.luna@linaro.org> mailbox driver patch series. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v6, v7, v8 None. Changes from v5 Renamed ipc_transmit() to pl320_ipc_transmit(). Properly exported pl320_ipc_{un}register_notifier(). Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 4 + drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ include/linux/mailbox.h | 19 +++- 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 drivers/mailbox/Makefile create mode 100644 drivers/mailbox/pl320-ipc.c diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 0e1d0a4..2896881 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index be8cac0..e89fdb4 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -34,4 +34,13 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile new file mode 100644 index 0000000..c9f14c3 --- /dev/null +++ b/drivers/mailbox/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_OMAP1_MBOX) += mailbox.o mailbox-omap1.o +obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox.o mailbox-omap2.o +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o + diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..1a9d8e4 --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int pl320_ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(pl320_ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_register_notifier); + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h index e8e4131..e7829e5 100644 --- a/include/linux/mailbox.h +++ b/include/linux/mailbox.h @@ -1,4 +1,16 @@ -/* mailbox.h */ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ typedef u32 mbox_msg_t; struct omap_mbox; @@ -20,3 +32,8 @@ void omap_mbox_save_ctx(struct omap_mbox *mbox); void omap_mbox_restore_ctx(struct omap_mbox *mbox); void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq); + +int pl320_ipc_transmit(u32 *data); +int pl320_ipc_register_notifier(struct notifier_block *nb); +int pl320_ipc_unregister_notifier(struct notifier_block *nb); + -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 5/6 v9] power: export opp cpufreq functions 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf ` (3 preceding siblings ...) 2012-12-06 22:42 ` [PATCH 4/6 v9] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2012-12-06 22:42 ` Mark Langsdorf 2012-12-06 22:42 ` [PATCH 6/6 v9] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 5 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf These functions are needed to make the cpufreq-core0 and highbank-cpufreq drivers loadable as modules. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Nishanth Menon <nm@ti.com> --- Changes from v4, v5, v6, v7, v8 None. Changes from v3 includes linux/export.h instead of module.h. Changes from v2 None. Changes from v1 Added Nishanth Menon's ack. Clarified the purpose of the change in the commit message. drivers/base/power/opp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d946864..4062ec3 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -23,6 +23,7 @@ #include <linux/rcupdate.h> #include <linux/opp.h> #include <linux/of.h> +#include <linux/export.h> /* * Internal data structure organization with the OPP layer library is as @@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev, return 0; } +EXPORT_SYMBOL(opp_init_cpufreq_table); /** * opp_free_cpufreq_table() - free the cpufreq table @@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev, kfree(*table); *table = NULL; } +EXPORT_SYMBOL(opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ /** @@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev) return 0; } +EXPORT_SYMBOL(of_init_opp_table); #endif -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 6/6 v9] cpufreq, highbank: add support for highbank cpufreq 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf ` (4 preceding siblings ...) 2012-12-06 22:42 ` [PATCH 5/6 v9] power: export opp cpufreq functions Mark Langsdorf @ 2012-12-06 22:42 ` Mark Langsdorf 2012-12-07 7:04 ` Mike Turquette 5 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, mturquette 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> Cc: mturquette@linaro.org --- Changes from v8 Added Shawn Guo's reviewed by. Removed some magic numbers. Changed failure returns in clk_notify from NOTIFY_STOP to NOTIFY_BAD. Changes from v7 Removed old attribution to cpufreq-cpu0. Added some description in the documentation. Made cpu_dev, cpu_clk into local variables. Removed __devinit. Removed some unneeded includes. Added a brace to clarify some nested if logic. Changes from v6 Removed devicetree bindings documentation. Restructured driver to use clk notifications. Core driver logic is now cpufreq-clk0. Changes from v5 Changed ipc_transmit() to pl320_ipc_transmit(). Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. arch/arm/boot/dts/highbank.dts | 10 ++++ arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 16 ++++++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 109 +++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 0c6fc34..7c4c27d 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + clock-latency = <100000>; }; cpu@1 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2896881..b7862da 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000 (Highbank)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e64..7aaac9f 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,19 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select GENERIC_CPUFREQ_CPU0 + select PM_OPP + select REGULATOR + + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. + diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1bc90e1..9e8f12a 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..863bb39 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver provides the clk notifier callbacks that are used when + * the cpufreq-cpu0 driver changes to frequency to alert the highbank + * EnergyCore Management Engine (ECME) about the need to change + * voltage. The ECME interfaces with the actual voltage regulators. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 +#define HB_CPUFREQ_IPC_LEN 7 +#define HB_CPUFREQ_VOLT_RETRIES 15 + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[HB_CPUFREQ_IPC_LEN]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000000; + for (i = 2; i < HB_CPUFREQ_IPC_LEN; i++) + msg[i] = 0; + + return pl320_ipc_transmit(msg); +} + +static int hb_cpufreq_clk_notify(struct notifier_block *nb, + unsigned long action, void *hclk) +{ + struct clk_notifier_data *clk_data = hclk; + int i = 0; + + if (action == PRE_RATE_CHANGE) { + if (clk_data->new_rate > clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } else if (action == POST_RATE_CHANGE) { + if (clk_data->new_rate < clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} + +static struct notifier_block hb_cpufreq_clk_nb = { + .notifier_call = hb_cpufreq_clk_notify, +}; + +static int hb_cpufreq_driver_init(void) +{ + struct device *cpu_dev; + struct clk *cpu_clk; + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); + if (ret) { + pr_err("failed to register clk notifier: %d\n", ret); + goto out_put_node; + } + +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 6/6 v9] cpufreq, highbank: add support for highbank cpufreq 2012-12-06 22:42 ` [PATCH 6/6 v9] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf @ 2012-12-07 7:04 ` Mike Turquette 0 siblings, 0 replies; 120+ messages in thread From: Mike Turquette @ 2012-12-07 7:04 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm@vger.kernel.org, linux-arm-kernel On Thu, Dec 6, 2012 at 2:42 PM, Mark Langsdorf <mark.langsdorf@calxeda.com> wrote: > 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> > Cc: mturquette@linaro.org Looks good to me. Reviewed-by: Mike Turquette <mturquette@linaro.org> Regards, Mike ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (9 preceding siblings ...) 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf @ 2013-01-04 16:35 ` Mark Langsdorf 2013-01-04 16:35 ` [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock Mark Langsdorf ` (3 more replies) 2013-01-25 19:46 ` [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-28 16:13 ` [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 12 siblings, 4 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on the ECX-1000 manages the voltage for the part and communications with Linux through a pl320 mailbox. clk notifications are used to control when to send messages to the ECME. Previous versions of this patch set include two other patches. One has been dropped as unworkable and the other got picked up and included in 3.8.0. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock 2013-01-04 16:35 ` [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2013-01-04 16:35 ` Mark Langsdorf 2013-01-10 23:34 ` Russell King - ARM Linux 2013-01-04 16:35 ` [PATCH 2/4 v10] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (2 subsequent siblings) 3 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v9 Updated to work with 3.8 kernel. Changes from v4, v5, v6, v7, v8 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 49f335d..dad2d81 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -239,12 +239,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static struct clk *twd_get_clock(struct device_node *np) { struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); + if (np) + clk = of_clk_get(np, 0); + else + clk = clk_get_sys("smp_twd", NULL); if (IS_ERR(clk)) { pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); return clk; @@ -257,6 +260,7 @@ static struct clk *twd_get_clock(void) return ERR_PTR(err); } + twd_timer_rate = clk_get_rate(clk); return clk; } @@ -285,7 +289,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) * during the runtime of the system. */ if (!common_setup_called) { - twd_clk = twd_get_clock(); + twd_clk = twd_get_clock(NULL); /* * We use IS_ERR_OR_NULL() here, because if the clock stubs @@ -373,6 +377,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; + twd_clk = twd_get_clock(NULL); + return twd_local_timer_common_register(); } @@ -405,6 +411,8 @@ void __init twd_local_timer_of_register(void) goto out; } + twd_clk = twd_get_clock(np); + err = twd_local_timer_common_register(); out: -- 1.8.0.2 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock 2013-01-04 16:35 ` [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2013-01-10 23:34 ` Russell King - ARM Linux 2013-01-11 14:40 ` Rob Herring 0 siblings, 1 reply; 120+ messages in thread From: Russell King - ARM Linux @ 2013-01-10 23:34 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel, Rob Herring Mark, Rafael just asked me to look at this patch, though I guess these comments should be directed to Rob who was the original patch author. On Fri, Jan 04, 2013 at 10:35:43AM -0600, Mark Langsdorf wrote: > diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c > index 49f335d..dad2d81 100644 > --- a/arch/arm/kernel/smp_twd.c > +++ b/arch/arm/kernel/smp_twd.c > @@ -239,12 +239,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) > return IRQ_NONE; > } > > -static struct clk *twd_get_clock(void) > +static struct clk *twd_get_clock(struct device_node *np) > { > struct clk *clk; > int err; > > - clk = clk_get_sys("smp_twd", NULL); > + if (np) > + clk = of_clk_get(np, 0); > + else > + clk = clk_get_sys("smp_twd", NULL); > if (IS_ERR(clk)) { > pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); > return clk; > @@ -257,6 +260,7 @@ static struct clk *twd_get_clock(void) > return ERR_PTR(err); > } > > + twd_timer_rate = clk_get_rate(clk); Hmm, so this overrides the later clk_get_rate() in twd_timer_setup(), making the later one redundant. However... > return clk; > } > > @@ -285,7 +289,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) > * during the runtime of the system. > */ > if (!common_setup_called) { > - twd_clk = twd_get_clock(); > + twd_clk = twd_get_clock(NULL); > > /* > * We use IS_ERR_OR_NULL() here, because if the clock stubs > @@ -373,6 +377,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) > if (!twd_base) > return -ENOMEM; > > + twd_clk = twd_get_clock(NULL); > + > return twd_local_timer_common_register(); Ok, so this sets up twd_clk, and also twd_timer_rate, but twd_local_timer_common_register() just ends up registering the set of function pointers with the local timer code. Some point later, the ->setup function is called, and that will happen with common_setup_called false. The result will be another call to twd_get_clock(). > } > > @@ -405,6 +411,8 @@ void __init twd_local_timer_of_register(void) > goto out; > } > > + twd_clk = twd_get_clock(np); > + > err = twd_local_timer_common_register(); And a similar thing happens here. Except... the twd_clk gets overwritten by the call to twd_get_clock(NULL) from twd_timer_setup(). I wonder if it would be much better to move twd_get_clock() out of twd_timer_setup() entirely, moving it into twd_local_timer_common_register(). twd_local_timer_common_register() would have to take the dev node. Also, leave the setting of twd_timer_rate in twd_timer_setup(). An alternative strategy would be to move the initialization of the timer rate also into twd_local_timer_common_register(), detect the NULL or error clock, and run the calibration from there (I don't think we can move the calibration). If that's chosen, then "common_setup_called" should probably be renamed to "twd_calibration_done". What do you think? ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock 2013-01-10 23:34 ` Russell King - ARM Linux @ 2013-01-11 14:40 ` Rob Herring 0 siblings, 0 replies; 120+ messages in thread From: Rob Herring @ 2013-01-11 14:40 UTC (permalink / raw) To: Russell King - ARM Linux Cc: Mark Langsdorf, linux-kernel, cpufreq, linux-pm, linux-arm-kernel On 01/10/2013 05:34 PM, Russell King - ARM Linux wrote: > Mark, > > Rafael just asked me to look at this patch, though I guess these comments > should be directed to Rob who was the original patch author. > > On Fri, Jan 04, 2013 at 10:35:43AM -0600, Mark Langsdorf wrote: >> diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c >> index 49f335d..dad2d81 100644 >> --- a/arch/arm/kernel/smp_twd.c >> +++ b/arch/arm/kernel/smp_twd.c >> @@ -239,12 +239,15 @@ static irqreturn_t twd_handler(int irq, void *dev_id) >> return IRQ_NONE; >> } >> >> -static struct clk *twd_get_clock(void) >> +static struct clk *twd_get_clock(struct device_node *np) >> { >> struct clk *clk; >> int err; >> >> - clk = clk_get_sys("smp_twd", NULL); >> + if (np) >> + clk = of_clk_get(np, 0); >> + else >> + clk = clk_get_sys("smp_twd", NULL); >> if (IS_ERR(clk)) { >> pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); >> return clk; >> @@ -257,6 +260,7 @@ static struct clk *twd_get_clock(void) >> return ERR_PTR(err); >> } >> >> + twd_timer_rate = clk_get_rate(clk); > > Hmm, so this overrides the later clk_get_rate() in twd_timer_setup(), making > the later one redundant. However... > >> return clk; >> } >> >> @@ -285,7 +289,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) >> * during the runtime of the system. >> */ >> if (!common_setup_called) { >> - twd_clk = twd_get_clock(); >> + twd_clk = twd_get_clock(NULL); >> >> /* >> * We use IS_ERR_OR_NULL() here, because if the clock stubs >> @@ -373,6 +377,8 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) >> if (!twd_base) >> return -ENOMEM; >> >> + twd_clk = twd_get_clock(NULL); >> + >> return twd_local_timer_common_register(); > > Ok, so this sets up twd_clk, and also twd_timer_rate, but > twd_local_timer_common_register() just ends up registering the set of > function pointers with the local timer code. Some point later, the > ->setup function is called, and that will happen with common_setup_called > false. The result will be another call to twd_get_clock(). > >> } >> >> @@ -405,6 +411,8 @@ void __init twd_local_timer_of_register(void) >> goto out; >> } >> >> + twd_clk = twd_get_clock(np); >> + >> err = twd_local_timer_common_register(); > > And a similar thing happens here. Except... the twd_clk gets overwritten > by the call to twd_get_clock(NULL) from twd_timer_setup(). > > I wonder if it would be much better to move twd_get_clock() out of > twd_timer_setup() entirely, moving it into twd_local_timer_common_register(). > twd_local_timer_common_register() would have to take the dev node. > Also, leave the setting of twd_timer_rate in twd_timer_setup(). > > An alternative strategy would be to move the initialization of the > timer rate also into twd_local_timer_common_register(), detect the > NULL or error clock, and run the calibration from there (I don't think > we can move the calibration). If that's chosen, then "common_setup_called" > should probably be renamed to "twd_calibration_done". > > What do you think? Yes, things can be simplified a bit. How about this patch? I moved the clk setup to twd_local_timer_common_register. Then we just rely on twd_timer_rate being 0 when there is no clock and we need to do calibration. Then we can get rid of common_setup_called altogether. diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index dc9bb01..2201e2d 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -30,7 +30,6 @@ static void __iomem *twd_base; static struct clk *twd_clk; static unsigned long twd_timer_rate; -static bool common_setup_called; static DEFINE_PER_CPU(bool, percpu_setup_called); static struct clock_event_device __percpu **twd_evt; @@ -238,25 +237,28 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static void twd_get_clock(struct device_node *np) { - struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); - if (IS_ERR(clk)) { - pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); - return clk; + if (np) + twd_clk = of_clk_get(np, 0); + else + twd_clk = clk_get_sys("smp_twd", NULL); + + if (IS_ERR(twd_clk)) { + pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(twd_clk)); + return; } - err = clk_prepare_enable(clk); + err = clk_prepare_enable(twd_clk); if (err) { pr_err("smp_twd: clock failed to prepare+enable: %d\n", err); - clk_put(clk); - return ERR_PTR(err); + clk_put(twd_clk); + return; } - return clk; + twd_timer_rate = clk_get_rate(twd_clk); } /* @@ -279,26 +281,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) } per_cpu(percpu_setup_called, cpu) = true; - /* - * This stuff only need to be done once for the entire TWD cluster - * during the runtime of the system. - */ - if (!common_setup_called) { - twd_clk = twd_get_clock(); - - /* - * We use IS_ERR_OR_NULL() here, because if the clock stubs - * are active we will get a valid clk reference which is - * however NULL and will return the rate 0. In that case we - * need to calibrate the rate instead. - */ - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else - twd_calibrate_rate(); - - common_setup_called = true; - } + twd_calibrate_rate(); /* * The following is done once per CPU the first time .setup() is @@ -329,7 +312,7 @@ static struct local_timer_ops twd_lt_ops __cpuinitdata = { .stop = twd_timer_stop, }; -static int __init twd_local_timer_common_register(void) +static int __init twd_local_timer_common_register(struct device_node *np) { int err; @@ -349,6 +332,8 @@ static int __init twd_local_timer_common_register(void) if (err) goto out_irq; + twd_get_clock(np); + return 0; out_irq: @@ -372,7 +357,7 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; - return twd_local_timer_common_register(); + return twd_local_timer_common_register(NULL); } #ifdef CONFIG_OF @@ -404,7 +389,7 @@ void __init twd_local_timer_of_register(void) goto out; } - err = twd_local_timer_common_register(); + err = twd_local_timer_common_register(np); out: WARN(err, "twd_local_timer_of_register failed (%d)\n", err); ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/4 v10] clk, highbank: Prevent glitches in non-bypass reset mode 2013-01-04 16:35 ` [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-04 16:35 ` [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock Mark Langsdorf @ 2013-01-04 16:35 ` Mark Langsdorf 2013-01-04 16:35 ` [PATCH 3/4 v10] arm highbank: add support for pl320 IPC Mark Langsdorf 2013-01-04 16:35 ` [PATCH 4/4 v10] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Mike Turquette <mturquette@linaro.org> --- Changes from v6, v7, v8, v9 None. Changes from v5 Added Mike Turquette's ack. Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1 Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.8.0.2 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 3/4 v10] arm highbank: add support for pl320 IPC 2013-01-04 16:35 ` [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-04 16:35 ` [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock Mark Langsdorf 2013-01-04 16:35 ` [PATCH 2/4 v10] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2013-01-04 16:35 ` Mark Langsdorf 2013-01-04 16:35 ` [PATCH 4/4 v10] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, Omar Ramirez Luna, Arnd Bergmann From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. This patch depends on Omar Ramirez Luna's <omar.luna@linaro.org> mailbox driver patch series. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v9 Used to be the 4th patch in the series. Changes from v6, v7, v8 None. Changes from v5 Renamed ipc_transmit() to pl320_ipc_transmit(). Properly exported pl320_ipc_{un}register_notifier(). Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 6 +- drivers/mailbox/Makefile.rej | 7 ++ drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 drivers/mailbox/Makefile.rej create mode 100644 drivers/mailbox/pl320-ipc.c diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 551c97e..2388085 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index be8cac0..e89fdb4 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -34,4 +34,13 @@ config OMAP_MBOX_KFIFO_SIZE This can also be changed at runtime (via the mbox_kfifo_size module parameter). +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index fa71fab..dc3fbc5 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_MAILBOX) += mailbox.o +obj-$(CONFIG_OMAP1_MBOX) += mailbox-omap1.o mailbox.o +obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox-omap2.o mailbox.o +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o -obj-$(CONFIG_OMAP1_MBOX) += mailbox-omap1.o -obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox-omap2.o diff --git a/drivers/mailbox/Makefile.rej b/drivers/mailbox/Makefile.rej new file mode 100644 index 0000000..62ade60 --- /dev/null +++ b/drivers/mailbox/Makefile.rej @@ -0,0 +1,7 @@ +--- /dev/null ++++ drivers/mailbox/Makefile +@@ -0,0 +1,4 @@ ++obj-$(CONFIG_OMAP1_MBOX) += mailbox.o mailbox-omap1.o ++obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox.o mailbox-omap2.o ++obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o ++ diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..1a9d8e4 --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int pl320_ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(pl320_ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_register_notifier); + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); + +static int __devinit pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); -- 1.8.0.2 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 4/4 v10] cpufreq, highbank: add support for highbank cpufreq 2013-01-04 16:35 ` [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (2 preceding siblings ...) 2013-01-04 16:35 ` [PATCH 3/4 v10] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2013-01-04 16:35 ` Mark Langsdorf 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf 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> --- Changes from v9 Added Mike Turquette's reviewed by. Used to be the 6th patch in the series. Changes from v8 Added Shawn Guo's reviewed by. Removed some magic numbers. Changed failure returns in clk_notify from NOTIFY_STOP to NOTIFY_BAD. Changes from v7 Removed old attribution to cpufreq-cpu0. Added some description in the documentation. Made cpu_dev, cpu_clk into local variables. Removed __devinit. Removed some unneeded includes. Added a brace to clarify some nested if logic. Changes from v6 Removed devicetree bindings documentation. Restructured driver to use clk notifications. Core driver logic is now cpufreq-clk0. Changes from v5 Changed ipc_transmit() to pl320_ipc_transmit(). Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. arch/arm/boot/dts/highbank.dts | 10 ++++ arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 15 +++++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/highbank-cpufreq.c | 109 +++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+) create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index a9ae5d3..202f12e 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -36,6 +36,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + clock-latency = <100000>; }; cpu@1 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2388085..44b12f9 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index a0b3661..7c71c1b 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -83,3 +83,18 @@ config ARM_SPEAR_CPUFREQ default y help This adds the CPUFreq driver support for SPEAr SOCs. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select GENERIC_CPUFREQ_CPU0 + select PM_OPP + select REGULATOR + + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1f254ec0..2f7ab0b 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..8c85608 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver provides the clk notifier callbacks that are used when + * the cpufreq-cpu0 driver changes to frequency to alert the highbank + * EnergyCore Management Engine (ECME) about the need to change + * voltage. The ECME interfaces with the actual voltage regulators. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 +#define HB_CPUFREQ_IPC_LEN 7 +#define HB_CPUFREQ_VOLT_RETRIES 15 + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[HB_CPUFREQ_IPC_LEN]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000000; + for (i = 2; i < HB_CPUFREQ_IPC_LEN; i++) + msg[i] = 0; + + return pl320_ipc_transmit(msg); +} + +static int hb_cpufreq_clk_notify(struct notifier_block *nb, + unsigned long action, void *hclk) +{ + struct clk_notifier_data *clk_data = hclk; + int i = 0; + + if (action == PRE_RATE_CHANGE) { + if (clk_data->new_rate > clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } else if (action == POST_RATE_CHANGE) { + if (clk_data->new_rate < clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} + +static struct notifier_block hb_cpufreq_clk_nb = { + .notifier_call = hb_cpufreq_clk_notify, +}; + +static int hb_cpufreq_driver_init(void) +{ + struct device *cpu_dev; + struct clk *cpu_clk; + struct device_node *np; + int ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); + if (ret) { + pr_err("failed to register clk notifier: %d\n", ret); + goto out_put_node; + } + +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.8.0.2 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (10 preceding siblings ...) 2013-01-04 16:35 ` [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2013-01-25 19:46 ` Mark Langsdorf 2013-01-25 19:46 ` [PATCH 1/4 v11] arm: use device tree to get smp_twd clock Mark Langsdorf ` (3 more replies) 2013-01-28 16:13 ` [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 12 siblings, 4 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on the ECX-1000 manages the voltage for the part and communications with Linux through a pl320 mailbox. clk notifications are used to control when to send messages to the ECME. Previous versions of this patch set include two other patches. One has been dropped as unworkable and the other turned out to be unnecessary. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/4 v11] arm: use device tree to get smp_twd clock 2013-01-25 19:46 ` [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2013-01-25 19:46 ` Mark Langsdorf 2013-01-25 21:03 ` Rafael J. Wysocki 2013-01-25 19:46 ` [PATCH 2/4 v11] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (2 subsequent siblings) 3 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Move clk setup to twd_local_timer_common_register and rely on twd_timer_rate being 0 to force calibration if there is no clock. Remove common_setup_called as it is no longer needed. Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> --- Changes from v10 Reworked to simplify the logic as suggested by Russell King. Changes from v9 Updated to work with 3.8 kernel. Changes from v4, v5, v6, v7, v8 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 53 +++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 49f335d..ae0c7bb 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -31,7 +31,6 @@ static void __iomem *twd_base; static struct clk *twd_clk; static unsigned long twd_timer_rate; -static bool common_setup_called; static DEFINE_PER_CPU(bool, percpu_setup_called); static struct clock_event_device __percpu **twd_evt; @@ -239,25 +238,28 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static void twd_get_clock(struct device_node *np) { - struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); - if (IS_ERR(clk)) { - pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); - return clk; + if (np) + twd_clk = of_clk_get(np, 0); + else + twd_clk = clk_get_sys("smp_twd", NULL); + + if (IS_ERR(twd_clk)) { + pr_err("smp_twd: clock not found %d\n", (int) PTR_ERR(twd_clk)); + return; } - err = clk_prepare_enable(clk); + err = clk_prepare_enable(twd_clk); if (err) { pr_err("smp_twd: clock failed to prepare+enable: %d\n", err); - clk_put(clk); - return ERR_PTR(err); + clk_put(twd_clk); + return; } - return clk; + twd_timer_rate = clk_get_rate(twd_clk); } /* @@ -280,26 +282,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) } per_cpu(percpu_setup_called, cpu) = true; - /* - * This stuff only need to be done once for the entire TWD cluster - * during the runtime of the system. - */ - if (!common_setup_called) { - twd_clk = twd_get_clock(); - - /* - * We use IS_ERR_OR_NULL() here, because if the clock stubs - * are active we will get a valid clk reference which is - * however NULL and will return the rate 0. In that case we - * need to calibrate the rate instead. - */ - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else - twd_calibrate_rate(); - - common_setup_called = true; - } + twd_calibrate_rate(); /* * The following is done once per CPU the first time .setup() is @@ -330,7 +313,7 @@ static struct local_timer_ops twd_lt_ops __cpuinitdata = { .stop = twd_timer_stop, }; -static int __init twd_local_timer_common_register(void) +static int __init twd_local_timer_common_register(struct device_node *np) { int err; @@ -350,6 +333,8 @@ static int __init twd_local_timer_common_register(void) if (err) goto out_irq; + twd_get_clock(np); + return 0; out_irq: @@ -373,7 +358,7 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; - return twd_local_timer_common_register(); + return twd_local_timer_common_register(NULL); } #ifdef CONFIG_OF @@ -405,7 +390,7 @@ void __init twd_local_timer_of_register(void) goto out; } - err = twd_local_timer_common_register(); + err = twd_local_timer_common_register(np); out: WARN(err, "twd_local_timer_of_register failed (%d)\n", err); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 1/4 v11] arm: use device tree to get smp_twd clock 2013-01-25 19:46 ` [PATCH 1/4 v11] arm: use device tree to get smp_twd clock Mark Langsdorf @ 2013-01-25 21:03 ` Rafael J. Wysocki 2013-01-25 21:40 ` Russell King - ARM Linux 0 siblings, 1 reply; 120+ messages in thread From: Rafael J. Wysocki @ 2013-01-25 21:03 UTC (permalink / raw) To: Mark Langsdorf, Russell King - ARM Linux Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel, Rob Herring On Friday, January 25, 2013 01:46:42 PM Mark Langsdorf wrote: > From: Rob Herring <rob.herring@calxeda.com> > > Move clk setup to twd_local_timer_common_register and rely on > twd_timer_rate being 0 to force calibration if there is no clock. > Remove common_setup_called as it is no longer needed. > > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Russell, is this fine with you? Rafael > --- > Changes from v10 > Reworked to simplify the logic as suggested by Russell King. > Changes from v9 > Updated to work with 3.8 kernel. > Changes from v4, v5, v6, v7, v8 > None. > Changes from v3 > No longer setting *clk to NULL in twd_get_clock(). > Changes from v2 > Turned the check for the node pointer into an if-then-else statement. > Removed the second, redundant clk_get_rate. > Changes from v1 > None. > > arch/arm/kernel/smp_twd.c | 53 +++++++++++++++++------------------------------ > 1 file changed, 19 insertions(+), 34 deletions(-) > > diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c > index 49f335d..ae0c7bb 100644 > --- a/arch/arm/kernel/smp_twd.c > +++ b/arch/arm/kernel/smp_twd.c > @@ -31,7 +31,6 @@ static void __iomem *twd_base; > > static struct clk *twd_clk; > static unsigned long twd_timer_rate; > -static bool common_setup_called; > static DEFINE_PER_CPU(bool, percpu_setup_called); > > static struct clock_event_device __percpu **twd_evt; > @@ -239,25 +238,28 @@ static irqreturn_t twd_handler(int irq, void *dev_id) > return IRQ_NONE; > } > > -static struct clk *twd_get_clock(void) > +static void twd_get_clock(struct device_node *np) > { > - struct clk *clk; > int err; > > - clk = clk_get_sys("smp_twd", NULL); > - if (IS_ERR(clk)) { > - pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); > - return clk; > + if (np) > + twd_clk = of_clk_get(np, 0); > + else > + twd_clk = clk_get_sys("smp_twd", NULL); > + > + if (IS_ERR(twd_clk)) { > + pr_err("smp_twd: clock not found %d\n", (int) PTR_ERR(twd_clk)); > + return; > } > > - err = clk_prepare_enable(clk); > + err = clk_prepare_enable(twd_clk); > if (err) { > pr_err("smp_twd: clock failed to prepare+enable: %d\n", err); > - clk_put(clk); > - return ERR_PTR(err); > + clk_put(twd_clk); > + return; > } > > - return clk; > + twd_timer_rate = clk_get_rate(twd_clk); > } > > /* > @@ -280,26 +282,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) > } > per_cpu(percpu_setup_called, cpu) = true; > > - /* > - * This stuff only need to be done once for the entire TWD cluster > - * during the runtime of the system. > - */ > - if (!common_setup_called) { > - twd_clk = twd_get_clock(); > - > - /* > - * We use IS_ERR_OR_NULL() here, because if the clock stubs > - * are active we will get a valid clk reference which is > - * however NULL and will return the rate 0. In that case we > - * need to calibrate the rate instead. > - */ > - if (!IS_ERR_OR_NULL(twd_clk)) > - twd_timer_rate = clk_get_rate(twd_clk); > - else > - twd_calibrate_rate(); > - > - common_setup_called = true; > - } > + twd_calibrate_rate(); > > /* > * The following is done once per CPU the first time .setup() is > @@ -330,7 +313,7 @@ static struct local_timer_ops twd_lt_ops __cpuinitdata = { > .stop = twd_timer_stop, > }; > > -static int __init twd_local_timer_common_register(void) > +static int __init twd_local_timer_common_register(struct device_node *np) > { > int err; > > @@ -350,6 +333,8 @@ static int __init twd_local_timer_common_register(void) > if (err) > goto out_irq; > > + twd_get_clock(np); > + > return 0; > > out_irq: > @@ -373,7 +358,7 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) > if (!twd_base) > return -ENOMEM; > > - return twd_local_timer_common_register(); > + return twd_local_timer_common_register(NULL); > } > > #ifdef CONFIG_OF > @@ -405,7 +390,7 @@ void __init twd_local_timer_of_register(void) > goto out; > } > > - err = twd_local_timer_common_register(); > + err = twd_local_timer_common_register(np); > > out: > WARN(err, "twd_local_timer_of_register failed (%d)\n", err); > -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/4 v11] arm: use device tree to get smp_twd clock 2013-01-25 21:03 ` Rafael J. Wysocki @ 2013-01-25 21:40 ` Russell King - ARM Linux 2013-01-25 22:15 ` Rafael J. Wysocki 0 siblings, 1 reply; 120+ messages in thread From: Russell King - ARM Linux @ 2013-01-25 21:40 UTC (permalink / raw) To: Rafael J. Wysocki Cc: Mark Langsdorf, linux-kernel, cpufreq, linux-pm, linux-arm-kernel, Rob Herring On Fri, Jan 25, 2013 at 10:03:05PM +0100, Rafael J. Wysocki wrote: > On Friday, January 25, 2013 01:46:42 PM Mark Langsdorf wrote: > > From: Rob Herring <rob.herring@calxeda.com> > > > > Move clk setup to twd_local_timer_common_register and rely on > > twd_timer_rate being 0 to force calibration if there is no clock. > > Remove common_setup_called as it is no longer needed. > > > > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > > Russell, is this fine with you? Looks fine to me, though I had to check twd_calibrate_rate() to make sure it wouldn't run if the twd_timer_rate was non-zero. If you want an acked-by: Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> Thanks. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 1/4 v11] arm: use device tree to get smp_twd clock 2013-01-25 21:40 ` Russell King - ARM Linux @ 2013-01-25 22:15 ` Rafael J. Wysocki 0 siblings, 0 replies; 120+ messages in thread From: Rafael J. Wysocki @ 2013-01-25 22:15 UTC (permalink / raw) To: Russell King - ARM Linux Cc: Mark Langsdorf, linux-kernel, cpufreq, linux-pm, linux-arm-kernel, Rob Herring On Friday, January 25, 2013 09:40:00 PM Russell King - ARM Linux wrote: > On Fri, Jan 25, 2013 at 10:03:05PM +0100, Rafael J. Wysocki wrote: > > On Friday, January 25, 2013 01:46:42 PM Mark Langsdorf wrote: > > > From: Rob Herring <rob.herring@calxeda.com> > > > > > > Move clk setup to twd_local_timer_common_register and rely on > > > twd_timer_rate being 0 to force calibration if there is no clock. > > > Remove common_setup_called as it is no longer needed. > > > > > > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > > > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > > > > Russell, is this fine with you? > > Looks fine to me, though I had to check twd_calibrate_rate() to make > sure it wouldn't run if the twd_timer_rate was non-zero. > > If you want an acked-by: > > Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> I will, thanks a lot! Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 2/4 v11] clk, highbank: Prevent glitches in non-bypass reset mode 2013-01-25 19:46 ` [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-25 19:46 ` [PATCH 1/4 v11] arm: use device tree to get smp_twd clock Mark Langsdorf @ 2013-01-25 19:46 ` Mark Langsdorf 2013-01-25 19:46 ` [PATCH 3/4 v11] arm highbank: add support for pl320 IPC Mark Langsdorf 2013-01-25 19:46 ` [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Mike Turquette <mturquette@linaro.org> --- Changes from v6, v7, v8, v9, v10 None. Changes from v5 Added Mike Turquette's ack. Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1 Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 3/4 v11] arm highbank: add support for pl320 IPC 2013-01-25 19:46 ` [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-25 19:46 ` [PATCH 1/4 v11] arm: use device tree to get smp_twd clock Mark Langsdorf 2013-01-25 19:46 ` [PATCH 2/4 v11] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2013-01-25 19:46 ` Mark Langsdorf 2013-01-28 12:49 ` Rafael J. Wysocki 2013-01-25 19:46 ` [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 3 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, Omar Ramirez Luna, Arnd Bergmann From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v10 Removed deependency on Omar Ramirez Luna's mailbox code. Now the patch creates the directory itself. Changes from v9 Used to be the 4th patch in the series. Changes from v6, v7, v8 None. Changes from v5 Renamed ipc_transmit() to pl320_ipc_transmit(). Properly exported pl320_ipc_{un}register_notifier(). Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/mailbox/Kconfig | 18 ++++ drivers/mailbox/Makefile | 1 + drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ include/linux/mailbox.h | 18 ++++ 5 files changed, 238 insertions(+) create mode 100644 drivers/mailbox/Kconfig create mode 100644 drivers/mailbox/Makefile create mode 100644 drivers/mailbox/pl320-ipc.c create mode 100644 include/linux/mailbox.h diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 551c97e..2388085 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig new file mode 100644 index 0000000..9489554 --- /dev/null +++ b/drivers/mailbox/Kconfig @@ -0,0 +1,18 @@ +menuconfig MAILBOX + bool "Mailbox Hardware Support" + help + Mailbox is a framework to control hardware communication between + on-chip processors through queued messages and interrupt driven + signals. Say Y if your platform supports hardware mailboxes. + +if MAILBOX +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + +endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile new file mode 100644 index 0000000..543ad6a --- /dev/null +++ b/drivers/mailbox/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..68c0d50 --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int pl320_ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL(pl320_ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_register_notifier); + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); + +static int __init pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h new file mode 100644 index 0000000..911cb28 --- /dev/null +++ b/include/linux/mailbox.h @@ -0,0 +1,18 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +int pl320_ipc_transmit(u32 *data); +int pl320_ipc_register_notifier(struct notifier_block *nb); +int pl320_ipc_unregister_notifier(struct notifier_block *nb); + -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 3/4 v11] arm highbank: add support for pl320 IPC 2013-01-25 19:46 ` [PATCH 3/4 v11] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2013-01-28 12:49 ` Rafael J. Wysocki 2013-01-28 13:44 ` Mark Langsdorf 0 siblings, 1 reply; 120+ messages in thread From: Rafael J. Wysocki @ 2013-01-28 12:49 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel, Rob Herring, Omar Ramirez Luna, Arnd Bergmann On Friday, January 25, 2013 01:46:44 PM Mark Langsdorf wrote: > From: Rob Herring <rob.herring@calxeda.com> > > The pl320 IPC allows for interprocessor communication between the highbank A9 > and the EnergyCore Management Engine. The pl320 implements a straightforward > mailbox protocol. > > Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> > Signed-off-by: Rob Herring <rob.herring@calxeda.com> > Cc: Omar Ramirez Luna <omar.luna@linaro.org> > Cc: Arnd Bergmann <arnd@arndb.de> > --- > Changes from v10 > Removed deependency on Omar Ramirez Luna's mailbox code. Now the > patch creates the directory itself. > Changes from v9 > Used to be the 4th patch in the series. > Changes from v6, v7, v8 > None. > Changes from v5 > Renamed ipc_transmit() to pl320_ipc_transmit(). > Properly exported pl320_ipc_{un}register_notifier(). > Changes from v4 > Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. > Moved header information to include/linux/mailbox.h. > Added Kconfig options to reflect the new code location. > Change drivers/mailbox/Makefile to build the omap mailboxes only > when they are configured. > Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. > Changes from v3, v2 > None. > Changes from v1 > Removed erroneous changes for cpufreq Kconfig. > > arch/arm/mach-highbank/Kconfig | 2 + > drivers/mailbox/Kconfig | 18 ++++ > drivers/mailbox/Makefile | 1 + > drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ > include/linux/mailbox.h | 18 ++++ > 5 files changed, 238 insertions(+) > create mode 100644 drivers/mailbox/Kconfig > create mode 100644 drivers/mailbox/Makefile > create mode 100644 drivers/mailbox/pl320-ipc.c > create mode 100644 include/linux/mailbox.h > > diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig > index 551c97e..2388085 100644 > --- a/arch/arm/mach-highbank/Kconfig > +++ b/arch/arm/mach-highbank/Kconfig > @@ -11,5 +11,7 @@ config ARCH_HIGHBANK > select GENERIC_CLOCKEVENTS > select HAVE_ARM_SCU > select HAVE_SMP > + select MAILBOX > + select PL320_MBOX > select SPARSE_IRQ > select USE_OF > diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig > new file mode 100644 > index 0000000..9489554 > --- /dev/null > +++ b/drivers/mailbox/Kconfig > @@ -0,0 +1,18 @@ > +menuconfig MAILBOX > + bool "Mailbox Hardware Support" > + help > + Mailbox is a framework to control hardware communication between > + on-chip processors through queued messages and interrupt driven > + signals. Say Y if your platform supports hardware mailboxes. > + > +if MAILBOX > +config PL320_MBOX > + bool "ARM PL320 Mailbox" > + help > + An implementation of the ARM PL320 Interprocessor Communication > + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to > + send short messages between Highbank's A9 cores and the EnergyCore > + Management Engine, primarily for cpufreq. Say Y here if you want > + to use the PL320 IPCM support. > + > +endif > diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile > new file mode 100644 > index 0000000..543ad6a > --- /dev/null > +++ b/drivers/mailbox/Makefile > @@ -0,0 +1 @@ > +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o > diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c > new file mode 100644 > index 0000000..68c0d50 > --- /dev/null > +++ b/drivers/mailbox/pl320-ipc.c > @@ -0,0 +1,199 @@ > +/* > + * Copyright 2012 Calxeda, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > +#include <linux/types.h> > +#include <linux/err.h> > +#include <linux/delay.h> > +#include <linux/export.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/completion.h> > +#include <linux/mutex.h> > +#include <linux/notifier.h> > +#include <linux/spinlock.h> > +#include <linux/device.h> > +#include <linux/amba/bus.h> > + > +#include <linux/mailbox.h> > + > +#define IPCMxSOURCE(m) ((m) * 0x40) > +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) > +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) > +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) > +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) > +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) > +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) > +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) > +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) > +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) > + > +#define IPCMMIS(irq) (((irq) * 8) + 0x800) > +#define IPCMRIS(irq) (((irq) * 8) + 0x804) > + > +#define MBOX_MASK(n) (1 << (n)) > +#define IPC_TX_MBOX 1 > +#define IPC_RX_MBOX 2 > + > +#define CHAN_MASK(n) (1 << (n)) > +#define A9_SOURCE 1 > +#define M3_SOURCE 0 > + > +static void __iomem *ipc_base; > +static int ipc_irq; > +static DEFINE_MUTEX(ipc_m1_lock); > +static DECLARE_COMPLETION(ipc_completion); > +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); > + > +static inline void set_destination(int source, int mbox) > +{ > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); > +} > + > +static inline void clear_destination(int source, int mbox) > +{ > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); > + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); > +} > + > +static void __ipc_send(int mbox, u32 *data) > +{ > + int i; > + for (i = 0; i < 7; i++) > + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); > + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); > +} > + > +static u32 __ipc_rcv(int mbox, u32 *data) > +{ > + int i; > + for (i = 0; i < 7; i++) > + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); > + return data[1]; > +} > + > +/* blocking implmentation from the A9 side, not usuable in interrupts! */ > +int pl320_ipc_transmit(u32 *data) > +{ > + int ret; > + > + mutex_lock(&ipc_m1_lock); > + > + init_completion(&ipc_completion); > + __ipc_send(IPC_TX_MBOX, data); > + ret = wait_for_completion_timeout(&ipc_completion, > + msecs_to_jiffies(1000)); > + if (ret == 0) { > + ret = -ETIMEDOUT; > + goto out; > + } > + > + ret = __ipc_rcv(IPC_TX_MBOX, data); > +out: > + mutex_unlock(&ipc_m1_lock); > + return ret; > +} > +EXPORT_SYMBOL(pl320_ipc_transmit); > + > +irqreturn_t ipc_handler(int irq, void *dev) > +{ > + u32 irq_stat; > + u32 data[7]; > + > + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); > + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { > + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); > + complete(&ipc_completion); > + } > + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { > + __ipc_rcv(IPC_RX_MBOX, data); > + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); > + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); > + } > + > + return IRQ_HANDLED; > +} > + > +int pl320_ipc_register_notifier(struct notifier_block *nb) > +{ > + return atomic_notifier_chain_register(&ipc_notifier, nb); > +} > +EXPORT_SYMBOL(pl320_ipc_register_notifier); > + > +int pl320_ipc_unregister_notifier(struct notifier_block *nb) > +{ > + return atomic_notifier_chain_unregister(&ipc_notifier, nb); > +} > +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); I need all of your symbols to be exported with EXPORT_SYMBOL_GPL(). Is it OK to make that change when applying the patch or do you want to send a new one? Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/4 v11] arm highbank: add support for pl320 IPC 2013-01-28 12:49 ` Rafael J. Wysocki @ 2013-01-28 13:44 ` Mark Langsdorf 2013-01-28 20:48 ` Rafael J. Wysocki 0 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2013-01-28 13:44 UTC (permalink / raw) To: Rafael J. Wysocki Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Rob Herring, Omar Ramirez Luna, Arnd Bergmann On 01/28/2013 06:49 AM, Rafael J. Wysocki wrote: >> +int pl320_ipc_register_notifier(struct notifier_block *nb) >> +{ >> + return atomic_notifier_chain_register(&ipc_notifier, nb); >> +} >> +EXPORT_SYMBOL(pl320_ipc_register_notifier); >> + >> +int pl320_ipc_unregister_notifier(struct notifier_block *nb) >> +{ >> + return atomic_notifier_chain_unregister(&ipc_notifier, nb); >> +} >> +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); > > I need all of your symbols to be exported with EXPORT_SYMBOL_GPL(). > > Is it OK to make that change when applying the patch or do you want to send > a new one? I probably should resend so I can include the drivers level Kconfig and Makefile. I'll get that out this morning. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 3/4 v11] arm highbank: add support for pl320 IPC 2013-01-28 13:44 ` Mark Langsdorf @ 2013-01-28 20:48 ` Rafael J. Wysocki 0 siblings, 0 replies; 120+ messages in thread From: Rafael J. Wysocki @ 2013-01-28 20:48 UTC (permalink / raw) To: Mark Langsdorf Cc: linux-kernel@vger.kernel.org, cpufreq@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Rob Herring, Omar Ramirez Luna, Arnd Bergmann On Monday, January 28, 2013 07:44:13 AM Mark Langsdorf wrote: > On 01/28/2013 06:49 AM, Rafael J. Wysocki wrote: > > >> +int pl320_ipc_register_notifier(struct notifier_block *nb) > >> +{ > >> + return atomic_notifier_chain_register(&ipc_notifier, nb); > >> +} > >> +EXPORT_SYMBOL(pl320_ipc_register_notifier); > >> + > >> +int pl320_ipc_unregister_notifier(struct notifier_block *nb) > >> +{ > >> + return atomic_notifier_chain_unregister(&ipc_notifier, nb); > >> +} > >> +EXPORT_SYMBOL(pl320_ipc_unregister_notifier); > > > > I need all of your symbols to be exported with EXPORT_SYMBOL_GPL(). > > > > Is it OK to make that change when applying the patch or do you want to send > > a new one? > > I probably should resend so I can include the drivers level Kconfig and > Makefile. > > I'll get that out this morning. OK, thanks! Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq 2013-01-25 19:46 ` [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (2 preceding siblings ...) 2013-01-25 19:46 ` [PATCH 3/4 v11] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2013-01-25 19:46 ` Mark Langsdorf 2013-01-26 14:39 ` Shawn Guo 3 siblings, 1 reply; 120+ messages in thread From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf 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> --- Changes from v10 Now finds a cpu node by searching under /cpus and looking for something with an operating-points descriptor. This applies to both highbank-cpufreq and cpufreq-cpu0. Changes from v9 Added Mike Turquette's reviewed by. Used to be the 6th patch in the series. Changes from v8 Added Shawn Guo's reviewed by. Removed some magic numbers. Changed failure returns in clk_notify from NOTIFY_STOP to NOTIFY_BAD. Changes from v7 Removed old attribution to cpufreq-cpu0. Added some description in the documentation. Made cpu_dev, cpu_clk into local variables. Removed __devinit. Removed some unneeded includes. Added a brace to clarify some nested if logic. Changes from v6 Removed devicetree bindings documentation. Restructured driver to use clk notifications. Core driver logic is now cpufreq-clk0. Changes from v5 Changed ipc_transmit() to pl320_ipc_transmit(). Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. arch/arm/boot/dts/highbank.dts | 10 ++++ arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 15 +++++ drivers/cpufreq/Makefile | 3 +- drivers/cpufreq/cpufreq-cpu0.c | 6 +- drivers/cpufreq/highbank-cpufreq.c | 112 +++++++++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 5927a8d..6aad34a 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -37,6 +37,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + clock-latency = <100000>; }; cpu@901 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2388085..44b12f9 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index a0b3661..ffe55b8 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -83,3 +83,18 @@ config ARM_SPEAR_CPUFREQ default y help This adds the CPUFreq driver support for SPEAr SOCs. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select GENERIC_CPUFREQ_CPU0 + select PM_OPP + select REGULATOR + + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index fadc4d4..31e6f19 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,8 +50,9 @@ obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o -obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 52bf36d..90e9d73 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -179,7 +179,11 @@ static int cpu0_cpufreq_driver_init(void) struct device_node *np; int ret; - np = of_find_node_by_path("/cpus/cpu@0"); + for_each_child_of_node(of_find_node_by_path("/cpus"), np) { + if (of_get_property(np, "operating-points", NULL)) + break; + } + if (!np) { pr_err("failed to find cpu0 node\n"); return -ENOENT; diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..a5bad89 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver provides the clk notifier callbacks that are used when + * the cpufreq-cpu0 driver changes to frequency to alert the highbank + * EnergyCore Management Engine (ECME) about the need to change + * voltage. The ECME interfaces with the actual voltage regulators. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 +#define HB_CPUFREQ_IPC_LEN 7 +#define HB_CPUFREQ_VOLT_RETRIES 15 + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[HB_CPUFREQ_IPC_LEN]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000000; + for (i = 2; i < HB_CPUFREQ_IPC_LEN; i++) + msg[i] = 0; + + return pl320_ipc_transmit(msg); +} + +static int hb_cpufreq_clk_notify(struct notifier_block *nb, + unsigned long action, void *hclk) +{ + struct clk_notifier_data *clk_data = hclk; + int i = 0; + + if (action == PRE_RATE_CHANGE) { + if (clk_data->new_rate > clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } else if (action == POST_RATE_CHANGE) { + if (clk_data->new_rate < clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} + +static struct notifier_block hb_cpufreq_clk_nb = { + .notifier_call = hb_cpufreq_clk_notify, +}; + +static int hb_cpufreq_driver_init(void) +{ + struct device *cpu_dev; + struct clk *cpu_clk; + struct device_node *np; + int ret; + + for_each_child_of_node(of_find_node_by_path("/cpus"), np) + if (of_get_property(np, "operating-points", NULL)) + break; + + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); + if (ret) { + pr_err("failed to register clk notifier: %d\n", ret); + goto out_put_node; + } + +out_put_node: + of_node_put(np); + return ret; +} +module_init(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* Re: [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq 2013-01-25 19:46 ` [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf @ 2013-01-26 14:39 ` Shawn Guo 2013-01-26 22:24 ` Rafael J. Wysocki 0 siblings, 1 reply; 120+ messages in thread From: Shawn Guo @ 2013-01-26 14:39 UTC (permalink / raw) To: Mark Langsdorf; +Cc: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Mark, On Fri, Jan 25, 2013 at 01:46:45PM -0600, Mark Langsdorf wrote: > +static int hb_cpufreq_driver_init(void) > +{ > + struct device *cpu_dev; > + struct clk *cpu_clk; > + struct device_node *np; > + int ret; > + > + for_each_child_of_node(of_find_node_by_path("/cpus"), np) > + if (of_get_property(np, "operating-points", NULL)) > + break; > + > + if (!np) { > + pr_err("failed to find highbank cpufreq node\n"); > + return -ENOENT; > + } > + ... > +out_put_node: > + of_node_put(np); > + return ret; > +} > +module_init(hb_cpufreq_driver_init); As we are moving to multiplatform build, this init function will run on all other platforms built together with highbank. We should probably eliminate that effect. cpufreq-cpu0 driver is facing the same problem, and I just sent a patch "cpufreq: instantiate cpufreq-cpu0 as a platform_driver" to address that. Shawn ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq 2013-01-26 14:39 ` Shawn Guo @ 2013-01-26 22:24 ` Rafael J. Wysocki 2013-01-28 8:37 ` Shawn Guo 0 siblings, 1 reply; 120+ messages in thread From: Rafael J. Wysocki @ 2013-01-26 22:24 UTC (permalink / raw) To: Shawn Guo Cc: Mark Langsdorf, linux-kernel, cpufreq, linux-pm, linux-arm-kernel On Saturday, January 26, 2013 10:39:53 PM Shawn Guo wrote: > Mark, > > On Fri, Jan 25, 2013 at 01:46:45PM -0600, Mark Langsdorf wrote: > > +static int hb_cpufreq_driver_init(void) > > +{ > > + struct device *cpu_dev; > > + struct clk *cpu_clk; > > + struct device_node *np; > > + int ret; > > + > > + for_each_child_of_node(of_find_node_by_path("/cpus"), np) > > + if (of_get_property(np, "operating-points", NULL)) > > + break; > > + > > + if (!np) { > > + pr_err("failed to find highbank cpufreq node\n"); > > + return -ENOENT; > > + } > > + > ... > > +out_put_node: > > + of_node_put(np); > > + return ret; > > +} > > +module_init(hb_cpufreq_driver_init); > > As we are moving to multiplatform build, this init function will run > on all other platforms built together with highbank. We should > probably eliminate that effect. That change can be made on top of the Mark's patches I think, right? If so, I'd prefer it that way. The Mark's patches have been in a limbo for too long already and that issue doesn't seem to be serious enough to block them any longer. Thanks, Rafael -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ^ permalink raw reply [flat|nested] 120+ messages in thread
* Re: [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq 2013-01-26 22:24 ` Rafael J. Wysocki @ 2013-01-28 8:37 ` Shawn Guo 0 siblings, 0 replies; 120+ messages in thread From: Shawn Guo @ 2013-01-28 8:37 UTC (permalink / raw) To: Rafael J. Wysocki Cc: linux-arm-kernel, cpufreq, linux-kernel, Mark Langsdorf, linux-pm Rafael, On Sat, Jan 26, 2013 at 11:24:12PM +0100, Rafael J. Wysocki wrote: > > As we are moving to multiplatform build, this init function will run > > on all other platforms built together with highbank. We should > > probably eliminate that effect. > > That change can be made on top of the Mark's patches I think, right? > Yes. > If so, I'd prefer it that way. The Mark's patches have been in a limbo for > too long already and that issue doesn't seem to be serious enough to block > them any longer. > Ok, I will take care of the changes needed on highbank. Do you have a branch with Mark's series and the EXPORT_SYMBOL() one applied, so that I can start my patch from there. Shawn ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> ` (11 preceding siblings ...) 2013-01-25 19:46 ` [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2013-01-28 16:13 ` Mark Langsdorf 2013-01-28 16:13 ` [PATCH 1/4 v12] arm: use device tree to get smp_twd clock Mark Langsdorf ` (3 more replies) 12 siblings, 4 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank) SoCs. The EnergyCore Management Engine (ECME) on the ECX-1000 manages the voltage for the part and communications with Linux through a pl320 mailbox. clk notifications are used to control when to send messages to the ECME. Previous versions of this patch set include two other patches. One has been dropped as unworkable and the other turned out to be unnecessary. --Mark Langsdorf Calxeda, Inc. ^ permalink raw reply [flat|nested] 120+ messages in thread
* [PATCH 1/4 v12] arm: use device tree to get smp_twd clock 2013-01-28 16:13 ` [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf @ 2013-01-28 16:13 ` Mark Langsdorf 2013-01-28 16:13 ` [PATCH 2/4 v12] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf ` (2 subsequent siblings) 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring From: Rob Herring <rob.herring@calxeda.com> Move clk setup to twd_local_timer_common_register and rely on twd_timer_rate being 0 to force calibration if there is no clock. Remove common_setup_called as it is no longer needed. Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> --- Changes from v11 Added Russell King's acked-by. Changes from v10 Reworked to simplify the logic as suggested by Russell King. Changes from v9 Updated to work with 3.8 kernel. Changes from v4, v5, v6, v7, v8 None. Changes from v3 No longer setting *clk to NULL in twd_get_clock(). Changes from v2 Turned the check for the node pointer into an if-then-else statement. Removed the second, redundant clk_get_rate. Changes from v1 None. arch/arm/kernel/smp_twd.c | 53 +++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 49f335d..ae0c7bb 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -31,7 +31,6 @@ static void __iomem *twd_base; static struct clk *twd_clk; static unsigned long twd_timer_rate; -static bool common_setup_called; static DEFINE_PER_CPU(bool, percpu_setup_called); static struct clock_event_device __percpu **twd_evt; @@ -239,25 +238,28 @@ static irqreturn_t twd_handler(int irq, void *dev_id) return IRQ_NONE; } -static struct clk *twd_get_clock(void) +static void twd_get_clock(struct device_node *np) { - struct clk *clk; int err; - clk = clk_get_sys("smp_twd", NULL); - if (IS_ERR(clk)) { - pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); - return clk; + if (np) + twd_clk = of_clk_get(np, 0); + else + twd_clk = clk_get_sys("smp_twd", NULL); + + if (IS_ERR(twd_clk)) { + pr_err("smp_twd: clock not found %d\n", (int) PTR_ERR(twd_clk)); + return; } - err = clk_prepare_enable(clk); + err = clk_prepare_enable(twd_clk); if (err) { pr_err("smp_twd: clock failed to prepare+enable: %d\n", err); - clk_put(clk); - return ERR_PTR(err); + clk_put(twd_clk); + return; } - return clk; + twd_timer_rate = clk_get_rate(twd_clk); } /* @@ -280,26 +282,7 @@ static int __cpuinit twd_timer_setup(struct clock_event_device *clk) } per_cpu(percpu_setup_called, cpu) = true; - /* - * This stuff only need to be done once for the entire TWD cluster - * during the runtime of the system. - */ - if (!common_setup_called) { - twd_clk = twd_get_clock(); - - /* - * We use IS_ERR_OR_NULL() here, because if the clock stubs - * are active we will get a valid clk reference which is - * however NULL and will return the rate 0. In that case we - * need to calibrate the rate instead. - */ - if (!IS_ERR_OR_NULL(twd_clk)) - twd_timer_rate = clk_get_rate(twd_clk); - else - twd_calibrate_rate(); - - common_setup_called = true; - } + twd_calibrate_rate(); /* * The following is done once per CPU the first time .setup() is @@ -330,7 +313,7 @@ static struct local_timer_ops twd_lt_ops __cpuinitdata = { .stop = twd_timer_stop, }; -static int __init twd_local_timer_common_register(void) +static int __init twd_local_timer_common_register(struct device_node *np) { int err; @@ -350,6 +333,8 @@ static int __init twd_local_timer_common_register(void) if (err) goto out_irq; + twd_get_clock(np); + return 0; out_irq: @@ -373,7 +358,7 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt) if (!twd_base) return -ENOMEM; - return twd_local_timer_common_register(); + return twd_local_timer_common_register(NULL); } #ifdef CONFIG_OF @@ -405,7 +390,7 @@ void __init twd_local_timer_of_register(void) goto out; } - err = twd_local_timer_common_register(); + err = twd_local_timer_common_register(np); out: WARN(err, "twd_local_timer_of_register failed (%d)\n", err); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 2/4 v12] clk, highbank: Prevent glitches in non-bypass reset mode 2013-01-28 16:13 ` [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-28 16:13 ` [PATCH 1/4 v12] arm: use device tree to get smp_twd clock Mark Langsdorf @ 2013-01-28 16:13 ` Mark Langsdorf 2013-01-28 16:13 ` [PATCH 3/4 v12] arm highbank: add support for pl320 IPC Mark Langsdorf 2013-01-28 16:13 ` [PATCH 4/4 v12] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring The highbank clock will glitch with the current code if the clock rate is reset without relocking the PLL. Program the PLL correctly to prevent glitches. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Mike Turquette <mturquette@linaro.org> --- Changes from v6, v7, v8, v9, v10, v11 None. Changes from v5 Added Mike Turquette's ack. Changes from v4 None. Changes from v3 Changelog text and patch name now correspond to the actual patch. was clk, highbank: remove non-bypass reset mode. Changes from v2 None. Changes from v1 Removed erroneous reformating. drivers/clk/clk-highbank.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index 52fecad..3a0b723 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -182,8 +182,10 @@ static int clk_pll_set_rate(struct clk_hw *hwclk, unsigned long rate, reg |= HB_PLL_EXT_ENA; reg &= ~HB_PLL_EXT_BYPASS; } else { + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); reg &= ~HB_PLL_DIVQ_MASK; reg |= divq << HB_PLL_DIVQ_SHIFT; + writel(reg | HB_PLL_EXT_BYPASS, hbclk->reg); } writel(reg, hbclk->reg); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 3/4 v12] arm highbank: add support for pl320 IPC 2013-01-28 16:13 ` [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-28 16:13 ` [PATCH 1/4 v12] arm: use device tree to get smp_twd clock Mark Langsdorf 2013-01-28 16:13 ` [PATCH 2/4 v12] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf @ 2013-01-28 16:13 ` Mark Langsdorf 2013-01-28 16:13 ` [PATCH 4/4 v12] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel Cc: Mark Langsdorf, Rob Herring, Omar Ramirez Luna, Arnd Bergmann From: Rob Herring <rob.herring@calxeda.com> The pl320 IPC allows for interprocessor communication between the highbank A9 and the EnergyCore Management Engine. The pl320 implements a straightforward mailbox protocol. Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Arnd Bergmann <arnd@arndb.de> --- Changes from v11 EXPORT_SYMBOL_GPL used instead of EXPORT_SYMBOL. Added driver/Kconfig and driver/Makefile changes. Changes from v10 Removed deependency on Omar Ramirez Luna's mailbox code. Now the patch creates the directory itself. Changes from v9 Used to be the 4th patch in the series. Changes from v6, v7, v8 None. Changes from v5 Renamed ipc_transmit() to pl320_ipc_transmit(). Properly exported pl320_ipc_{un}register_notifier(). Changes from v4 Moved pl320-ipc.c from arch/arm/mach-highbank to drivers/mailbox. Moved header information to include/linux/mailbox.h. Added Kconfig options to reflect the new code location. Change drivers/mailbox/Makefile to build the omap mailboxes only when they are configured. Removed ipc_call_fast and renamed ipc_call_slow ipc_transmit. Changes from v3, v2 None. Changes from v1 Removed erroneous changes for cpufreq Kconfig. arch/arm/mach-highbank/Kconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/mailbox/Kconfig | 18 ++++ drivers/mailbox/Makefile | 1 + drivers/mailbox/pl320-ipc.c | 199 +++++++++++++++++++++++++++++++++++++++++ include/linux/mailbox.h | 18 ++++ 7 files changed, 241 insertions(+) create mode 100644 drivers/mailbox/Kconfig create mode 100644 drivers/mailbox/Makefile create mode 100644 drivers/mailbox/pl320-ipc.c create mode 100644 include/linux/mailbox.h diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 551c97e..2388085 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -11,5 +11,7 @@ config ARCH_HIGHBANK select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU select HAVE_SMP + select MAILBOX + select PL320_MBOX select SPARSE_IRQ select USE_OF diff --git a/drivers/Kconfig b/drivers/Kconfig index f5fb072..2b4e89b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -134,6 +134,8 @@ source "drivers/hwspinlock/Kconfig" source "drivers/clocksource/Kconfig" +source "drivers/mailbox/Kconfig" + source "drivers/iommu/Kconfig" source "drivers/remoteproc/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 7863b9f..a8d32f1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -130,6 +130,7 @@ obj-y += platform/ #common clk code obj-y += clk/ +obj-$(CONFIG_MAILBOX) += mailbox/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig new file mode 100644 index 0000000..9489554 --- /dev/null +++ b/drivers/mailbox/Kconfig @@ -0,0 +1,18 @@ +menuconfig MAILBOX + bool "Mailbox Hardware Support" + help + Mailbox is a framework to control hardware communication between + on-chip processors through queued messages and interrupt driven + signals. Say Y if your platform supports hardware mailboxes. + +if MAILBOX +config PL320_MBOX + bool "ARM PL320 Mailbox" + help + An implementation of the ARM PL320 Interprocessor Communication + Mailbox (IPCM), tailored for the Calxeda Highbank. It is used to + send short messages between Highbank's A9 cores and the EnergyCore + Management Engine, primarily for cpufreq. Say Y here if you want + to use the PL320 IPCM support. + +endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile new file mode 100644 index 0000000..543ad6a --- /dev/null +++ b/drivers/mailbox/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c new file mode 100644 index 0000000..ba8293f --- /dev/null +++ b/drivers/mailbox/pl320-ipc.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/types.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/amba/bus.h> + +#include <linux/mailbox.h> + +#define IPCMxSOURCE(m) ((m) * 0x40) +#define IPCMxDSET(m) (((m) * 0x40) + 0x004) +#define IPCMxDCLEAR(m) (((m) * 0x40) + 0x008) +#define IPCMxDSTATUS(m) (((m) * 0x40) + 0x00C) +#define IPCMxMODE(m) (((m) * 0x40) + 0x010) +#define IPCMxMSET(m) (((m) * 0x40) + 0x014) +#define IPCMxMCLEAR(m) (((m) * 0x40) + 0x018) +#define IPCMxMSTATUS(m) (((m) * 0x40) + 0x01C) +#define IPCMxSEND(m) (((m) * 0x40) + 0x020) +#define IPCMxDR(m, dr) (((m) * 0x40) + ((dr) * 4) + 0x024) + +#define IPCMMIS(irq) (((irq) * 8) + 0x800) +#define IPCMRIS(irq) (((irq) * 8) + 0x804) + +#define MBOX_MASK(n) (1 << (n)) +#define IPC_TX_MBOX 1 +#define IPC_RX_MBOX 2 + +#define CHAN_MASK(n) (1 << (n)) +#define A9_SOURCE 1 +#define M3_SOURCE 0 + +static void __iomem *ipc_base; +static int ipc_irq; +static DEFINE_MUTEX(ipc_m1_lock); +static DECLARE_COMPLETION(ipc_completion); +static ATOMIC_NOTIFIER_HEAD(ipc_notifier); + +static inline void set_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox)); +} + +static inline void clear_destination(int source, int mbox) +{ + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox)); + __raw_writel(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox)); +} + +static void __ipc_send(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + __raw_writel(data[i], ipc_base + IPCMxDR(mbox, i)); + __raw_writel(0x1, ipc_base + IPCMxSEND(mbox)); +} + +static u32 __ipc_rcv(int mbox, u32 *data) +{ + int i; + for (i = 0; i < 7; i++) + data[i] = __raw_readl(ipc_base + IPCMxDR(mbox, i)); + return data[1]; +} + +/* blocking implmentation from the A9 side, not usuable in interrupts! */ +int pl320_ipc_transmit(u32 *data) +{ + int ret; + + mutex_lock(&ipc_m1_lock); + + init_completion(&ipc_completion); + __ipc_send(IPC_TX_MBOX, data); + ret = wait_for_completion_timeout(&ipc_completion, + msecs_to_jiffies(1000)); + if (ret == 0) { + ret = -ETIMEDOUT; + goto out; + } + + ret = __ipc_rcv(IPC_TX_MBOX, data); +out: + mutex_unlock(&ipc_m1_lock); + return ret; +} +EXPORT_SYMBOL_GPL(pl320_ipc_transmit); + +irqreturn_t ipc_handler(int irq, void *dev) +{ + u32 irq_stat; + u32 data[7]; + + irq_stat = __raw_readl(ipc_base + IPCMMIS(1)); + if (irq_stat & MBOX_MASK(IPC_TX_MBOX)) { + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + complete(&ipc_completion); + } + if (irq_stat & MBOX_MASK(IPC_RX_MBOX)) { + __ipc_rcv(IPC_RX_MBOX, data); + atomic_notifier_call_chain(&ipc_notifier, data[0], data + 1); + __raw_writel(2, ipc_base + IPCMxSEND(IPC_RX_MBOX)); + } + + return IRQ_HANDLED; +} + +int pl320_ipc_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ipc_notifier, nb); +} +EXPORT_SYMBOL_GPL(pl320_ipc_register_notifier); + +int pl320_ipc_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ipc_notifier, nb); +} +EXPORT_SYMBOL_GPL(pl320_ipc_unregister_notifier); + +static int __init pl320_probe(struct amba_device *adev, + const struct amba_id *id) +{ + int ret; + + ipc_base = ioremap(adev->res.start, resource_size(&adev->res)); + if (ipc_base == NULL) + return -ENOMEM; + + __raw_writel(0, ipc_base + IPCMxSEND(IPC_TX_MBOX)); + + ipc_irq = adev->irq[0]; + ret = request_irq(ipc_irq, ipc_handler, 0, dev_name(&adev->dev), NULL); + if (ret < 0) + goto err; + + /* Init slow mailbox */ + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxSOURCE(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxDSET(IPC_TX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_TX_MBOX)); + + /* Init receive mailbox */ + __raw_writel(CHAN_MASK(M3_SOURCE), + ipc_base + IPCMxSOURCE(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxDSET(IPC_RX_MBOX)); + __raw_writel(CHAN_MASK(M3_SOURCE) | CHAN_MASK(A9_SOURCE), + ipc_base + IPCMxMSET(IPC_RX_MBOX)); + + return 0; +err: + iounmap(ipc_base); + return ret; +} + +static struct amba_id pl320_ids[] = { + { + .id = 0x00041320, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl320_driver = { + .drv = { + .name = "pl320", + }, + .id_table = pl320_ids, + .probe = pl320_probe, +}; + +static int __init ipc_init(void) +{ + return amba_driver_register(&pl320_driver); +} +module_init(ipc_init); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h new file mode 100644 index 0000000..911cb28 --- /dev/null +++ b/include/linux/mailbox.h @@ -0,0 +1,18 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +int pl320_ipc_transmit(u32 *data); +int pl320_ipc_register_notifier(struct notifier_block *nb); +int pl320_ipc_unregister_notifier(struct notifier_block *nb); + -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
* [PATCH 4/4 v12] cpufreq, highbank: add support for highbank cpufreq 2013-01-28 16:13 ` [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf ` (2 preceding siblings ...) 2013-01-28 16:13 ` [PATCH 3/4 v12] arm highbank: add support for pl320 IPC Mark Langsdorf @ 2013-01-28 16:13 ` Mark Langsdorf 3 siblings, 0 replies; 120+ messages in thread From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw) To: linux-kernel, cpufreq, linux-pm, linux-arm-kernel; +Cc: Mark Langsdorf 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> --- Changes from v11 Added a compatible check against calxeda,highbank. Changes from v10 Now finds a cpu node by searching under /cpus and looking for something with an operating-points descriptor. This applies to both highbank-cpufreq and cpufreq-cpu0. Changes from v9 Added Mike Turquette's reviewed by. Used to be the 6th patch in the series. Changes from v8 Added Shawn Guo's reviewed by. Removed some magic numbers. Changed failure returns in clk_notify from NOTIFY_STOP to NOTIFY_BAD. Changes from v7 Removed old attribution to cpufreq-cpu0. Added some description in the documentation. Made cpu_dev, cpu_clk into local variables. Removed __devinit. Removed some unneeded includes. Added a brace to clarify some nested if logic. Changes from v6 Removed devicetree bindings documentation. Restructured driver to use clk notifications. Core driver logic is now cpufreq-clk0. Changes from v5 Changed ipc_transmit() to pl320_ipc_transmit(). Changes from v4 Removed erroneous changes to arch/arm/Kconfig. Removed unnecessary changes to drivers/cpufreq/Kconfig.arm Alphabetized additions to arch/arm/mach-highbank/Kconfig Changed ipc call and header to match new ipc location in drivers/mailbox. Changes from v3 None. Changes from v2 Changed transition latency binding in code to match documentation. Changes from v1 Added highbank specific Kconfig changes. arch/arm/boot/dts/highbank.dts | 10 ++++ arch/arm/mach-highbank/Kconfig | 2 + drivers/cpufreq/Kconfig.arm | 15 +++++ drivers/cpufreq/Makefile | 3 +- drivers/cpufreq/cpufreq-cpu0.c | 6 +- drivers/cpufreq/highbank-cpufreq.c | 115 +++++++++++++++++++++++++++++++++++++ 6 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 drivers/cpufreq/highbank-cpufreq.c diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index 5927a8d..6aad34a 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -37,6 +37,16 @@ next-level-cache = <&L2>; clocks = <&a9pll>; clock-names = "cpu"; + operating-points = < + /* kHz ignored */ + 1300000 1000000 + 1200000 1000000 + 1100000 1000000 + 800000 1000000 + 400000 1000000 + 200000 1000000 + >; + clock-latency = <100000>; }; cpu@901 { diff --git a/arch/arm/mach-highbank/Kconfig b/arch/arm/mach-highbank/Kconfig index 2388085..44b12f9 100644 --- a/arch/arm/mach-highbank/Kconfig +++ b/arch/arm/mach-highbank/Kconfig @@ -1,5 +1,7 @@ config ARCH_HIGHBANK bool "Calxeda ECX-1000/2000 (Highbank/Midway)" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_AMBA select ARM_GIC diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index a0b3661..ffe55b8 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -83,3 +83,18 @@ config ARM_SPEAR_CPUFREQ default y help This adds the CPUFreq driver support for SPEAr SOCs. + +config ARM_HIGHBANK_CPUFREQ + tristate "Calxeda Highbank-based" + depends on ARCH_HIGHBANK + select CPU_FREQ_TABLE + select GENERIC_CPUFREQ_CPU0 + select PM_OPP + select REGULATOR + + default m + help + This adds the CPUFreq driver for Calxeda Highbank SoC + based boards. + + If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index fadc4d4..31e6f19 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,8 +50,9 @@ obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o -obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o +obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 52bf36d..90e9d73 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -179,7 +179,11 @@ static int cpu0_cpufreq_driver_init(void) struct device_node *np; int ret; - np = of_find_node_by_path("/cpus/cpu@0"); + for_each_child_of_node(of_find_node_by_path("/cpus"), np) { + if (of_get_property(np, "operating-points", NULL)) + break; + } + if (!np) { pr_err("failed to find cpu0 node\n"); return -ENOENT; diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c new file mode 100644 index 0000000..2ea6276 --- /dev/null +++ b/drivers/cpufreq/highbank-cpufreq.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver provides the clk notifier callbacks that are used when + * the cpufreq-cpu0 driver changes to frequency to alert the highbank + * EnergyCore Management Engine (ECME) about the need to change + * voltage. The ECME interfaces with the actual voltage regulators. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/mailbox.h> + +#define HB_CPUFREQ_CHANGE_NOTE 0x80000001 +#define HB_CPUFREQ_IPC_LEN 7 +#define HB_CPUFREQ_VOLT_RETRIES 15 + +static int hb_voltage_change(unsigned int freq) +{ + int i; + u32 msg[HB_CPUFREQ_IPC_LEN]; + + msg[0] = HB_CPUFREQ_CHANGE_NOTE; + msg[1] = freq / 1000000; + for (i = 2; i < HB_CPUFREQ_IPC_LEN; i++) + msg[i] = 0; + + return pl320_ipc_transmit(msg); +} + +static int hb_cpufreq_clk_notify(struct notifier_block *nb, + unsigned long action, void *hclk) +{ + struct clk_notifier_data *clk_data = hclk; + int i = 0; + + if (action == PRE_RATE_CHANGE) { + if (clk_data->new_rate > clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } else if (action == POST_RATE_CHANGE) { + if (clk_data->new_rate < clk_data->old_rate) + while (hb_voltage_change(clk_data->new_rate)) + if (i++ > HB_CPUFREQ_VOLT_RETRIES) + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} + +static struct notifier_block hb_cpufreq_clk_nb = { + .notifier_call = hb_cpufreq_clk_notify, +}; + +static int hb_cpufreq_driver_init(void) +{ + struct device *cpu_dev; + struct clk *cpu_clk; + struct device_node *np; + int ret; + + if (!of_machine_is_compatible("calxeda,highbank")) + return -ENODEV; + + for_each_child_of_node(of_find_node_by_path("/cpus"), np) + if (of_get_property(np, "operating-points", NULL)) + break; + + if (!np) { + pr_err("failed to find highbank cpufreq node\n"); + return -ENOENT; + } + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get highbank cpufreq device\n"); + ret = -ENODEV; + goto out_put_node; + } + + cpu_dev->of_node = np; + + cpu_clk = clk_get(cpu_dev, NULL); + if (IS_ERR(cpu_clk)) { + ret = PTR_ERR(cpu_clk); + pr_err("failed to get cpu0 clock: %d\n", ret); + goto out_put_node; + } + + ret = clk_notifier_register(cpu_clk, &hb_cpufreq_clk_nb); + if (ret) { + pr_err("failed to register clk notifier: %d\n", ret); + goto out_put_node; + } + +out_put_node: + of_node_put(np); + return ret; +} +module_init(hb_cpufreq_driver_init); + +MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>"); +MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); +MODULE_LICENSE("GPL"); -- 1.7.11.7 ^ permalink raw reply related [flat|nested] 120+ messages in thread
end of thread, other threads:[~2013-01-28 20:48 UTC | newest] Thread overview: 120+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com> 2012-10-30 21:04 ` [PATCH 5/6] power: export opp cpufreq functions Mark Langsdorf 2012-10-31 1:17 ` Nishanth Menon 2012-10-31 0:21 ` [PATCH 0/6] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki [not found] ` <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com> 2012-11-02 18:51 ` [PATCH 5/6 v2] power: export opp cpufreq functions Mark Langsdorf 2012-11-06 20:18 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-11-06 20:18 ` [PATCH 1/6 v3] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-06 20:18 ` [PATCH 2/6 v3] clk, highbank: remove non-bypass reset mode Mark Langsdorf 2012-11-06 20:18 ` [PATCH 3/6 v3] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf 2012-11-06 20:18 ` [PATCH 4/6 v3] arm highbank: add support for pl320 IPC Mark Langsdorf 2012-11-06 20:18 ` [PATCH 5/6 v3] power: export opp cpufreq functions Mark Langsdorf 2012-11-06 20:18 ` [PATCH 6/6 v3] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-11-07 18:11 ` [PATCH 0/6 v3] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-11-07 18:32 ` [PATCH 0/6 v4] " Mark Langsdorf 2012-11-07 18:32 ` [PATCH 1/6 v4] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-07 18:32 ` [PATCH 2/6 v4] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2012-11-12 21:24 ` Mike Turquette 2012-11-12 21:35 ` Mark Langsdorf 2012-11-07 18:32 ` [PATCH 3/6 v4] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf 2012-11-11 16:38 ` Borislav Petkov 2012-11-12 16:35 ` Mark Langsdorf 2012-11-13 16:24 ` Borislav Petkov 2012-11-13 16:33 ` Mark Langsdorf 2012-11-13 19:13 ` Mark Langsdorf 2012-11-17 14:50 ` Borislav Petkov 2012-11-24 10:05 ` Rafael J. Wysocki 2012-11-26 13:57 ` Mark Langsdorf 2012-11-26 15:25 ` Rafael J. Wysocki 2012-11-07 18:32 ` [PATCH 4/6 v4] arm highbank: add support for pl320 IPC Mark Langsdorf 2012-11-14 14:03 ` Rob Herring 2012-11-07 18:32 ` [PATCH 5/6 v4] power: export opp cpufreq functions Mark Langsdorf [not found] ` <1352313166-28980-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 2012-11-07 18:32 ` [PATCH 6/6 v4] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf [not found] ` <1352313166-28980-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 2012-11-07 18:51 ` Rob Herring 2012-11-24 10:07 ` [PATCH 0/6 v4] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki 2012-11-27 15:04 ` [PATCH 0/6 v5] " Mark Langsdorf 2012-11-27 15:04 ` [PATCH 1/6 v5] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-27 15:04 ` [PATCH 2/6 v5] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2012-11-27 18:15 ` Mike Turquette 2012-11-27 15:04 ` [PATCH 3/6 v5] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf 2012-11-27 15:04 ` [PATCH 4/6 v5] arm highbank: add support for pl320 IPC Mark Langsdorf 2012-11-27 16:12 ` Thomas Petazzoni 2012-11-27 19:53 ` Mark Langsdorf 2012-11-27 15:04 ` [PATCH 5/6 v5] power: export opp cpufreq functions Mark Langsdorf [not found] ` <1354028674-23685-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 2012-11-27 15:04 ` [PATCH 6/6 v5] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-11-27 19:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki 2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf 2012-11-27 20:04 ` [PATCH 1/6 v6] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-11-27 20:04 ` [PATCH 2/6 v6] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2012-11-27 20:04 ` [PATCH 3/6 v6] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf 2012-11-27 20:04 ` [PATCH 4/6 v6] arm highbank: add support for pl320 IPC Mark Langsdorf 2012-11-27 20:04 ` [PATCH 5/6 v6] power: export opp cpufreq functions Mark Langsdorf [not found] ` <1354046672-7392-1-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 2012-11-27 20:04 ` [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf [not found] ` <1354046672-7392-7-git-send-email-mark.langsdorf-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org> 2012-11-28 2:32 ` Shawn Guo 2012-11-28 13:16 ` Mark Langsdorf 2012-11-28 14:58 ` Shawn Guo 2012-11-28 15:17 ` Shawn Guo 2012-11-28 15:01 ` Mark Langsdorf [not found] ` <20121128151741.GC28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> 2012-11-28 16:01 ` Mike Turquette 2012-11-28 16:18 ` Mark Langsdorf 2012-11-28 21:05 ` Mike Turquette 2012-11-29 0:24 ` Mark Langsdorf 2012-11-29 1:51 ` Shawn Guo [not found] ` <20121129015133.GD28170-+NayF8gZjK2ctlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> 2012-11-29 4:34 ` Mike Turquette 2012-12-04 14:33 ` [PATCH 0/6 v7] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-12-04 14:33 ` [PATCH 1/6 v7] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-12-04 14:33 ` [PATCH 2/6 v7] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2012-12-04 14:33 ` [PATCH 3/6 v7] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf 2012-12-04 14:34 ` [PATCH 4/6 v7] arm highbank: add support for pl320 IPC Mark Langsdorf 2012-12-04 14:34 ` [PATCH 5/6 v7] power: export opp cpufreq functions Mark Langsdorf 2012-12-04 14:34 ` [PATCH 6/6 v7] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-12-04 16:21 ` Shawn Guo 2012-12-05 16:48 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2012-12-05 16:48 ` [PATCH 1/6 v8] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-12-05 16:48 ` [PATCH 2/6 v8] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2012-12-05 18:02 ` Mike Turquette 2012-12-05 16:48 ` [PATCH 3/6 v8] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf 2012-12-05 16:48 ` [PATCH 4/6 v8] arm highbank: add support for pl320 IPC Mark Langsdorf 2012-12-05 16:48 ` [PATCH 5/6 v8] power: export opp cpufreq functions Mark Langsdorf 2012-12-05 16:48 ` [PATCH 6/6 v8] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-12-05 18:49 ` Mike Turquette 2012-12-05 22:09 ` Mark Langsdorf 2012-12-06 9:37 ` Shawn Guo 2012-12-27 13:12 ` [PATCH 0/6 v8] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki 2012-12-27 13:28 ` Mark Langsdorf 2012-12-27 14:43 ` Rafael J. Wysocki 2012-12-06 22:42 ` [PATCH 0/6 v9] " Mark Langsdorf 2012-12-06 22:42 ` [PATCH 1/6 v9] arm: use devicetree to get smp_twd clock Mark Langsdorf 2012-12-07 14:55 ` Thiago Farina 2012-12-27 5:11 ` Prashant Gaikwad 2012-12-06 22:42 ` [PATCH 2/6 v9] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2012-12-06 22:42 ` [PATCH 3/6 v9] cpufreq: tolerate inexact values when collecting stats Mark Langsdorf 2012-12-06 22:42 ` [PATCH 4/6 v9] arm highbank: add support for pl320 IPC Mark Langsdorf 2012-12-06 22:42 ` [PATCH 5/6 v9] power: export opp cpufreq functions Mark Langsdorf 2012-12-06 22:42 ` [PATCH 6/6 v9] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2012-12-07 7:04 ` Mike Turquette 2013-01-04 16:35 ` [PATCH 0/4 v10] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-04 16:35 ` [PATCH 1/4 v10] arm: use devicetree to get smp_twd clock Mark Langsdorf 2013-01-10 23:34 ` Russell King - ARM Linux 2013-01-11 14:40 ` Rob Herring 2013-01-04 16:35 ` [PATCH 2/4 v10] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2013-01-04 16:35 ` [PATCH 3/4 v10] arm highbank: add support for pl320 IPC Mark Langsdorf 2013-01-04 16:35 ` [PATCH 4/4 v10] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2013-01-25 19:46 ` [PATCH 0/4 v11] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-25 19:46 ` [PATCH 1/4 v11] arm: use device tree to get smp_twd clock Mark Langsdorf 2013-01-25 21:03 ` Rafael J. Wysocki 2013-01-25 21:40 ` Russell King - ARM Linux 2013-01-25 22:15 ` Rafael J. Wysocki 2013-01-25 19:46 ` [PATCH 2/4 v11] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2013-01-25 19:46 ` [PATCH 3/4 v11] arm highbank: add support for pl320 IPC Mark Langsdorf 2013-01-28 12:49 ` Rafael J. Wysocki 2013-01-28 13:44 ` Mark Langsdorf 2013-01-28 20:48 ` Rafael J. Wysocki 2013-01-25 19:46 ` [PATCH 4/4] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf 2013-01-26 14:39 ` Shawn Guo 2013-01-26 22:24 ` Rafael J. Wysocki 2013-01-28 8:37 ` Shawn Guo 2013-01-28 16:13 ` [PATCH 0/4 v12] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf 2013-01-28 16:13 ` [PATCH 1/4 v12] arm: use device tree to get smp_twd clock Mark Langsdorf 2013-01-28 16:13 ` [PATCH 2/4 v12] clk, highbank: Prevent glitches in non-bypass reset mode Mark Langsdorf 2013-01-28 16:13 ` [PATCH 3/4 v12] arm highbank: add support for pl320 IPC Mark Langsdorf 2013-01-28 16:13 ` [PATCH 4/4 v12] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).