* [PATCH 1/6] arm: use devicetree to get smp_twd clock
[not found] <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com>
@ 2012-10-30 21:04 ` Mark Langsdorf
[not found] ` <1351685025-26698-1-git-send-email-mark.langsdorf@calxeda.com>
` (9 subsequent siblings)
10 siblings, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-10-30 21:04 UTC (permalink / raw)
To: linux-arm-kernel
From: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Rob Herring <rob.herring@calxeda.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel at lists.infradead.org
---
arch/arm/kernel/smp_twd.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index b22d700..600fbcc 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);
+ if (!clk)
+ 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,10 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt)
if (!twd_base)
return -ENOMEM;
+ twd_clk = twd_get_clock(NULL);
+
+ twd_clk = twd_get_clock(NULL);
+
return twd_local_timer_common_register();
}
@@ -383,6 +386,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] 94+ messages in thread
* [PATCH 1/6] arm: use devicetree to get smp_twd clock
[not found] ` <1351685025-26698-1-git-send-email-mark.langsdorf@calxeda.com>
@ 2012-10-31 12:03 ` Mark Langsdorf
0 siblings, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-10-31 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Rob Herring <rob.herring@calxeda.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel at lists.infradead.org
---
arch/arm/kernel/smp_twd.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index b22d700..600fbcc 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);
+ if (!clk)
+ 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,10 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt)
if (!twd_base)
return -ENOMEM;
+ twd_clk = twd_get_clock(NULL);
+
+ twd_clk = twd_get_clock(NULL);
+
return twd_local_timer_common_register();
}
@@ -383,6 +386,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] 94+ messages in thread
* [PATCH 1/6 v2] arm: use devicetree to get smp_twd clock
[not found] ` <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com>
@ 2012-11-02 18:51 ` Mark Langsdorf
2012-11-04 10:08 ` Russell King - ARM Linux
0 siblings, 1 reply; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-02 18:51 UTC (permalink / raw)
To: linux-arm-kernel
From: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Rob Herring <rob.herring@calxeda.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: linux-arm-kernel at lists.infradead.org
Changes from v1
None.
---
arch/arm/kernel/smp_twd.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index b22d700..600fbcc 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);
+ if (!clk)
+ 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,10 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt)
if (!twd_base)
return -ENOMEM;
+ twd_clk = twd_get_clock(NULL);
+
+ twd_clk = twd_get_clock(NULL);
+
return twd_local_timer_common_register();
}
@@ -383,6 +386,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] 94+ messages in thread
* [PATCH 1/6 v2] arm: use devicetree to get smp_twd clock
2012-11-02 18:51 ` [PATCH 1/6 v2] " Mark Langsdorf
@ 2012-11-04 10:08 ` Russell King - ARM Linux
2012-11-05 22:28 ` Mark Langsdorf
0 siblings, 1 reply; 94+ messages in thread
From: Russell King - ARM Linux @ 2012-11-04 10:08 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Nov 02, 2012 at 01:51:44PM -0500, Mark Langsdorf wrote:
> -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);
> + if (!clk)
What does a NULL return from of_clk_get() mean? Where is this defined?
> @@ -349,6 +348,10 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt)
> if (!twd_base)
> return -ENOMEM;
>
> + twd_clk = twd_get_clock(NULL);
> +
> + twd_clk = twd_get_clock(NULL);
> +
Why twice?
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 1/6 v2] arm: use devicetree to get smp_twd clock
2012-11-04 10:08 ` Russell King - ARM Linux
@ 2012-11-05 22:28 ` Mark Langsdorf
2012-11-05 22:31 ` Russell King - ARM Linux
0 siblings, 1 reply; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-05 22:28 UTC (permalink / raw)
To: linux-arm-kernel
On 11/04/2012 04:08 AM, Russell King - ARM Linux wrote:
> On Fri, Nov 02, 2012 at 01:51:44PM -0500, Mark Langsdorf wrote:
>> -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);
>> + if (!clk)
>
> What does a NULL return from of_clk_get() mean? Where is this defined?
Well, it's a valid path if (np) is NULL. I'll add an IS_ERR(clk) and
resubmit.
>> @@ -349,6 +348,10 @@ int __init twd_local_timer_register(struct twd_local_timer *tlt)
>> if (!twd_base)
>> return -ENOMEM;
>>
>> + twd_clk = twd_get_clock(NULL);
>> +
>> + twd_clk = twd_get_clock(NULL);
>> +
>
> Why twice?
No good reason. I'll resubmit with it cleaned up. Thanks for the review.
--Mark Langsdorf
Calxeda, Inc.
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 1/6 v2] arm: use devicetree to get smp_twd clock
2012-11-05 22:28 ` Mark Langsdorf
@ 2012-11-05 22:31 ` Russell King - ARM Linux
2012-11-05 22:49 ` Mark Langsdorf
0 siblings, 1 reply; 94+ messages in thread
From: Russell King - ARM Linux @ 2012-11-05 22:31 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Nov 05, 2012 at 04:28:05PM -0600, Mark Langsdorf wrote:
> On 11/04/2012 04:08 AM, Russell King - ARM Linux wrote:
> > On Fri, Nov 02, 2012 at 01:51:44PM -0500, Mark Langsdorf wrote:
> >> -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);
> >> + if (!clk)
> >
> > What does a NULL return from of_clk_get() mean? Where is this defined?
>
> Well, it's a valid path if (np) is NULL. I'll add an IS_ERR(clk) and
> resubmit.
Hang on - what logic are you trying to achieve here? Wouldn't:
if (np)
clk = of_clk_get(np, 0);
else
clk = clk_get_sys("smp_twd", NULL);
be sufficient? If we have DT, why would we ever want to fall back to
"smp_twd" ?
^ permalink raw reply [flat|nested] 94+ messages in thread
* [PATCH 1/6 v2] arm: use devicetree to get smp_twd clock
2012-11-05 22:31 ` Russell King - ARM Linux
@ 2012-11-05 22:49 ` Mark Langsdorf
0 siblings, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-05 22:49 UTC (permalink / raw)
To: linux-arm-kernel
On 11/05/2012 04:31 PM, Russell King - ARM Linux wrote:
> On Mon, Nov 05, 2012 at 04:28:05PM -0600, Mark Langsdorf wrote:
>> On 11/04/2012 04:08 AM, Russell King - ARM Linux wrote:
>>> On Fri, Nov 02, 2012 at 01:51:44PM -0500, Mark Langsdorf wrote:
>>>> -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);
>>>> + if (!clk)
>>>
>>> What does a NULL return from of_clk_get() mean? Where is this defined?
>>
>> Well, it's a valid path if (np) is NULL. I'll add an IS_ERR(clk) and
>> resubmit.
>
> Hang on - what logic are you trying to achieve here? Wouldn't:
>
> if (np)
> clk = of_clk_get(np, 0);
> else
> clk = clk_get_sys("smp_twd", NULL);
>
> be sufficient? If we have DT, why would we ever want to fall back to
> "smp_twd" ?
I'm just trying to make sure I have a clock so I can do cpufreq operations.
Your solution works and is sufficient. Thanks.
--Mark Langsdorf
Calxeda, Inc.
^ permalink raw reply [flat|nested] 94+ 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>
` (2 preceding siblings ...)
[not found] ` <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com>
@ 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)
10 siblings, 7 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw)
To: 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] 94+ messages in thread
* [PATCH 1/6 v5] arm: use devicetree to get smp_twd clock
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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] cpufreq: add support for Calxeda ECX-1000 (highbank) 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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] 94+ messages in thread
* [PATCH 3/6 v5] cpufreq: tolerate inexact values when collecting stats
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [PATCH 4/6 v5] arm highbank: add support for pl320 IPC
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [PATCH 5/6 v5] power: export opp cpufreq functions
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) 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
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
6 siblings, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [PATCH 6/6 v5] cpufreq, highbank: add support for highbank cpufreq
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf
` (4 preceding siblings ...)
2012-11-27 15:04 ` [PATCH 5/6 v5] power: export opp cpufreq functions Mark Langsdorf
@ 2012-11-27 15:04 ` Mark Langsdorf
2012-11-27 19:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) Rafael J. Wysocki
6 siblings, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 15:04 UTC (permalink / raw)
To: linux-arm-kernel
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: devicetree-discuss at lists.ozlabs.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 at 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 at 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 at 1 {
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ next-level-cache = <&L2>;
+ };
+
+ cpu at 2 {
+ compatible = "arm,cortex-a9";
+ reg = <2>;
+ next-level-cache = <&L2>;
+ };
+
+ cpu at 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 at 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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Thomas Petazzoni @ 2012-11-27 16:12 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mike Turquette @ 2012-11-27 18:15 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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] 94+ messages in thread
* [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank)
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) Mark Langsdorf
` (5 preceding siblings ...)
2012-11-27 15:04 ` [PATCH 6/6 v5] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf
@ 2012-11-27 19:04 ` Rafael J. Wysocki
6 siblings, 0 replies; 94+ messages in thread
From: Rafael J. Wysocki @ 2012-11-27 19:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 19:53 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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>
` (3 preceding siblings ...)
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) 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)
10 siblings, 6 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw)
To: 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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
2012-11-27 20:04 ` [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf
5 siblings, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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
2012-11-27 20:04 ` [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf
5 siblings, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq
2012-11-27 20:04 ` [PATCH 0/6 v6] " Mark Langsdorf
` (4 preceding siblings ...)
2012-11-27 20:04 ` [PATCH 5/6 v6] power: export opp cpufreq functions Mark Langsdorf
@ 2012-11-27 20:04 ` Mark Langsdorf
2012-11-28 2:32 ` Shawn Guo
5 siblings, 1 reply; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-27 20:04 UTC (permalink / raw)
To: linux-arm-kernel
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: devicetree-discuss at lists.ozlabs.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 at 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 at 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 at 1 {
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ next-level-cache = <&L2>;
+ };
+
+ cpu at 2 {
+ compatible = "arm,cortex-a9";
+ reg = <2>;
+ next-level-cache = <&L2>;
+ };
+
+ cpu at 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 at 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 at 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] 94+ messages in thread
* [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq
2012-11-27 20:04 ` [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf
@ 2012-11-28 2:32 ` Shawn Guo
2012-11-28 13:16 ` Mark Langsdorf
0 siblings, 1 reply; 94+ messages in thread
From: Shawn Guo @ 2012-11-28 2:32 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-28 13:16 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Shawn Guo @ 2012-11-28 14:58 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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
2012-11-28 16:01 ` Mike Turquette
1 sibling, 0 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-28 15:01 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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
2012-11-28 16:01 ` Mike Turquette
0 siblings, 2 replies; 94+ messages in thread
From: Shawn Guo @ 2012-11-28 15:17 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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
@ 2012-11-28 16:01 ` Mike Turquette
2012-11-28 16:18 ` Mark Langsdorf
2012-11-29 1:51 ` Shawn Guo
1 sibling, 2 replies; 94+ messages in thread
From: Mike Turquette @ 2012-11-28 16:01 UTC (permalink / raw)
To: linux-arm-kernel
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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 94+ messages in thread
* [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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-28 16:18 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mike Turquette @ 2012-11-28 21:05 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mark Langsdorf @ 2012-11-29 0:24 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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
2012-11-29 4:34 ` Mike Turquette
1 sibling, 1 reply; 94+ messages in thread
From: Shawn Guo @ 2012-11-29 1:51 UTC (permalink / raw)
To: linux-arm-kernel
> 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] 94+ messages in thread
* [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq
2012-11-29 1:51 ` Shawn Guo
@ 2012-11-29 4:34 ` Mike Turquette
0 siblings, 0 replies; 94+ messages in thread
From: Mike Turquette @ 2012-11-29 4:34 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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>
` (4 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)
10 siblings, 6 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw)
To: 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-04 14:33 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-04 14:34 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-04 14:34 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-04 14:34 UTC (permalink / raw)
To: linux-arm-kernel
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 at linaro.org
Cc: mturquette at 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 at 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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Shawn Guo @ 2012-12-04 16:21 UTC (permalink / raw)
To: linux-arm-kernel
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 at linaro.org
> Cc: mturquette at 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 at 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 at 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] 94+ 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>
` (5 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)
10 siblings, 7 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw)
To: 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 16:48 UTC (permalink / raw)
To: linux-arm-kernel
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 at linaro.org
Cc: mturquette at 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 at 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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mike Turquette @ 2012-12-05 18:02 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mike Turquette @ 2012-12-05 18:49 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-05 22:09 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Shawn Guo @ 2012-12-06 9:37 UTC (permalink / raw)
To: linux-arm-kernel
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 at linaro.org
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
^ permalink raw reply [flat|nested] 94+ 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>
` (6 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)
10 siblings, 6 replies; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw)
To: 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-06 22:42 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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 at 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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mike Turquette @ 2012-12-07 7:04 UTC (permalink / raw)
To: 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 at linaro.org
Looks good to me.
Reviewed-by: Mike Turquette <mturquette@linaro.org>
Regards,
Mike
^ permalink raw reply [flat|nested] 94+ messages in thread
* [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; 94+ messages in thread
From: Thiago Farina @ 2012-12-07 14:55 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Prashant Gaikwad @ 2012-12-27 5:11 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rafael J. Wysocki @ 2012-12-27 13:12 UTC (permalink / raw)
To: 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mark Langsdorf @ 2012-12-27 13:28 UTC (permalink / raw)
To: linux-arm-kernel
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 at sisk.pl]
Sent: Thursday, December 27, 2012 7:12 AM
To: Mark Langsdorf
Cc: linux-kernel at vger.kernel.org; cpufreq at vger.kernel.org; linux-pm at vger.kernel.org; linux-arm-kernel at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rafael J. Wysocki @ 2012-12-27 14:43 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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>
` (7 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
10 siblings, 4 replies; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw)
To: 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-04 16:35 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Russell King - ARM Linux @ 2013-01-10 23:34 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rob Herring @ 2013-01-11 14:40 UTC (permalink / raw)
To: 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] 94+ 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>
` (8 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
10 siblings, 4 replies; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw)
To: 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-25 19:46 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rafael J. Wysocki @ 2013-01-25 21:03 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Russell King - ARM Linux @ 2013-01-25 21:40 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rafael J. Wysocki @ 2013-01-25 22:15 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Shawn Guo @ 2013-01-26 14:39 UTC (permalink / raw)
To: 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rafael J. Wysocki @ 2013-01-26 22:24 UTC (permalink / raw)
To: 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Shawn Guo @ 2013-01-28 8:37 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rafael J. Wysocki @ 2013-01-28 12:49 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
* [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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-28 13:44 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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>
` (9 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)
10 siblings, 4 replies; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw)
To: 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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ 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; 94+ messages in thread
From: Mark Langsdorf @ 2013-01-28 16:13 UTC (permalink / raw)
To: linux-arm-kernel
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 at 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 at 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] 94+ messages in thread
* [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; 94+ messages in thread
From: Rafael J. Wysocki @ 2013-01-28 20:48 UTC (permalink / raw)
To: linux-arm-kernel
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] 94+ messages in thread
end of thread, other threads:[~2013-01-28 20:48 UTC | newest]
Thread overview: 94+ 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 1/6] arm: use devicetree to get smp_twd clock Mark Langsdorf
[not found] ` <1351685025-26698-1-git-send-email-mark.langsdorf@calxeda.com>
2012-10-31 12:03 ` Mark Langsdorf
[not found] ` <1351882309-733-1-git-send-email-mark.langsdorf@calxeda.com>
2012-11-02 18:51 ` [PATCH 1/6 v2] " Mark Langsdorf
2012-11-04 10:08 ` Russell King - ARM Linux
2012-11-05 22:28 ` Mark Langsdorf
2012-11-05 22:31 ` Russell King - ARM Linux
2012-11-05 22:49 ` Mark Langsdorf
2012-11-27 15:04 ` [PATCH 0/6 v5] cpufreq: add support for Calxeda ECX-1000 (highbank) 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
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
2012-11-27 20:04 ` [PATCH 6/6 v6] cpufreq, highbank: add support for highbank cpufreq Mark Langsdorf
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
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
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).