* [PATCH 3/4] pinctrl: samsung: Add support for Exynos4x12
From: Tomasz Figa @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAH9JG2V2UjuvjK2_sjLQsEt21duCqyoQuAi0KsMfB+CjRrh8KQ@mail.gmail.com>
Hi Linus, Kyungmin,
On Monday 29 of October 2012 09:30:26 Kyungmin Park wrote:
> On 10/29/12, Linus Walleij <linus.walleij@linaro.org> wrote:
> > On Wed, Oct 24, 2012 at 4:37 PM, Tomasz Figa <t.figa@samsung.com>
wrote:
> >> This patch extends the driver with any necessary SoC-specific
> >> definitions to support Exynos4x12 SoCs.
> >>
> >> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> >> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> >
> > Acked-by: Linus Walleij <linus.walleij@linaro.org>
> >
> > I guess you need all of this to go into my Samsung branch?
> > I need and ACK from the Samsung maintainer and preferably
> > Thomas A as well.
>
> Hi,
>
> Now we're trying to send the standalone patches to avoid the conflict.
> and hope to merge patches via proper subsystem. In this case, pinctl.
Since this depends on the patch adding Exynos4x12 dts files
([PATCH] ARM: dts: exynos4: Add support for Exynos4x12 SoCs),
which will be going through Kgene's tree and this patch series contains
mostly SoC-specific code, maybe this should rather go through Kgene's
tree? Or this is not a problem?
Best regards,
--
Tomasz Figa
Samsung Poland R&D Center
SW Solution Development, Linux Platform
^ permalink raw reply
* [PATCH V3 7/7] ARM: tegra30: cpuidle: add powered-down state for CPU0
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351506501-600-1-git-send-email-josephl@nvidia.com>
This is a power gating idle mode. It support power gating vdd_cpu rail
after all cpu cores in "powered-down" status. For Tegra30, the CPU0 can
enter this state only when all secondary CPU is offline. We need to take
care and make sure whole secondary CPUs were offline and checking the
CPU power gate status. After that, the CPU0 can go into "powered-down"
state safely. Then shut off the CPU rail.
Be aware of that, you may see the legacy power state "LP2" in the code
which is exactly the same meaning of "CPU power down".
Base on the work by:
Scott Williams <scwilliams@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V3:
* Fix the potential issue that will cause the CPU corruption when CPUs
doing LP2. All CPUs entering LP2 is not working. The CPU0 can enter
LP2 only when all secondary CPU is offline. This can be covered by
CPUquiet and cluster switching mechanism.
* update the commit message
* rename the funciton name from "tegra30_idle_enter_lp2_cpu_0" to
"tegra30_cpu_cluster_power_down"
* remove the redundant "flush_cache_all" in "tegra_idle_lp2_last"
because the "cpu_suspend" already do that after save the CPU contexts
V2:
* refine the pclk usage in "set_power_timers"
---
arch/arm/mach-tegra/cpuidle-tegra30.c | 44 +++++++++-
arch/arm/mach-tegra/pm.c | 144 +++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/pm.h | 3 +
arch/arm/mach-tegra/sleep-tegra30.S | 44 ++++++++++
arch/arm/mach-tegra/sleep.S | 42 ++++++++++
arch/arm/mach-tegra/sleep.h | 2 +
6 files changed, 275 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
index cc48d7f..5e8cbf5 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra30.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -32,6 +32,7 @@
#include "pm.h"
#include "sleep.h"
+#include "tegra_cpu_car.h"
#ifdef CONFIG_PM_SLEEP
static int tegra30_idle_lp2(struct cpuidle_device *dev,
@@ -67,6 +68,31 @@ static struct cpuidle_driver tegra_idle_driver = {
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
#ifdef CONFIG_PM_SLEEP
+static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ struct cpuidle_state *state = &drv->states[index];
+ u32 cpu_on_time = state->exit_latency;
+ u32 cpu_off_time = state->target_residency - state->exit_latency;
+
+ /* All CPUs entering LP2 is not working.
+ * Don't let CPU0 enter LP2 when any secondary CPU is online.
+ */
+ if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) {
+ cpu_do_idle();
+ return false;
+ }
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
+
+ tegra_idle_lp2_last(cpu_on_time, cpu_off_time);
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
+
+ return true;
+}
+
#ifdef CONFIG_SMP
static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
@@ -101,16 +127,22 @@ static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev,
{
u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
bool entered_lp2 = false;
+ bool last_cpu;
local_fiq_disable();
- tegra_set_cpu_in_lp2(cpu);
+ last_cpu = tegra_set_cpu_in_lp2(cpu);
cpu_pm_enter();
- if (cpu == 0)
- cpu_do_idle();
- else
+ if (cpu == 0) {
+ if (last_cpu)
+ entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv,
+ index);
+ else
+ cpu_do_idle();
+ } else {
entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
+ }
cpu_pm_exit();
tegra_clear_cpu_in_lp2(cpu);
@@ -130,6 +162,10 @@ int __init tegra30_cpuidle_init(void)
struct cpuidle_device *dev;
struct cpuidle_driver *drv = &tegra_idle_driver;
+#ifdef CONFIG_PM_SLEEP
+ tegra_tear_down_cpu = tegra30_tear_down_cpu;
+#endif
+
ret = cpuidle_register_driver(&tegra_idle_driver);
if (ret) {
pr_err("CPUidle driver registration failed\n");
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 39ee557..0d9e8b9 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -20,14 +20,37 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/cpu_pm.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/smp_plat.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include <asm/idmap.h>
+#include <asm/proc-fns.h>
+#include <asm/tlbflush.h>
#include <mach/iomap.h>
#include "reset.h"
+#include "flowctrl.h"
+#include "sleep.h"
+#include "tegra_cpu_car.h"
+
+#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
+
+#define PMC_CTRL 0x0
+#define PMC_CPUPWRGOOD_TIMER 0xc8
+#define PMC_CPUPWROFF_TIMER 0xcc
#ifdef CONFIG_PM_SLEEP
static unsigned int g_diag_reg;
static DEFINE_SPINLOCK(tegra_lp2_lock);
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static struct clk *tegra_pclk;
+void (*tegra_tear_down_cpu)(void);
void save_cpu_arch_register(void)
{
@@ -43,6 +66,89 @@ void restore_cpu_arch_register(void)
return;
}
+static void set_power_timers(unsigned long us_on, unsigned long us_off)
+{
+ unsigned long long ticks;
+ unsigned long long pclk;
+ unsigned long rate;
+ static unsigned long tegra_last_pclk;
+
+ if (tegra_pclk == NULL) {
+ tegra_pclk = clk_get_sys(NULL, "pclk");
+ WARN_ON(IS_ERR(tegra_pclk));
+ }
+
+ rate = clk_get_rate(tegra_pclk);
+
+ if (WARN_ON_ONCE(rate <= 0))
+ pclk = 100000000;
+ else
+ pclk = rate;
+
+ if ((rate != tegra_last_pclk)) {
+ ticks = (us_on * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
+
+ ticks = (us_off * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
+ wmb();
+ }
+ tegra_last_pclk = pclk;
+}
+
+/*
+ * restore_cpu_complex
+ *
+ * restores cpu clock setting, clears flow controller
+ *
+ * Always called on CPU 0.
+ */
+static void restore_cpu_complex(void)
+{
+ int cpu = smp_processor_id();
+
+ BUG_ON(cpu != 0);
+
+#ifdef CONFIG_SMP
+ cpu = cpu_logical_map(cpu);
+#endif
+
+ /* Restore the CPU clock settings */
+ tegra_cpu_clock_resume();
+
+ flowctrl_cpu_suspend_exit(cpu);
+
+ restore_cpu_arch_register();
+}
+
+/*
+ * suspend_cpu_complex
+ *
+ * saves pll state for use by restart_plls, prepares flow controller for
+ * transition to suspend state
+ *
+ * Must always be called on cpu 0.
+ */
+static void suspend_cpu_complex(void)
+{
+ int cpu = smp_processor_id();
+
+ BUG_ON(cpu != 0);
+
+#ifdef CONFIG_SMP
+ cpu = cpu_logical_map(cpu);
+#endif
+
+ /* Save the CPU clock settings */
+ tegra_cpu_clock_suspend();
+
+ flowctrl_cpu_suspend_enter(cpu);
+
+ save_cpu_arch_register();
+}
+
void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
{
u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
@@ -72,4 +178,42 @@ bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id)
spin_unlock(&tegra_lp2_lock);
return last_cpu;
}
+
+static int tegra_sleep_cpu(unsigned long v2p)
+{
+ /* Switch to the identity mapping. */
+ cpu_switch_mm(idmap_pgd, &init_mm);
+
+ /* Flush the TLB. */
+ local_flush_tlb_all();
+
+ tegra_sleep_cpu_finish(v2p);
+
+ /* should never here */
+ BUG();
+
+ return 0;
+}
+
+void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time)
+{
+ u32 mode;
+
+ /* Only the last cpu down does the final suspend steps */
+ mode = readl(pmc + PMC_CTRL);
+ mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+ writel(mode, pmc + PMC_CTRL);
+
+ set_power_timers(cpu_on_time, cpu_off_time);
+
+ cpu_cluster_pm_enter();
+ suspend_cpu_complex();
+ outer_disable();
+
+ cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
+
+ outer_resume();
+ restore_cpu_complex();
+ cpu_cluster_pm_exit();
+}
#endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index bcfc45f..512345c 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -27,4 +27,7 @@ void restore_cpu_arch_register(void);
void tegra_clear_cpu_in_lp2(int phy_cpu_id);
bool tegra_set_cpu_in_lp2(int phy_cpu_id);
+void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time);
+extern void (*tegra_tear_down_cpu)(void);
+
#endif /* _MACH_TEGRA_PM_H_ */
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index 59984d7..562a8e7 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -124,4 +124,48 @@ ENTRY(tegra30_sleep_cpu_secondary_finish)
mov r0, #1 @ never return here
mov pc, r7
ENDPROC(tegra30_sleep_cpu_secondary_finish)
+
+/*
+ * tegra30_tear_down_cpu
+ *
+ * Switches the CPU to enter sleep.
+ */
+ENTRY(tegra30_tear_down_cpu)
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+
+ b tegra30_enter_sleep
+ENDPROC(tegra30_tear_down_cpu)
+
+/*
+ * tegra30_enter_sleep
+ *
+ * uses flow controller to enter sleep state
+ * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
+ * executes from SDRAM with target state is LP2
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ */
+tegra30_enter_sleep:
+ cpu_id r1
+
+ cpu_to_csr_reg r2, r1
+ ldr r0, [r6, r2]
+ orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ orr r0, r0, #FLOW_CTRL_CSR_ENABLE
+ str r0, [r6, r2]
+
+ mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
+ orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
+ cpu_to_halt_reg r2, r1
+ str r0, [r6, r2]
+ dsb
+ ldr r0, [r6, r2] /* memory barrier */
+
+halted:
+ isb
+ dsb
+ wfi /* CPU should be power gated here */
+
+ /* !!!FIXME!!! Implement halt failure handler */
+ b halted
+
#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index cba7a52..b7541e2 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -25,6 +25,7 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/cache.h>
#include <asm/cp15.h>
#include <mach/iomap.h>
@@ -59,4 +60,45 @@ ENTRY(tegra_disable_clean_inv_dcache)
ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
ENDPROC(tegra_disable_clean_inv_dcache)
+/*
+ * tegra_sleep_cpu_finish(unsigned long v2p)
+ *
+ * enters suspend in LP2 by turning off the mmu and jumping to
+ * tegra?_tear_down_cpu
+ */
+ENTRY(tegra_sleep_cpu_finish)
+ /* Flush and disable the L1 data cache */
+ bl tegra_disable_clean_inv_dcache
+
+ mov32 r6, tegra_tear_down_cpu
+ ldr r1, [r6]
+ add r1, r1, r0
+
+ mov32 r3, tegra_shut_off_mmu
+ add r3, r3, r0
+ mov r0, r1
+
+ mov pc, r3
+ENDPROC(tegra_sleep_cpu_finish)
+
+/*
+ * tegra_shut_off_mmu
+ *
+ * r0 = physical address to jump to with mmu off
+ *
+ * called with VA=PA mapping
+ * turns off MMU, icache, dcache and branch prediction
+ */
+ .align L1_CACHE_SHIFT
+ .pushsection .idmap.text, "ax"
+ENTRY(tegra_shut_off_mmu)
+ mrc p15, 0, r3, c1, c0, 0
+ movw r2, #CR_I | CR_Z | CR_C | CR_M
+ bic r3, r3, r2
+ dsb
+ mcr p15, 0, r3, c1, c0, 0
+ isb
+ mov pc, r0
+ENDPROC(tegra_shut_off_mmu)
+ .popsection
#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index 220fbd1..001920f 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -73,6 +73,7 @@
.endm
#else
void tegra_resume(void);
+int tegra_sleep_cpu_finish(unsigned long);
#ifdef CONFIG_HOTPLUG_CPU
void tegra20_hotplug_init(void);
@@ -83,6 +84,7 @@ static inline void tegra30_hotplug_init(void) {}
#endif
int tegra30_sleep_cpu_secondary_finish(unsigned long);
+void tegra30_tear_down_cpu(void);
#endif
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V3 6/7] ARM: tegra30: flowctrl: add cpu_suspend_exter/exit function
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351506501-600-1-git-send-email-josephl@nvidia.com>
The flow controller can help CPU to go into suspend mode (powered-down
state). When CPU go into powered-down state, it needs some careful
settings before getting into and after leaving. The enter and exit
functions do that by configuring appropriate mode for flow controller.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V3:
* update the commit message
V2:
* no change
---
arch/arm/mach-tegra/flowctrl.c | 47 ++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/flowctrl.h | 8 ++++++
2 files changed, 55 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/flowctrl.c b/arch/arm/mach-tegra/flowctrl.c
index f07488e..5967b08 100644
--- a/arch/arm/mach-tegra/flowctrl.c
+++ b/arch/arm/mach-tegra/flowctrl.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/cpumask.h>
#include <mach/iomap.h>
@@ -51,6 +52,14 @@ static void flowctrl_update(u8 offset, u32 value)
readl_relaxed(addr);
}
+u32 flowctrl_read_cpu_csr(unsigned int cpuid)
+{
+ u8 offset = flowctrl_offset_cpu_csr[cpuid];
+ void __iomem *addr = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + offset;
+
+ return readl(addr);
+}
+
void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value)
{
return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value);
@@ -60,3 +69,41 @@ void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value)
{
return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value);
}
+
+void flowctrl_cpu_suspend_enter(unsigned int cpuid)
+{
+ unsigned int reg;
+ int i;
+
+ reg = flowctrl_read_cpu_csr(cpuid);
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
+ reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid; /* pwr gating on wfi */
+ reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */
+ flowctrl_write_cpu_csr(cpuid, reg);
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ if (i == cpuid)
+ continue;
+ reg = flowctrl_read_cpu_csr(i);
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG;
+ reg |= FLOW_CTRL_CSR_INTR_FLAG;
+ flowctrl_write_cpu_csr(i, reg);
+ }
+}
+
+void flowctrl_cpu_suspend_exit(unsigned int cpuid)
+{
+ unsigned int reg;
+
+ /* Disable powergating via flow controller for CPU0 */
+ reg = flowctrl_read_cpu_csr(cpuid);
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
+ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
+ flowctrl_write_cpu_csr(cpuid, reg);
+}
diff --git a/arch/arm/mach-tegra/flowctrl.h b/arch/arm/mach-tegra/flowctrl.h
index 1942817..0798dec 100644
--- a/arch/arm/mach-tegra/flowctrl.h
+++ b/arch/arm/mach-tegra/flowctrl.h
@@ -34,9 +34,17 @@
#define FLOW_CTRL_HALT_CPU1_EVENTS 0x14
#define FLOW_CTRL_CPU1_CSR 0x18
+#define TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 (1 << 8)
+#define TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4)
+#define TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8)
+
#ifndef __ASSEMBLY__
+u32 flowctrl_read_cpu_csr(unsigned int cpuid);
void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value);
void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value);
+
+void flowctrl_cpu_suspend_enter(unsigned int cpuid);
+void flowctrl_cpu_suspend_exit(unsigned int cpuid);
#endif
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V3 5/7] ARM: tegra30: clocks: add CPU low-power function into tegra_cpu_car_ops
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351506501-600-1-git-send-email-josephl@nvidia.com>
Add suspend, resume and rail_off_ready API into tegra_cpu_car_ops. These
functions were used for CPU powered-down state maintenance. One thing
needs to notice the rail_off_ready API only availalbe for cpu_g cluster
not cpu_lp cluster.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V3:
* update the commit message
V2:
* no change
---
arch/arm/mach-tegra/tegra30_clocks.c | 107 ++++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/tegra_cpu_car.h | 37 ++++++++++++
2 files changed, 144 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c
index 665618a..19ff1c6 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.c
+++ b/arch/arm/mach-tegra/tegra30_clocks.c
@@ -32,6 +32,7 @@
#include <asm/clkdev.h>
#include <mach/iomap.h>
+#include <mach/powergate.h>
#include "clock.h"
#include "fuse.h"
@@ -310,6 +311,31 @@
#define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
#define CPU_RESET(cpu) (0x1111ul << (cpu))
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_PLLX_BASE 0xe0
+#define CLK_RESET_PLLX_MISC 0xe4
+
+#define CLK_RESET_SOURCE_CSITE 0x1d4
+
+#define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28
+#define CLK_RESET_CCLK_RUN_POLICY_SHIFT 4
+#define CLK_RESET_CCLK_IDLE_POLICY_SHIFT 0
+#define CLK_RESET_CCLK_IDLE_POLICY 1
+#define CLK_RESET_CCLK_RUN_POLICY 2
+#define CLK_RESET_CCLK_BURST_POLICY_PLLX 8
+
+#ifdef CONFIG_PM_SLEEP
+static struct cpu_clk_suspend_context {
+ u32 pllx_misc;
+ u32 pllx_base;
+
+ u32 cpu_burst;
+ u32 clk_csite_src;
+ u32 cclk_divider;
+} tegra30_cpu_clk_sctx;
+#endif
+
/**
* Structure defining the fields for USB UTMI clocks Parameters.
*/
@@ -2387,12 +2413,93 @@ static void tegra30_disable_cpu_clock(u32 cpu)
reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
}
+#ifdef CONFIG_PM_SLEEP
+static bool tegra30_cpu_rail_off_ready(void)
+{
+ unsigned int cpu_rst_status;
+ int cpu_pwr_status;
+
+ cpu_rst_status = readl(reg_clk_base +
+ TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
+ cpu_pwr_status = tegra_powergate_is_powered(TEGRA_POWERGATE_CPU1) ||
+ tegra_powergate_is_powered(TEGRA_POWERGATE_CPU2) ||
+ tegra_powergate_is_powered(TEGRA_POWERGATE_CPU3);
+
+ if (((cpu_rst_status & 0xE) != 0xE) || cpu_pwr_status)
+ return false;
+
+ return true;
+}
+
+static void tegra30_cpu_clock_suspend(void)
+{
+ /* switch coresite to clk_m, save off original source */
+ tegra30_cpu_clk_sctx.clk_csite_src =
+ readl(reg_clk_base + CLK_RESET_SOURCE_CSITE);
+ writel(3<<30, reg_clk_base + CLK_RESET_SOURCE_CSITE);
+
+ tegra30_cpu_clk_sctx.cpu_burst =
+ readl(reg_clk_base + CLK_RESET_CCLK_BURST);
+ tegra30_cpu_clk_sctx.pllx_base =
+ readl(reg_clk_base + CLK_RESET_PLLX_BASE);
+ tegra30_cpu_clk_sctx.pllx_misc =
+ readl(reg_clk_base + CLK_RESET_PLLX_MISC);
+ tegra30_cpu_clk_sctx.cclk_divider =
+ readl(reg_clk_base + CLK_RESET_CCLK_DIVIDER);
+}
+
+static void tegra30_cpu_clock_resume(void)
+{
+ unsigned int reg, policy;
+
+ /* Is CPU complex already running on PLLX? */
+ reg = readl(reg_clk_base + CLK_RESET_CCLK_BURST);
+ policy = (reg >> CLK_RESET_CCLK_BURST_POLICY_SHIFT) & 0xF;
+
+ if (policy == CLK_RESET_CCLK_IDLE_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_IDLE_POLICY_SHIFT) & 0xF;
+ else if (policy == CLK_RESET_CCLK_RUN_POLICY)
+ reg = (reg >> CLK_RESET_CCLK_RUN_POLICY_SHIFT) & 0xF;
+ else
+ BUG();
+
+ if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) {
+ /* restore PLLX settings if CPU is on different PLL */
+ writel(tegra30_cpu_clk_sctx.pllx_misc,
+ reg_clk_base + CLK_RESET_PLLX_MISC);
+ writel(tegra30_cpu_clk_sctx.pllx_base,
+ reg_clk_base + CLK_RESET_PLLX_BASE);
+
+ /* wait for PLL stabilization if PLLX was enabled */
+ if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30))
+ udelay(300);
+ }
+
+ /*
+ * Restore original burst policy setting for calls resulting from CPU
+ * LP2 in idle or system suspend.
+ */
+ writel(tegra30_cpu_clk_sctx.cclk_divider,
+ reg_clk_base + CLK_RESET_CCLK_DIVIDER);
+ writel(tegra30_cpu_clk_sctx.cpu_burst,
+ reg_clk_base + CLK_RESET_CCLK_BURST);
+
+ writel(tegra30_cpu_clk_sctx.clk_csite_src,
+ reg_clk_base + CLK_RESET_SOURCE_CSITE);
+}
+#endif
+
static struct tegra_cpu_car_ops tegra30_cpu_car_ops = {
.wait_for_reset = tegra30_wait_cpu_in_reset,
.put_in_reset = tegra30_put_cpu_in_reset,
.out_of_reset = tegra30_cpu_out_of_reset,
.enable_clock = tegra30_enable_cpu_clock,
.disable_clock = tegra30_disable_cpu_clock,
+#ifdef CONFIG_PM_SLEEP
+ .rail_off_ready = tegra30_cpu_rail_off_ready,
+ .suspend = tegra30_cpu_clock_suspend,
+ .resume = tegra30_cpu_clock_resume,
+#endif
};
void __init tegra30_cpu_car_ops_init(void)
diff --git a/arch/arm/mach-tegra/tegra_cpu_car.h b/arch/arm/mach-tegra/tegra_cpu_car.h
index 30d063a..9764d31 100644
--- a/arch/arm/mach-tegra/tegra_cpu_car.h
+++ b/arch/arm/mach-tegra/tegra_cpu_car.h
@@ -30,6 +30,12 @@
* CPU clock un-gate
* disable_clock:
* CPU clock gate
+ * rail_off_ready:
+ * CPU is ready for rail off
+ * suspend:
+ * save the clock settings when CPU go into low-power state
+ * resume:
+ * restore the clock settings when CPU exit low-power state
*/
struct tegra_cpu_car_ops {
void (*wait_for_reset)(u32 cpu);
@@ -37,6 +43,11 @@ struct tegra_cpu_car_ops {
void (*out_of_reset)(u32 cpu);
void (*enable_clock)(u32 cpu);
void (*disable_clock)(u32 cpu);
+#ifdef CONFIG_PM_SLEEP
+ bool (*rail_off_ready)(void);
+ void (*suspend)(void);
+ void (*resume)(void);
+#endif
};
extern struct tegra_cpu_car_ops *tegra_cpu_car_ops;
@@ -81,6 +92,32 @@ static inline void tegra_disable_cpu_clock(u32 cpu)
tegra_cpu_car_ops->disable_clock(cpu);
}
+#ifdef CONFIG_PM_SLEEP
+static inline bool tegra_cpu_rail_off_ready(void)
+{
+ if (WARN_ON(!tegra_cpu_car_ops->rail_off_ready))
+ return false;
+
+ return tegra_cpu_car_ops->rail_off_ready();
+}
+
+static inline void tegra_cpu_clock_suspend(void)
+{
+ if (WARN_ON(!tegra_cpu_car_ops->suspend))
+ return;
+
+ tegra_cpu_car_ops->suspend();
+}
+
+static inline void tegra_cpu_clock_resume(void)
+{
+ if (WARN_ON(!tegra_cpu_car_ops->resume))
+ return;
+
+ tegra_cpu_car_ops->resume();
+}
+#endif
+
void tegra20_cpu_car_ops_init(void);
void tegra30_cpu_car_ops_init(void);
--
1.7.0.4
^ permalink raw reply related
* [PATCH V3 4/7] ARM: tegra30: common: enable csite clock
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351506501-600-1-git-send-email-josephl@nvidia.com>
Enable csite (debug and trace controller) clock at init to prevent it
be disabled. And this also the necessary clock for CPU be brought up or
resumed from a power-gating low power state.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V3:
* update the commit message
V2:
* no change
---
arch/arm/mach-tegra/common.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index d2d6dbb..a8f9175 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -108,6 +108,7 @@ static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
{ "sclk", "pll_p_out4", 102000000, true },
{ "hclk", "sclk", 102000000, true },
{ "pclk", "hclk", 51000000, true },
+ { "csite", NULL, 0, true },
{ NULL, NULL, 0, 0},
};
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V3 3/7] ARM: tegra30: cpuidle: add powered-down state for secondary CPUs
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351506501-600-1-git-send-email-josephl@nvidia.com>
This supports power-gated idle on secondary CPUs for Tegra30. The
secondary CPUs can go into powered-down state independently. When
CPU goes into this state, it saves it's contexts and puts itself
to flow controlled WFI state. After that, it will been power gated.
Be aware of that, you may see the legacy power state "LP2" in the
code which is exactly the same meaning of "CPU power down".
Based on the work by:
Scott Williams <scwilliams@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V3:
* remove the redundant header file
* update the subject and the commit message
* rename the LP2 state to "powered-down"
* refine the function name from "tegra30_idle_enter_lp2_cpu_n" to
"tegra30_cpu_core_power_down"
V2:
* static initialization for idle states when PM_SLEEP is true or not
* using inline fuction to replace the empty fuction when #ifdef false
* convert the phy cpu number by cpu_logical_map
* update the usage of tegra_cpu_lp2_mask (only one copy in the IRAM)
* update the usage of data cache maintenance API for LP2
* disable L1 data cache
* v7_flush_dcache_louis
* exit SMP coherency
---
arch/arm/mach-tegra/Makefile | 1 +
arch/arm/mach-tegra/cpuidle-tegra30.c | 86 +++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/pm.c | 75 ++++++++++++++++++++++++++++
arch/arm/mach-tegra/pm.h | 30 +++++++++++
arch/arm/mach-tegra/reset.h | 9 ++++
arch/arm/mach-tegra/sleep-tegra30.S | 22 ++++++++
arch/arm/mach-tegra/sleep.S | 29 +++++++++++
arch/arm/mach-tegra/sleep.h | 2 +
8 files changed, 254 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-tegra/pm.c
create mode 100644 arch/arm/mach-tegra/pm.h
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 9b80c1e..6f224f7 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -8,6 +8,7 @@ obj-y += pmc.o
obj-y += flowctrl.o
obj-y += powergate.o
obj-y += apbio.o
+obj-y += pm.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_CPU_IDLE) += sleep.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
index 37e7551..cc48d7f 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra30.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -22,21 +22,107 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
+#include <linux/clockchips.h>
#include <asm/cpuidle.h>
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+#include <asm/smp_plat.h>
+
+#include "pm.h"
+#include "sleep.h"
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra30_idle_lp2(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index);
+#endif
static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
+#ifdef CONFIG_PM_SLEEP
+ .state_count = 2,
+#else
.state_count = 1,
+#endif
.states = {
[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
+#ifdef CONFIG_PM_SLEEP
+ [1] = {
+ .enter = tegra30_idle_lp2,
+ .exit_latency = 2000,
+ .target_residency = 2200,
+ .power_usage = 0,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .name = "powered-down",
+ .desc = "CPU power gated",
+ },
+#endif
},
};
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
+#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_SMP
+static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
+
+ smp_wmb();
+
+ save_cpu_arch_register();
+
+ cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
+
+ restore_cpu_arch_register();
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
+
+ return true;
+}
+#else
+static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ return true;
+}
+#endif
+
+static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
+ bool entered_lp2 = false;
+
+ local_fiq_disable();
+
+ tegra_set_cpu_in_lp2(cpu);
+ cpu_pm_enter();
+
+ if (cpu == 0)
+ cpu_do_idle();
+ else
+ entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
+
+ cpu_pm_exit();
+ tegra_clear_cpu_in_lp2(cpu);
+
+ local_fiq_enable();
+
+ smp_rmb();
+
+ return (entered_lp2) ? index : 0;
+}
+#endif
+
int __init tegra30_cpuidle_init(void)
{
int ret;
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
new file mode 100644
index 0000000..39ee557
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.c
@@ -0,0 +1,75 @@
+/*
+ * CPU complex suspend & resume functions for Tegra SoCs
+ *
+ * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/cpumask.h>
+
+#include <mach/iomap.h>
+
+#include "reset.h"
+
+#ifdef CONFIG_PM_SLEEP
+static unsigned int g_diag_reg;
+static DEFINE_SPINLOCK(tegra_lp2_lock);
+
+void save_cpu_arch_register(void)
+{
+ /* read diagnostic register */
+ asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc");
+ return;
+}
+
+void restore_cpu_arch_register(void)
+{
+ /* write diagnostic register */
+ asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc");
+ return;
+}
+
+void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id)
+{
+ u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
+
+ spin_lock(&tegra_lp2_lock);
+
+ BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id)));
+ *cpu_in_lp2 &= ~BIT(phy_cpu_id);
+
+ spin_unlock(&tegra_lp2_lock);
+}
+
+bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id)
+{
+ bool last_cpu = false;
+ cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask;
+ u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
+
+ spin_lock(&tegra_lp2_lock);
+
+ BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id)));
+ *cpu_in_lp2 |= BIT(phy_cpu_id);
+
+ if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
+ last_cpu = true;
+
+ spin_unlock(&tegra_lp2_lock);
+ return last_cpu;
+}
+#endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
new file mode 100644
index 0000000..bcfc45f
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2010-2012 NVIDIA Corporation. All rights reserved.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * 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/>.
+ */
+
+#ifndef _MACH_TEGRA_PM_H_
+#define _MACH_TEGRA_PM_H_
+
+void save_cpu_arch_register(void);
+void restore_cpu_arch_register(void);
+
+void tegra_clear_cpu_in_lp2(int phy_cpu_id);
+bool tegra_set_cpu_in_lp2(int phy_cpu_id);
+
+#endif /* _MACH_TEGRA_PM_H_ */
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
index de88bf8..234cd6b 100644
--- a/arch/arm/mach-tegra/reset.h
+++ b/arch/arm/mach-tegra/reset.h
@@ -29,6 +29,8 @@
#ifndef __ASSEMBLY__
+#include <mach/irammap.h>
+
extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
void __tegra_cpu_reset_handler_start(void);
@@ -36,6 +38,13 @@ void __tegra_cpu_reset_handler(void);
void __tegra_cpu_reset_handler_end(void);
void tegra_secondary_startup(void);
+#ifdef CONFIG_PM_SLEEP
+#define tegra_cpu_lp2_mask \
+ (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
+ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
+ (u32)__tegra_cpu_reset_handler_start)))
+#endif
+
#define tegra_cpu_reset_handler_offset \
((u32)__tegra_cpu_reset_handler - \
(u32)__tegra_cpu_reset_handler_start)
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index be7614b..59984d7 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -17,6 +17,7 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
#include "sleep.h"
#include "flowctrl.h"
@@ -80,6 +81,7 @@ delay_1:
ldr r3, [r1] @ read CSR
str r3, [r1] @ clear CSR
tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
+ moveq r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT @ For LP2
movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug
str r3, [r2]
ldr r0, [r2]
@@ -103,3 +105,23 @@ wfe_war:
ENDPROC(tegra30_cpu_shutdown)
#endif
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra30_sleep_cpu_secondary_finish(unsigned long v2p)
+ *
+ * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
+ */
+ENTRY(tegra30_sleep_cpu_secondary_finish)
+ mov r7, lr
+
+ /* Flush and disable the L1 data cache */
+ bl tegra_disable_clean_inv_dcache
+
+ /* Powergate this CPU. */
+ mov r0, #0 @ power mode flags (!hotplug)
+ bl tegra30_cpu_shutdown
+ mov r0, #1 @ never return here
+ mov pc, r7
+ENDPROC(tegra30_sleep_cpu_secondary_finish)
+#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index ea81554..cba7a52 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -25,9 +25,38 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/cp15.h>
#include <mach/iomap.h>
#include "flowctrl.h"
#include "sleep.h"
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra_disable_clean_inv_dcache
+ *
+ * disable, clean & invalidate the D-cache
+ *
+ * Corrupted registers: r1-r3, r6, r8, r9-r11
+ */
+ENTRY(tegra_disable_clean_inv_dcache)
+ stmfd sp!, {r0, r4-r5, r7, r9-r11, lr}
+ dmb @ ensure ordering
+
+ /* Disable the D-cache */
+ mrc p15, 0, r2, c1, c0, 0
+ bic r2, r2, #CR_C
+ mcr p15, 0, r2, c1, c0, 0
+ isb
+
+ /* Flush the D-cache */
+ bl v7_flush_dcache_louis
+
+ /* Trun off coherency */
+ exit_smp r4, r5
+
+ ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
+ENDPROC(tegra_disable_clean_inv_dcache)
+
+#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index c9dec37..220fbd1 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -82,5 +82,7 @@ static inline void tegra20_hotplug_init(void) {}
static inline void tegra30_hotplug_init(void) {}
#endif
+int tegra30_sleep_cpu_secondary_finish(unsigned long);
+
#endif
#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V3 2/7] ARM: tegra: cpuidle: add CPU resume function
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351506501-600-1-git-send-email-josephl@nvidia.com>
The CPU suspending on Tegra means CPU power gating. We add a resume
function for taking care the CPUs that resume from power gating status.
This function was been hooked to the reset handler. We take care
everything here before go into kernel.
Be aware of that, you may see the legacy power status "LP2" in the code
which is exactly the same meaning of "CPU power down".
Based on the work by:
Scott Williams <scwilliams@nvidia.com>
Colin Cross <ccross@android.com>
Gary King <gking@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V3:
* update subject and the commit message
V2:
* moving the code that only for Tegra30 inside the ifdef in the tegra_resume
---
arch/arm/mach-tegra/headsmp.S | 58 +++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-tegra/reset.c | 6 ++++
arch/arm/mach-tegra/sleep.h | 1 +
3 files changed, 65 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
index 6addc78..36066f2 100644
--- a/arch/arm/mach-tegra/headsmp.S
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -69,6 +69,53 @@ ENTRY(tegra_secondary_startup)
b secondary_startup
ENDPROC(tegra_secondary_startup)
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra_resume
+ *
+ * CPU boot vector when restarting the a CPU following
+ * an LP2 transition. Also branched to by LP0 and LP1 resume after
+ * re-enabling sdram.
+ */
+ENTRY(tegra_resume)
+ bl v7_invalidate_l1
+ /* Enable coresight */
+ mov32 r0, 0xC5ACCE55
+ mcr p14, 0, r0, c7, c12, 6
+
+ cpu_id r0
+ cmp r0, #0 @ CPU0?
+ bne cpu_resume @ no
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ /* Are we on Tegra20? */
+ mov32 r6, TEGRA_APB_MISC_BASE
+ ldr r0, [r6, #APB_MISC_GP_HIDREV]
+ and r0, r0, #0xff00
+ cmp r0, #(0x20 << 8)
+ beq 1f @ Yes
+ /* Clear the flow controller flags for this CPU. */
+ mov32 r2, TEGRA_FLOW_CTRL_BASE + FLOW_CTRL_CPU0_CSR @ CPU0 CSR
+ ldr r1, [r2]
+ /* Clear event & intr flag */
+ orr r1, r1, \
+ #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ movw r0, #0x0FFD @ enable, cluster_switch, immed, & bitmaps
+ bic r1, r1, r0
+ str r1, [r2]
+1:
+#endif
+
+ /* enable SCU */
+ mov32 r0, TEGRA_ARM_PERIF_BASE
+ ldr r1, [r0]
+ orr r1, r1, #1
+ str r1, [r0]
+
+ b cpu_resume
+ENDPROC(tegra_resume)
+#endif
+
.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_start)
@@ -122,6 +169,17 @@ ENTRY(__tegra_cpu_reset_handler)
1:
#endif
+ /* Waking up from LP2? */
+ ldr r9, [r12, #RESET_DATA(MASK_LP2)]
+ tst r9, r11 @ if in_lp2
+ beq __is_not_lp2
+ ldr lr, [r12, #RESET_DATA(STARTUP_LP2)]
+ cmp lr, #0
+ bleq __die @ no LP2 startup handler
+ bx lr
+
+__is_not_lp2:
+
#ifdef CONFIG_SMP
/*
* Can only be secondary boot (initial or hotplug) but CPU 0
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c
index 5beb7eb..c48548f 100644
--- a/arch/arm/mach-tegra/reset.c
+++ b/arch/arm/mach-tegra/reset.c
@@ -26,6 +26,7 @@
#include <mach/irammap.h>
#include "reset.h"
+#include "sleep.h"
#include "fuse.h"
#define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \
@@ -80,5 +81,10 @@ void __init tegra_cpu_reset_handler_init(void)
virt_to_phys((void *)tegra_secondary_startup);
#endif
+#ifdef CONFIG_PM_SLEEP
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] =
+ virt_to_phys((void *)tegra_resume);
+#endif
+
tegra_cpu_reset_handler_enable();
}
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index e25a7cd..c9dec37 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -72,6 +72,7 @@
dsb
.endm
#else
+void tegra_resume(void);
#ifdef CONFIG_HOTPLUG_CPU
void tegra20_hotplug_init(void);
--
1.7.0.4
^ permalink raw reply related
* [PATCH V3 1/7] ARM: tegra: cpuidle: separate cpuidle driver for different chips
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351506501-600-1-git-send-email-josephl@nvidia.com>
The different Tegra chips may have different CPU idle states and data.
Individual CPU idle driver make it more easy to maintain.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V3:
* no change
V2:
* only remove the line of file name and path in the (c) header
---
arch/arm/mach-tegra/Makefile | 6 +++
.../mach-tegra/{cpuidle.c => cpuidle-tegra20.c} | 7 +--
.../mach-tegra/{cpuidle.c => cpuidle-tegra30.c} | 7 +--
arch/arm/mach-tegra/cpuidle.c | 47 +++++--------------
arch/arm/mach-tegra/cpuidle.h | 32 +++++++++++++
5 files changed, 55 insertions(+), 44 deletions(-)
copy arch/arm/mach-tegra/{cpuidle.c => cpuidle-tegra20.c} (91%)
copy arch/arm/mach-tegra/{cpuidle.c => cpuidle-tegra30.c} (91%)
create mode 100644 arch/arm/mach-tegra/cpuidle.h
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index e6929c6..9b80c1e 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -14,9 +14,15 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks_data.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o
+ifeq ($(CONFIG_CPU_IDLE),y)
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o
+endif
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-tegra30.o
+ifeq ($(CONFIG_CPU_IDLE),y)
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o
+endif
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_SMP) += reset.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle-tegra20.c
similarity index 91%
copy from arch/arm/mach-tegra/cpuidle.c
copy to arch/arm/mach-tegra/cpuidle-tegra20.c
index 4e0b07c..d32e8b0 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra20.c
@@ -1,6 +1,4 @@
/*
- * arch/arm/mach-tegra/cpuidle.c
- *
* CPU idle driver for Tegra CPUs
*
* Copyright (c) 2010-2012, NVIDIA Corporation.
@@ -27,7 +25,7 @@
#include <asm/cpuidle.h>
-struct cpuidle_driver tegra_idle_driver = {
+static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
@@ -39,7 +37,7 @@ struct cpuidle_driver tegra_idle_driver = {
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
-static int __init tegra_cpuidle_init(void)
+int __init tegra20_cpuidle_init(void)
{
int ret;
unsigned int cpu;
@@ -66,4 +64,3 @@ static int __init tegra_cpuidle_init(void)
}
return 0;
}
-device_initcall(tegra_cpuidle_init);
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
similarity index 91%
copy from arch/arm/mach-tegra/cpuidle.c
copy to arch/arm/mach-tegra/cpuidle-tegra30.c
index 4e0b07c..37e7551 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra30.c
@@ -1,6 +1,4 @@
/*
- * arch/arm/mach-tegra/cpuidle.c
- *
* CPU idle driver for Tegra CPUs
*
* Copyright (c) 2010-2012, NVIDIA Corporation.
@@ -27,7 +25,7 @@
#include <asm/cpuidle.h>
-struct cpuidle_driver tegra_idle_driver = {
+static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.owner = THIS_MODULE,
.en_core_tk_irqen = 1,
@@ -39,7 +37,7 @@ struct cpuidle_driver tegra_idle_driver = {
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
-static int __init tegra_cpuidle_init(void)
+int __init tegra30_cpuidle_init(void)
{
int ret;
unsigned int cpu;
@@ -66,4 +64,3 @@ static int __init tegra_cpuidle_init(void)
}
return 0;
}
-device_initcall(tegra_cpuidle_init);
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
index 4e0b07c..d065139 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -23,47 +23,26 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/cpuidle.h>
-#include <asm/cpuidle.h>
-
-struct cpuidle_driver tegra_idle_driver = {
- .name = "tegra_idle",
- .owner = THIS_MODULE,
- .en_core_tk_irqen = 1,
- .state_count = 1,
- .states = {
- [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
- },
-};
-
-static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
+#include "fuse.h"
+#include "cpuidle.h"
static int __init tegra_cpuidle_init(void)
{
int ret;
- unsigned int cpu;
- struct cpuidle_device *dev;
- struct cpuidle_driver *drv = &tegra_idle_driver;
- ret = cpuidle_register_driver(&tegra_idle_driver);
- if (ret) {
- pr_err("CPUidle driver registration failed\n");
- return ret;
+ switch (tegra_chip_id) {
+ case TEGRA20:
+ ret = tegra20_cpuidle_init();
+ break;
+ case TEGRA30:
+ ret = tegra30_cpuidle_init();
+ break;
+ default:
+ ret = -ENODEV;
+ break;
}
- for_each_possible_cpu(cpu) {
- dev = &per_cpu(tegra_idle_device, cpu);
- dev->cpu = cpu;
-
- dev->state_count = drv->state_count;
- ret = cpuidle_register_device(dev);
- if (ret) {
- pr_err("CPU%u: CPUidle device registration failed\n",
- cpu);
- return ret;
- }
- }
- return 0;
+ return ret;
}
device_initcall(tegra_cpuidle_init);
diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h
new file mode 100644
index 0000000..496204d
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
+ *
+ * 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/>.
+ */
+
+#ifndef __MACH_TEGRA_CPUIDLE_H
+#define __MACH_TEGRA_CPUIDLE_H
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+int tegra20_cpuidle_init(void);
+#else
+static inline int tegra20_cpuidle_init(void) { return -ENODEV; }
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+int tegra30_cpuidle_init(void);
+#else
+static inline int tegra30_cpuidle_init(void) { return -ENODEV; }
+#endif
+
+#endif
--
1.7.0.4
^ permalink raw reply related
* [PATCH V3 0/7] ARM: tegra30: cpuidle: add a powered-down state
From: Joseph Lo @ 2012-10-29 10:28 UTC (permalink / raw)
To: linux-arm-kernel
This adds a "powered-down" state for cpuidle. It's a power gating idle
mode. It supports the secondary CPUs (i.e., CPU1-CPU3) to go into
powered-down state independently. When any of the secondary CPUs go into
this state, it can be power gated alone. There is a limitation on CPU0.
The CPU0 can go into powered-down state only when all secondary CPU is
offline. After CPU0 is in powered-down state, the CPU rail can be turned
off.
All CPUs entering powered-down state is not working. The CPU0 enters this
state only when secondary CPU is offline. This can be coverd by CPUquiet
and cluster switching mechanism.
We also remove the ambiguous name of LP2 in the cpuidle state.
Verified on Seaboard(Tegra20) and Cardhu(Tegra30).
This patch set should depend on these patches:
ARM: tegra: rename the file of "sleep-tXX" to "sleep-tegraXX"
ARM: tegra30: clocks: add AHB and APB clocks
ARM: tegra: dt: add L2 cache controller
ARM: tegra: common: using OF api for L2 cache init
Major changes:
V3:
* fix a potential issue that will cause CPU be corrupted
* rename the LP2 state to powered-down state
V2:
* refine L1 cache maintenance function
Previous work can be found at:
V2:
http://www.mail-archive.com/linux-tegra at vger.kernel.org/msg06478.html
V1:
http://www.mail-archive.com/linux-tegra at vger.kernel.org/msg06319.html
Joseph Lo (7):
ARM: tegra: cpuidle: separate cpuidle driver for different chips
ARM: tegra: cpuidle: add CPU resume function
ARM: tegra30: cpuidle: add powered-down state for secondary CPUs
ARM: tegra30: common: enable csite clock
ARM: tegra30: clocks: add CPU low-power function into
tegra_cpu_car_ops
ARM: tegra30: flowctrl: add cpu_suspend_exter/exit function
ARM: tegra30: cpuidle: add powered-down state for CPU0
arch/arm/mach-tegra/Makefile | 7 +
arch/arm/mach-tegra/common.c | 1 +
.../mach-tegra/{cpuidle.c => cpuidle-tegra20.c} | 7 +-
arch/arm/mach-tegra/cpuidle-tegra30.c | 188 +++++++++++++++++
arch/arm/mach-tegra/cpuidle.c | 47 ++---
arch/arm/mach-tegra/cpuidle.h | 32 +++
arch/arm/mach-tegra/flowctrl.c | 47 +++++
arch/arm/mach-tegra/flowctrl.h | 8 +
arch/arm/mach-tegra/headsmp.S | 58 +++++
arch/arm/mach-tegra/pm.c | 219 ++++++++++++++++++++
arch/arm/mach-tegra/pm.h | 33 +++
arch/arm/mach-tegra/reset.c | 6 +
arch/arm/mach-tegra/reset.h | 9 +
arch/arm/mach-tegra/sleep-tegra30.S | 66 ++++++
arch/arm/mach-tegra/sleep.S | 71 +++++++
arch/arm/mach-tegra/sleep.h | 5 +
arch/arm/mach-tegra/tegra30_clocks.c | 107 ++++++++++
arch/arm/mach-tegra/tegra_cpu_car.h | 37 ++++
18 files changed, 909 insertions(+), 39 deletions(-)
copy arch/arm/mach-tegra/{cpuidle.c => cpuidle-tegra20.c} (91%)
create mode 100644 arch/arm/mach-tegra/cpuidle-tegra30.c
create mode 100644 arch/arm/mach-tegra/cpuidle.h
create mode 100644 arch/arm/mach-tegra/pm.c
create mode 100644 arch/arm/mach-tegra/pm.h
^ permalink raw reply
* [PATCH 2/5] ARM: PXA: Zipit Z2: Add USB host and device support
From: Vasily Khoruzhick @ 2012-10-29 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201210291122.25510.marex@denx.de>
On Mon, Oct 29, 2012 at 1:22 PM, Marek Vasut <marex@denx.de> wrote:
>
> Uh, you already have that. U-Boot can boot kernel and pass DT properly, just use
> latest 2012.10 release which just got out a few weeks ago.
It still lacks pxa27x keypad support, same for fb rotation :) So I
need to port some older patches
>> > Some parts like the uart, host-mode usb, mmc, gpio-leds, gpio-keys, i2c
>> > etc are already finished and should work instantly. What's missing is
>> > the spi controller and the display, but maybe you can help getting these
>> > done?
>>
>> What about libertas? I still don't understand how DT handles
>> platform-specific callbacks like set_power(). And what about pm_power_off
>> hook?
>
> Start small, platform crap and DT goo can co-exist for a while.
OK
> Best regards,
> Marek Vasut
Regards
Vasily
^ permalink raw reply
* [PATCH V2 2/2] ARM: tegra: common: using OF api for L2 cache init
From: Joseph Lo @ 2012-10-29 10:25 UTC (permalink / raw)
To: linux-arm-kernel
Moving L2 cache init to DT support.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* no changes
---
arch/arm/mach-tegra/common.c | 11 ++++-------
1 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 177f164..d2d6dbb 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -113,20 +113,17 @@ static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
#endif
-static void __init tegra_init_cache(u32 tag_latency, u32 data_latency)
+static void __init tegra_init_cache(void)
{
#ifdef CONFIG_CACHE_L2X0
void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
u32 aux_ctrl, cache_type;
- writel_relaxed(tag_latency, p + L2X0_TAG_LATENCY_CTRL);
- writel_relaxed(data_latency, p + L2X0_DATA_LATENCY_CTRL);
-
cache_type = readl(p + L2X0_CACHE_TYPE);
aux_ctrl = (cache_type & 0x700) << (17-8);
aux_ctrl |= 0x6C000001;
- l2x0_init(p, aux_ctrl, 0x8200c3fe);
+ l2x0_of_init(aux_ctrl, 0x8200c3fe);
#endif
}
@@ -138,7 +135,7 @@ void __init tegra20_init_early(void)
tegra_init_fuse();
tegra2_init_clocks();
tegra_clk_init_from_table(tegra20_clk_init_table);
- tegra_init_cache(0x331, 0x441);
+ tegra_init_cache();
tegra_pmc_init();
tegra_powergate_init();
tegra20_hotplug_init();
@@ -151,7 +148,7 @@ void __init tegra30_init_early(void)
tegra_init_fuse();
tegra30_init_clocks();
tegra_clk_init_from_table(tegra30_clk_init_table);
- tegra_init_cache(0x441, 0x551);
+ tegra_init_cache();
tegra_pmc_init();
tegra_powergate_init();
tegra30_hotplug_init();
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 1/2] ARM: tegra: dt: add L2 cache controller
From: Joseph Lo @ 2012-10-29 10:25 UTC (permalink / raw)
To: linux-arm-kernel
Add L2 cache controller binding into DT for Tegra.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* remove "L2" label for cache-controller DT binding
---
arch/arm/boot/dts/tegra20.dtsi | 9 +++++++++
arch/arm/boot/dts/tegra30.dtsi | 9 +++++++++
2 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 6934bca..21ca861 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -4,6 +4,15 @@
compatible = "nvidia,tegra20";
interrupt-parent = <&intc>;
+ cache-controller at 50043000 {
+ compatible = "arm,pl310-cache";
+ reg = <0x50043000 0x1000>;
+ arm,data-latency = <5 5 2>;
+ arm,tag-latency = <4 4 2>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
timer at 50004600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0x50040600 0x20>;
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index 81f5df4..568f291 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -4,6 +4,15 @@
compatible = "nvidia,tegra30";
interrupt-parent = <&intc>;
+ cache-controller at 50043000 {
+ compatible = "arm,pl310-cache";
+ reg = <0x50043000 0x1000>;
+ arm,data-latency = <6 6 2>;
+ arm,tag-latency = <5 5 2>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
timer at 50004600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0x50040600 0x20>;
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2] ARM: tegra30: clocks: add AHB and APB clocks
From: Joseph Lo @ 2012-10-29 10:25 UTC (permalink / raw)
To: linux-arm-kernel
Adding the AHB and APB bus clock for Tegra30.
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
V2:
* fix the round_rate func when "rate >= parent_rate"
---
arch/arm/mach-tegra/common.c | 4 +
arch/arm/mach-tegra/tegra30_clocks.c | 106 +++++++++++++++++++++++++++++
arch/arm/mach-tegra/tegra30_clocks.h | 1 +
arch/arm/mach-tegra/tegra30_clocks_data.c | 46 +++++++++++++
4 files changed, 157 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 0b0a5f5..177f164 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -104,6 +104,10 @@ static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
{ "clk_m", NULL, 0, true },
{ "pll_p", "clk_m", 408000000, true },
{ "pll_p_out1", "pll_p", 9600000, true },
+ { "pll_p_out4", "pll_p", 102000000, true },
+ { "sclk", "pll_p_out4", 102000000, true },
+ { "hclk", "sclk", 102000000, true },
+ { "pclk", "hclk", 51000000, true },
{ NULL, NULL, 0, 0},
};
#endif
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c
index e9de5df..665618a 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.c
+++ b/arch/arm/mach-tegra/tegra30_clocks.c
@@ -792,6 +792,112 @@ struct clk_ops tegra30_twd_ops = {
.recalc_rate = tegra30_twd_clk_recalc_rate,
};
+/* bus clock functions */
+static int tegra30_bus_clk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_tegra *c = to_clk_tegra(hw);
+ u32 val = clk_readl(c->reg);
+
+ c->state = ((val >> c->reg_shift) & BUS_CLK_DISABLE) ? OFF : ON;
+ return c->state;
+}
+
+static int tegra30_bus_clk_enable(struct clk_hw *hw)
+{
+ struct clk_tegra *c = to_clk_tegra(hw);
+ u32 val;
+
+ val = clk_readl(c->reg);
+ val &= ~(BUS_CLK_DISABLE << c->reg_shift);
+ clk_writel(val, c->reg);
+
+ return 0;
+}
+
+static void tegra30_bus_clk_disable(struct clk_hw *hw)
+{
+ struct clk_tegra *c = to_clk_tegra(hw);
+ u32 val;
+
+ val = clk_readl(c->reg);
+ val |= BUS_CLK_DISABLE << c->reg_shift;
+ clk_writel(val, c->reg);
+}
+
+static unsigned long tegra30_bus_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct clk_tegra *c = to_clk_tegra(hw);
+ u32 val = clk_readl(c->reg);
+ u64 rate = prate;
+
+ c->div = ((val >> c->reg_shift) & BUS_CLK_DIV_MASK) + 1;
+ c->mul = 1;
+
+ if (c->mul != 0 && c->div != 0) {
+ rate *= c->mul;
+ rate += c->div - 1; /* round up */
+ do_div(rate, c->div);
+ }
+ return rate;
+}
+
+static int tegra30_bus_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_tegra *c = to_clk_tegra(hw);
+ int ret = -EINVAL;
+ u32 val;
+ int i;
+
+ val = clk_readl(c->reg);
+ for (i = 1; i <= 4; i++) {
+ if (rate == parent_rate / i) {
+ val &= ~(BUS_CLK_DIV_MASK << c->reg_shift);
+ val |= (i - 1) << c->reg_shift;
+ clk_writel(val, c->reg);
+ c->div = i;
+ c->mul = 1;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static long tegra30_bus_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ unsigned long parent_rate = *prate;
+ s64 divider;
+
+ if (rate >= parent_rate)
+ return parent_rate;
+
+ divider = parent_rate;
+ divider += rate - 1;
+ do_div(divider, rate);
+
+ if (divider < 0)
+ return divider;
+
+ if (divider > 4)
+ divider = 4;
+ do_div(parent_rate, divider);
+
+ return parent_rate;
+}
+
+struct clk_ops tegra30_bus_ops = {
+ .is_enabled = tegra30_bus_clk_is_enabled,
+ .enable = tegra30_bus_clk_enable,
+ .disable = tegra30_bus_clk_disable,
+ .set_rate = tegra30_bus_clk_set_rate,
+ .round_rate = tegra30_bus_clk_round_rate,
+ .recalc_rate = tegra30_bus_clk_recalc_rate,
+};
+
/* Blink output functions */
static int tegra30_blink_clk_is_enabled(struct clk_hw *hw)
{
diff --git a/arch/arm/mach-tegra/tegra30_clocks.h b/arch/arm/mach-tegra/tegra30_clocks.h
index f2f88fe..7a34adb 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.h
+++ b/arch/arm/mach-tegra/tegra30_clocks.h
@@ -34,6 +34,7 @@ extern struct clk_ops tegra_clk_out_ops;
extern struct clk_ops tegra30_super_ops;
extern struct clk_ops tegra30_blink_clk_ops;
extern struct clk_ops tegra30_twd_ops;
+extern struct clk_ops tegra30_bus_ops;
extern struct clk_ops tegra30_periph_clk_ops;
extern struct clk_ops tegra30_dsib_clk_ops;
extern struct clk_ops tegra_nand_clk_ops;
diff --git a/arch/arm/mach-tegra/tegra30_clocks_data.c b/arch/arm/mach-tegra/tegra30_clocks_data.c
index 3d2e553..7bc8b1d 100644
--- a/arch/arm/mach-tegra/tegra30_clocks_data.c
+++ b/arch/arm/mach-tegra/tegra30_clocks_data.c
@@ -711,6 +711,50 @@ static struct clk tegra_clk_sclk = {
.num_parents = ARRAY_SIZE(mux_sclk),
};
+static const char *tegra_hclk_parent_names[] = {
+ "tegra_sclk",
+};
+
+static struct clk *tegra_hclk_parents[] = {
+ &tegra_clk_sclk,
+};
+
+static struct clk tegra_hclk;
+static struct clk_tegra tegra_hclk_hw = {
+ .hw = {
+ .clk = &tegra_hclk,
+ },
+ .flags = DIV_BUS,
+ .reg = 0x30,
+ .reg_shift = 4,
+ .max_rate = 378000000,
+ .min_rate = 12000000,
+};
+DEFINE_CLK_TEGRA(hclk, 0, &tegra30_bus_ops, 0, tegra_hclk_parent_names,
+ tegra_hclk_parents, &tegra_clk_sclk);
+
+static const char *tegra_pclk_parent_names[] = {
+ "tegra_hclk",
+};
+
+static struct clk *tegra_pclk_parents[] = {
+ &tegra_hclk,
+};
+
+static struct clk tegra_pclk;
+static struct clk_tegra tegra_pclk_hw = {
+ .hw = {
+ .clk = &tegra_pclk,
+ },
+ .flags = DIV_BUS,
+ .reg = 0x30,
+ .reg_shift = 0,
+ .max_rate = 167000000,
+ .min_rate = 12000000,
+};
+DEFINE_CLK_TEGRA(pclk, 0, &tegra30_bus_ops, 0, tegra_pclk_parent_names,
+ tegra_pclk_parents, &tegra_hclk);
+
static const char *mux_blink[] = {
"clk_32k",
};
@@ -1325,6 +1369,8 @@ struct clk *tegra_ptr_clks[] = {
&tegra_cml1,
&tegra_pciex,
&tegra_clk_sclk,
+ &tegra_hclk,
+ &tegra_pclk,
&tegra_clk_blink,
&tegra30_clk_twd,
};
--
1.7.0.4
^ permalink raw reply related
* [PATCH 2/5] ARM: PXA: Zipit Z2: Add USB host and device support
From: Marek Vasut @ 2012-10-29 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CA+E=qVcnacT8L5GkqTxdowKt2KkoEWMhd3+XBk6nKsEEDtWecg@mail.gmail.com>
Dear Vasily Khoruzhick,
[...]
> >
> > I rather wanted to encourage you to work on the PXA2xx DT stuff :)
>
> I'm not refusing to work on PXA2xx DT stuff, just want to get bugfixes in
> place, some of them are serious enough (like fixing oops on poweroff)
>
> > Please have a quick look at arch/arm/mach-pxa/pxa-dt.c - what it takes
> > for basic 270 support is just copying the logic that is there already
> > for the 3xx models.
>
> Sure, but I need to get DT-capable bootloader working on Z2 in first
> place, right?
Uh, you already have that. U-Boot can boot kernel and pass DT properly, just use
latest 2012.10 release which just got out a few weeks ago.
> > Some parts like the uart, host-mode usb, mmc, gpio-leds, gpio-keys, i2c
> > etc are already finished and should work instantly. What's missing is
> > the spi controller and the display, but maybe you can help getting these
> > done?
>
> What about libertas? I still don't understand how DT handles
> platform-specific callbacks like set_power(). And what about pm_power_off
> hook?
Start small, platform crap and DT goo can co-exist for a while.
Best regards,
Marek Vasut
^ permalink raw reply
* [PATCH v2 1/5] clk: samsung: add common clock framework support for Samsung platforms
From: Thomas Abraham @ 2012-10-29 10:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50856C6B.3090001@samsung.com>
Hi Sylwester,
Thanks for your comments. As usual, your comments are very helpful.
On 22 October 2012 21:25, Sylwester Nawrocki <s.nawrocki@samsung.com> wrote:
> Hi Thomas,
>
> On 10/07/2012 07:10 PM, Thomas Abraham wrote:
>> All Samsung platforms include several types of clocks including fixed-rate,
>> mux, divider and gate clock types. There are typically hundreds of such clocks
>> on each of the Samsung platforms. To enable Samsung platforms to register these
>> clocks using the common clock framework, a bunch of utility functions are
>> introduced here which simplify the clock registration process.
>>
>> In addition to the basic types of clock supported by common clock framework,
>> a Samsung specific representation of the PLL clocks is also introduced.
>>
>> Both legacy and device tree based Samsung platforms are supported. On legacy
>> platforms, the clocks are statically instantiated and registered with common
>> clock framework. On device tree enabled platforms, the device tree is
>> searched and all clock nodes found are registered. It is also possible to
>> register statically instantiated clocks on device tree enabled platforms.
>>
>> Cc: Mike Turquette <mturquette@ti.com>
>> Cc: Kukjin Kim <kgene.kim@samsung.com>
>> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
>
> Thanks for the patch. I'm trying to use this series on an Exynos4412
> SoC based board. I think it wasn't tested with Exynos4x12 (with FDT
> support), was it ?
No, it has not been tested on any Exynos4x12 based board. I have
tested it only for Exynos4210 based origen board.
>
> I have a few comments, please see below.
>
>> ---
>> drivers/clk/Makefile | 1 +
>> drivers/clk/samsung/Makefile | 5 +
>> drivers/clk/samsung/clk.c | 414 ++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/samsung/clk.h | 212 +++++++++++++++++++++
>> 4 files changed, 632 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/clk/samsung/Makefile
>> create mode 100644 drivers/clk/samsung/clk.c
>> create mode 100644 drivers/clk/samsung/clk.h
>>
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index 71a25b9..95644e3 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -19,6 +19,7 @@ endif
>> obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
>> obj-$(CONFIG_ARCH_U8500) += ux500/
>> obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
>> +obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
>>
>> # Chip specific
>> obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
>> diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
>> new file mode 100644
>> index 0000000..3f926b0
>> --- /dev/null
>> +++ b/drivers/clk/samsung/Makefile
>> @@ -0,0 +1,5 @@
>> +#
>> +# Samsung Clock specific Makefile
>> +#
>> +
>> +obj-$(CONFIG_PLAT_SAMSUNG) += clk.o
>> diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
>> new file mode 100644
>> index 0000000..f5e269a
>> --- /dev/null
>> +++ b/drivers/clk/samsung/clk.c
>> @@ -0,0 +1,414 @@
>> +/*
>> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
>> + * Copyright (c) 2012 Linaro Ltd.
>> + *
>> + * 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 file includes utility functions to register clocks to common
>> + * clock framework for Samsung platforms. This includes an implementation
>> + * of Samsung 'pll type' clock to represent the implementation of the
>> + * pll found on Samsung platforms. In addition to that, utility functions
>> + * to register mux, div, gate and fixed rate types of clocks are included.
>> +*/
>> +
>> +#include <linux/of.h>
>> +#include "clk.h"
>> +
>> +#define MAX_PARENT_CLKS 16
>> +#define to_clk_pll(_hw) container_of(_hw, struct samsung_pll_clock, hw)
>> +
>> +static DEFINE_SPINLOCK(lock);
>> +static void __iomem *reg_base;
>> +static void __iomem *reg_fin_pll;
>> +
>> +void __init samsung_clk_set_ctrl_base(void __iomem *base)
>> +{
>> + reg_base = base;
>> +}
>> +
>> +void __init samsung_clk_set_finpll_reg(void __iomem *reg)
>> +{
>> + reg_fin_pll = reg;
>> +}
>> +
>> +/* determine the output clock speed of the pll */
>> +static unsigned long samsung_pll_clock_recalc_rate(struct clk_hw *hw,
>> + unsigned long parent_rate)
>> +{
>> + struct samsung_pll_clock *clk_pll = to_clk_pll(hw);
>> +
>> + if (clk_pll->get_rate)
>> + return to_clk_pll(hw)->get_rate(parent_rate);
>> +
>> + return 0;
>> +}
>> +
>> +/* round operation not supported */
>> +static long samsung_pll_clock_round_rate(struct clk_hw *hw, unsigned long drate,
>> + unsigned long *prate)
>> +{
>> + return samsung_pll_clock_recalc_rate(hw, *prate);
>> +}
>> +
>> +/* set the clock output rate of the pll */
>> +static int samsung_pll_clock_set_rate(struct clk_hw *hw, unsigned long drate,
>> + unsigned long prate)
>> +{
>> + struct samsung_pll_clock *clk_pll = to_clk_pll(hw);
>> +
>> + if (clk_pll->set_rate)
>> + return to_clk_pll(hw)->set_rate(drate);
>> +
>> + return 0;
>> +}
>> +
>> +/* clock operations for samsung pll clock type */
>> +static const struct clk_ops samsung_pll_clock_ops = {
>> + .recalc_rate = samsung_pll_clock_recalc_rate,
>> + .round_rate = samsung_pll_clock_round_rate,
>> + .set_rate = samsung_pll_clock_set_rate,
>> +};
>> +
>> +/* register a samsung pll type clock */
>> +void __init samsung_clk_register_pll(const char *name, const char **pnames,
>> + struct device_node *np,
>> + int (*set_rate)(unsigned long rate),
>> + unsigned long (*get_rate)(unsigned long rate))
>> +{
>> + struct samsung_pll_clock *clk_pll;
>> + struct clk *clk;
>> + struct clk_init_data init;
>> + int ret;
>> +
>> + clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL);
>> + if (!clk_pll) {
>> + pr_err("%s: could not allocate pll clk %s\n", __func__, name);
>> + return;
>> + }
>> +
>> + init.name = name;
>> + init.ops = &samsung_pll_clock_ops;
>> + init.flags = CLK_GET_RATE_NOCACHE;
>> + init.parent_names = pnames;
>> + init.num_parents = 1;
>> +
>> + clk_pll->set_rate = set_rate;
>> + clk_pll->get_rate = get_rate;
>> + clk_pll->hw.init = &init;
>> +
>> + /* register the clock */
>> + clk = clk_register(NULL, &clk_pll->hw);
>> + if (IS_ERR(clk)) {
>> + pr_err("%s: failed to register pll clock %s\n", __func__,
>> + name);
>> + kfree(clk_pll);
>> + return;
>> + }
>> +
>> +#ifdef CONFIG_OF
>> + if (np)
>> + of_clk_add_provider(np, of_clk_src_simple_get, clk);
>> +#endif
>
> Is it really required to do clk_register() and of_clk_add_provider() for
> each single clock ? This seems more heavy than it could be. Looking at
of_clk_add_provider call for every clock instance is not really
required but it does allow platform code to lookup the clock and
retrieve/display the clock speed. That was the intention to add a
lookup for all the clocks.
> drivers/clk/mxs/clk-imx28.c, it registers only single clock provider for
> whole group of clocks. Also, couldn't we statically define most of the
> clocks and still register them so they can be used with platforms using
> FDT ? Something along the lines of imx28 implementation (arch/arm/boot/dts
> /imx28.dtsi), where a clock is specified at consumer device node by
> a phandle to the clock controller node and a clock index ?
We could do it that way. I was tempted to list out all the clocks in
device tree and then register them so that there is no static
definition of the clocks needed. You seem to prefer not to do that. I
am fine with either way, static or device tree based registration.
>
> Besides that, what bothers me with in the current approach is the
> clock consumers being defined through one big data structure together
> with the actual clocks. Not all clock objects are going to have
> consumers, some resources are waisted by using flat tables of those
> big data structure objects. Perhaps we could use two tables, one for the
> platform clocks and one for the consumers ? These common clock driver
> is intended to cover all Samsung SoC, I would expect all samsung
> sub-archs getting converted to use it eventually, with as many of them
> as possible then reworked to support device tree. It's a lot of work
> and is going to take some time, but it would be good to have it planned
> in advance. That said I'm not sure the common samsung clock driver in
> non-dt variant would be really a temporary thing.
Non-dt support in Samsung common clock driver will be maintained. But
for existing Exynos4 non-dt platforms, it should be possible to
convert them to completely device tree based platforms.
>
>> + /*
>> + * Register a clock lookup for the pll-type clock even if this
>> + * has been instantiated from device tree. This helps to do
>> + * clk_get() lookup on this clock for pruposes of displaying its
>> + * clock speed at boot time.
>> + */
>> + ret = clk_register_clkdev(clk, name, NULL);
>> + if (ret)
>> + pr_err("%s: failed to register clock lookup for %s", __func__,
>> + name);
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +/* register a samsung pll type clock instantiated from device tree */
>> +void __init samsung_of_clk_register_pll(struct device_node *np)
>> +{
>> + const char *clk_name = np->name;
>> + const char *parent_name;
>> +
>> + of_property_read_string(np, "clock-output-names", &clk_name);
>> + parent_name = of_clk_get_parent_name(np, 0);
>> + samsung_clk_register_pll(clk_name, &parent_name, np, NULL, NULL);
>> +}
>> +#endif
>> +
>> +/*
>> + * Allow platform specific implementations to attach set_rate and get_rate
>> + * callbacks for the pll type clock. Typical calling sequence..
>> + *
>> + * struct clk *clk = clk_get(NULL, "pll-clk-name");
>> + * samsung_pll_clk_set_cb(clk, pll_set_rate, pll_get_rate);
>> + */
>> +void __init samsung_pll_clk_set_cb(struct clk *clk,
>> + int (*set_rate)(unsigned long rate),
>> + unsigned long (*get_rate)(unsigned long rate))
>> +{
>> + struct samsung_pll_clock *clk_pll;
>> + struct clk_hw *hw = __clk_get_hw(clk);
>> +
>> + clk_pll = to_clk_pll(hw);
>> + clk_pll->set_rate = set_rate;
>> + clk_pll->get_rate = get_rate;
>> +}
>> +
>> +/* register a list of fixed clocks (used only for non-dt platforms) */
>> +void __init samsung_clk_register_fixed_rate(
>> + struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk)
>> +{
>> + struct clk *clk;
>> + unsigned int idx, ret;
>> +
>> + for (idx = 0; idx < nr_clk; idx++, clk_list++) {
>> + clk = clk_register_fixed_rate(NULL, clk_list->name, NULL,
>> + clk_list->flags, clk_list->fixed_rate);
>> + if (IS_ERR(clk)) {
>> + pr_err("%s: failed to register clock %s\n", __func__,
>> + clk_list->name);
>> + continue;
>> + }
>> +
>> + /*
>> + * Register a lookup which will help in clk_get() and
>> + * printing the clock rate during clock initialization.
>> + */
>> + ret = clk_register_clkdev(clk, clk_list->name,
>> + clk_list->dev_name);
>> + if (ret)
>> + pr_err("clock: failed to register clock lookup for %s",
>> + clk_list->name);
>> + }
>> +}
>> +
>> +/* register a list of mux clocks */
>> +void __init samsung_clk_register_mux(struct samsung_mux_clock *clk_list,
>> + unsigned int nr_clk)
>> +{
>> + struct clk *clk;
>> + unsigned int idx, ret;
>> +
>> + for (idx = 0; idx < nr_clk; idx++, clk_list++) {
>> + clk = clk_register_mux(NULL, clk_list->name,
>> + clk_list->parent_names, clk_list->num_parents,
>> + clk_list->flags, clk_list->reg, clk_list->shift,
>> + clk_list->width, clk_list->mux_flags, &lock);
>> + if (IS_ERR(clk)) {
>> + pr_err("%s: failed to register clock %s\n", __func__,
>> + clk_list->name);
>> + continue;
>> + }
>> +
>> +#ifdef CONFIG_OF
>> + if (clk_list->np)
>> + of_clk_add_provider(clk_list->np, of_clk_src_simple_get,
>> + clk);
>> +#endif
>> +
>> + ret = clk_register_clkdev(clk, clk_list->name,
>> + clk_list->dev_name);
>> + if (ret)
>> + pr_err("%s: failed to register clock lookup for %s",
>> + __func__, clk_list->name);
>> +
>> + if (clk_list->alias) {
>> + ret = clk_register_clkdev(clk, clk_list->alias,
>> + clk_list->dev_name);
>> + if (ret)
>> + pr_err("%s: failed to register lookup %s\n",
>> + __func__, clk_list->alias);
>> + }
>> + }
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +/* register a samsung mux type clock instantiated from device tree */
>> +void __init samsung_of_clk_register_mux(struct device_node *np)
>> +{
>> + struct samsung_mux_clock mux_clk;
>> + const char *clk_name = np->name;
>> + const char *parent_names[MAX_PARENT_CLKS];
>> + u32 reg_info[3];
>> + int idx = 0;
>> +
>> + of_property_read_string(np, "clock-output-names", &clk_name);
>> + do {
>> + /* get names of all parent clocks */
>> + parent_names[idx] = of_clk_get_parent_name(np, idx);
>> + idx++;
>> + } while (parent_names[idx-1]);
>> +
>> + if (of_property_read_u32_array(np, "reg-info", reg_info, 3))
>> + pr_err("%s: invalid register info in node\n", __func__);
>> +
>> + mux_clk.name = clk_name;
>> + mux_clk.parent_names = parent_names;
>> + mux_clk.num_parents = idx - 1;
>> + mux_clk.reg = (void __iomem *)(reg_base + reg_info[0]);
>> + mux_clk.shift = reg_info[1];
>> + mux_clk.width = reg_info[2];
>> + mux_clk.dev_name = NULL;
>> + mux_clk.flags = 0;
>> + mux_clk.mux_flags = 0;
>> + mux_clk.alias = NULL;
>> + mux_clk.np = np;
>> +
>> + if (!strcmp(mux_clk.name, "fin_pll"))
>> + mux_clk.reg = reg_fin_pll;
>> +
>> + samsung_clk_register_mux(&mux_clk, 1);
>> +}
>> +#endif
>> +
>> +/* register a list of div clocks */
>> +void __init samsung_clk_register_div(struct samsung_div_clock *clk_list,
>> + unsigned int nr_clk)
>> +{
>> + struct clk *clk;
>> + unsigned int idx, ret;
>> +
>> + for (idx = 0; idx < nr_clk; idx++, clk_list++) {
>> + clk = clk_register_divider(NULL, clk_list->name,
>> + clk_list->parent_name, clk_list->flags, clk_list->reg,
>> + clk_list->shift, clk_list->width, clk_list->div_flags,
>> + &lock);
>> + if (IS_ERR(clk)) {
>> + pr_err("clock: failed to register clock %s\n",
>> + clk_list->name);
>> + continue;
>> + }
>> +
>> +#ifdef CONFIG_OF
>> + if (clk_list->np)
>> + of_clk_add_provider(clk_list->np, of_clk_src_simple_get,
>> + clk);
>> +#endif
>> +
>> + ret = clk_register_clkdev(clk, clk_list->name,
>> + clk_list->dev_name);
>> + if (ret)
>> + pr_err("%s: failed to register clock lookup for %s",
>> + __func__, clk_list->name);
>> +
>> + if (clk_list->alias) {
>> + ret = clk_register_clkdev(clk, clk_list->alias,
>> + clk_list->dev_name);
>> + if (ret)
>> + pr_err("%s: failed to register lookup %s\n",
>> + __func__, clk_list->alias);
>> + }
>> + }
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +/* register a samsung div type clock instantiated from device tree */
>> +void __init samsung_of_clk_register_div(struct device_node *np)
>> +{
>> + struct samsung_div_clock clk_div;
>> + const char *clk_name = np->name;
>> + const char *parent_name;
>> + u32 reg_info[3];
>> +
>> + of_property_read_string(np, "clock-output-names", &clk_name);
>> + parent_name = of_clk_get_parent_name(np, 0);
>> + if (of_property_read_u32_array(np, "reg-info", reg_info, 3))
>> + pr_err("%s: invalid register info in node\n", __func__);
>> +
>> + clk_div.name = clk_name;
>> + clk_div.parent_name = parent_name;
>> + clk_div.reg = (void __iomem *)(reg_base + reg_info[0]);
>> + clk_div.shift = reg_info[1];
>> + clk_div.width = reg_info[2];
>> + clk_div.dev_name = NULL;
>> + clk_div.flags = 0;
>> + clk_div.div_flags = 0;
>> + clk_div.alias = NULL;
>> + clk_div.np = np;
>> +
>> + samsung_clk_register_div(&clk_div, 1);
>> +}
>> +#endif
>> +
>> +/* register a list of gate clocks */
>> +void __init samsung_clk_register_gate(struct samsung_gate_clock *clk_list,
>> + unsigned int nr_clk)
>> +{
>> + struct clk *clk;
>> + unsigned int idx, ret;
>> +
>> + for (idx = 0; idx < nr_clk; idx++, clk_list++) {
>> + clk = clk_register_gate(NULL, clk_list->name,
>> + clk_list->parent_name, clk_list->flags, clk_list->reg,
>> + clk_list->bit_idx, clk_list->gate_flags, &lock);
>> + if (IS_ERR(clk)) {
>> + pr_err("clock: failed to register clock %s\n",
>> + clk_list->name);
>> + continue;
>> + }
>> +
>> +#ifdef CONFIG_OF
>> + if (clk_list->np)
>> + of_clk_add_provider(clk_list->np, of_clk_src_simple_get,
>> + clk);
>> +#endif
>> +
>> + ret = clk_register_clkdev(clk, clk_list->name,
>> + clk_list->dev_name);
>> + if (ret)
>> + pr_err("%s: failed to register clock lookup for %s",
>> + __func__, clk_list->name);
>> +
>> + if (clk_list->alias) {
>> + ret = clk_register_clkdev(clk, clk_list->alias,
>> + clk_list->dev_name);
>> + if (ret)
>> + pr_err("%s: failed to register lookup %s\n",
>> + __func__, clk_list->alias);
>> + }
>> + }
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +/* register a samsung gate type clock instantiated from device tree */
>> +void __init samsung_of_clk_register_gate(struct device_node *np)
>> +{
>> + struct samsung_gate_clock clk_gate;
>> + const char *clk_name = np->name;
>> + const char *parent_name;
>> + u32 reg_info[2];
>> +
>> + of_property_read_string(np, "clock-output-names", &clk_name);
>> + parent_name = of_clk_get_parent_name(np, 0);
>> + if (of_property_read_u32_array(np, "reg-info", reg_info, 2))
>> + pr_err("%s: invalid register info in node\n", __func__);
>> +
>> + clk_gate.name = clk_name;
>> + clk_gate.parent_name = parent_name;
>> + clk_gate.reg = (void __iomem *)(reg_base + reg_info[0]);
>> + clk_gate.bit_idx = reg_info[1];
>> + clk_gate.dev_name = NULL;
>> + clk_gate.flags = 0;
>> + clk_gate.gate_flags = 0;
>
> Some clocks need CLK_SET_RATE_PARENT for the drivers to work
> as before. So far it is not set for any mux, div nor gate clock.
Ok. I will fix this.
So about the static vs device tree based clock registration, what
would you suggest?
Thanks,
Thomas.
>
>> + clk_gate.alias = NULL;
>> + clk_gate.np = np;
>> +
>> + samsung_clk_register_gate(&clk_gate, 1);
>> +}
>> +#endif
>> +
>
> --
>
> Thanks,
> Sylwester
>
^ permalink raw reply
* [PATCH 2/5] ARM: PXA: Zipit Z2: Add USB host and device support
From: Vasily Khoruzhick @ 2012-10-29 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <508E4F7F.7010607@gmail.com>
On Mon, Oct 29, 2012 at 12:42 PM, Daniel Mack <zonque@gmail.com> wrote:
> On 29.10.2012 10:33, Vasily Khoruzhick wrote:
>> On Mon, Oct 29, 2012 at 1:58 AM, Daniel Mack <zonque@gmail.com> wrote:
>>>> Any volunteers? :) It requires huge amount of work,
>>>
>>> Well, it's actually quite straight forward. With the changes that got merged
>>> to 3.7, pxa3xx platforms boot, and I also ported some pxa specific
>>> peripheral drivers that should work for both 2xx and 3xx. Adding CPU support
>>> for 27x should also just be a matter of some extra lines.
>>>
>>> So I would clearly say you should give the DT approach a try first and see
>>> which bits are missing. And I would vote for not taking any new features for
>>> the legacy board support files but just bugfixes.
>>
>> Fair enough, I'll resend series without USB stuff (so it'll contain
>> only bugfixes)
>
> I rather wanted to encourage you to work on the PXA2xx DT stuff :)
I'm not refusing to work on PXA2xx DT stuff, just want to get bugfixes in place,
some of them are serious enough (like fixing oops on poweroff)
> Please have a quick look at arch/arm/mach-pxa/pxa-dt.c - what it takes
> for basic 270 support is just copying the logic that is there already
> for the 3xx models.
Sure, but I need to get DT-capable bootloader working on Z2 in first
place, right?
> Some parts like the uart, host-mode usb, mmc, gpio-leds, gpio-keys, i2c
> etc are already finished and should work instantly. What's missing is
> the spi controller and the display, but maybe you can help getting these
> done?
What about libertas? I still don't understand how DT handles platform-specific
callbacks like set_power(). And what about pm_power_off hook?
Regards
Vasily
^ permalink raw reply
* [PATCH 0/4] DMA: PL330 fixes
From: Tomasz Figa @ 2012-10-29 10:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351504796-24788-1-git-send-email-b.zolnierkie@samsung.com>
Hi Bartlomiej,
On Monday 29 of October 2012 10:59:52 Bartlomiej Zolnierkiewicz wrote:
> Hi,
>
> The first patch fixes lockup caused by pl330 driver. It doesn't add
> any dependencies to later patches and can be applied independently.
>
> The second patch fixes PL330 regression on ARM Exynos 4210 revision 0
> SOC (used by Universal C210 board).
>
> The third patch adds PL330 MDMA1 controller node to device tree on
> ARM Exynos4 SOCs.
>
> The fourth patch adds device tree property for PL330 driver to be used
> by controllers that have DMA_MEMCPY capability and updates device tree
> files for ARM platforms using PL330. Since this patch depends on
> patches #2-3 it would be probably best to also push it through Kukjin's
> tree (with the relevant ACKs added of course).
>
>
> Bartlomiej Zolnierkiewicz (4):
> DMA: PL330: fix locking in pl330_free_chan_resources()
> ARM: EXYNOS: PL330 MDMA1 fix for revision 0 of Exynos4210 SOC
> ARM: dts: exynos4: add node for PL330 MDMA1 controller
> DMA: PL330: add device tree property for DMA_MEMCPY capability
>
> Documentation/devicetree/bindings/dma/arm-pl330.txt | 1 +
> arch/arm/boot/dts/exynos4.dtsi | 7 +++++++
> arch/arm/boot/dts/exynos5250.dtsi | 2 ++
> arch/arm/boot/dts/highbank.dts | 1 +
> arch/arm/boot/dts/socfpga.dtsi | 1 +
> arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts | 1 +
> arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 1 +
> arch/arm/mach-exynos/dma.c | 5 ++++-
> arch/arm/mach-exynos/include/mach/map.h | 3 ++-
> arch/arm/mach-exynos/mach-exynos4-dt.c | 1 +
> drivers/dma/pl330.c | 15
> ++++++++++----- 11 files changed, 31 insertions(+), 7 deletions(-)
For all four patches:
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Best regards,
--
Tomasz Figa
Samsung Poland R&D Center
SW Solution Development, Linux Platform
^ permalink raw reply
* [PATCH 2/2] MAINTAINERS: add pinctrl atmel at91 entry
From: Nicolas Ferre @ 2012-10-29 10:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351360392-8669-2-git-send-email-plagnioj@jcrosoft.com>
On 10/27/2012 07:53 PM, Jean-Christophe PLAGNIOL-VILLARD :
> Cc: linux-kernel at vger.kernel.org
> Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> ---
> MAINTAINERS | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 027ec2b..cb97031 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5624,6 +5624,12 @@ S: Maintained
> F: drivers/pinctrl/
> F: include/linux/pinctrl/
>
> +PIN CONTROLLER - ATMEL AT91
> +M: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
> +L: linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
> +S: Maintained
> +F: drivers/pinctrl/pinctrl-at91.c
> +
> PIN CONTROLLER - ST SPEAR
> M: Viresh Kumar <viresh.linux@gmail.com>
> L: spear-devel at list.st.com
>
--
Nicolas Ferre
^ permalink raw reply
* [PATCH v3 3/4] arm: mvebu: move irq controller driver to drivers/irqchip
From: Andrew Lunn @ 2012-10-29 10:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <508E4EB3.5060901@gmail.com>
On Mon, Oct 29, 2012 at 10:38:59AM +0100, Sebastian Hesselbarth wrote:
> On 10/29/2012 10:24 AM, Thomas Petazzoni wrote:
> >>using ARCH_MVEBU for enabling irq-armada-370-xp here will lead to
> >>incompatibilities with Orion SoCs when they move over to ARCH_MVEBU.
> >
> >Not really: the driver is probed depending on the DT compatible string.
> >So we can perfectly have two irqchip drivers being enabled on
> >ARCH_MVEBU.
>
> I agreed but this will always build irq-armada-370-xp into the kernel
> although I maybe want a orion-only kernel. Although multi-platform will
> be great for ARM, I doubt that every kernel will be multi-platform?
Actually, there are multiple multi-platform kernels, kirkwood is v5
where as 370/XP is v6. So a kirkwood build will not need
irq-armada-370-xp.
Andrew
^ permalink raw reply
* [PATCH 4/4] DMA: PL330: add device tree property for DMA_MEMCPY capability
From: Bartlomiej Zolnierkiewicz @ 2012-10-29 9:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351504796-24788-1-git-send-email-b.zolnierkie@samsung.com>
* Add device tree (DT) property ("pl330,dma-memcpy") for DMA_MEMCPY
capability and instead of setting this capability unconditionally
in pl330_probe() do it only when property is present.
* Set the new "pl330,dma-memcpy" device tree property for all
current pl330 driver users except pdma controllers on ARM EXYNOS
platforms (so the DT case matches non-DT one).
It fixes the issue on ARM EXYNOS platforms using DT where pdma
controller erroneously was used for DMA_MEMCPY operations instead
of mdma one (it surprisingly seems to work but at the cost of
worse performance).
Cc: Jassi Brar <jassisinghbrar@gmail.com>
Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Dinh Nguyen <dinguyen@altera.com>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
Documentation/devicetree/bindings/dma/arm-pl330.txt | 1 +
arch/arm/boot/dts/exynos4.dtsi | 1 +
arch/arm/boot/dts/exynos5250.dtsi | 2 ++
arch/arm/boot/dts/highbank.dts | 1 +
arch/arm/boot/dts/socfpga.dtsi | 1 +
arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts | 1 +
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 1 +
drivers/dma/pl330.c | 11 ++++++++---
8 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/dma/arm-pl330.txt b/Documentation/devicetree/bindings/dma/arm-pl330.txt
index 36e27d5..2661c7b 100644
--- a/Documentation/devicetree/bindings/dma/arm-pl330.txt
+++ b/Documentation/devicetree/bindings/dma/arm-pl330.txt
@@ -11,6 +11,7 @@ Required properties:
Optional properties:
- dma-coherent : Present if dma operations are coherent
+- pl330,dma-memcpy : Present if controller has DMA_MEMCPY capability
Example:
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 96d4462..ce5b03f 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -249,6 +249,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x12850000 0x1000>;
interrupts = <0 34 0>;
+ pl330,dma-memcpy;
};
};
};
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 49546bc..d659e7b 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -247,12 +247,14 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0x10800000 0x1000>;
interrupts = <0 33 0>;
+ pl330,dma-memcpy;
};
mdma1: mdma at 11C10000 {
compatible = "arm,pl330", "arm,primecell";
reg = <0x11C10000 0x1000>;
interrupts = <0 124 0>;
+ pl330,dma-memcpy;
};
};
diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts
index 0c6fc34..87f1d25 100644
--- a/arch/arm/boot/dts/highbank.dts
+++ b/arch/arm/boot/dts/highbank.dts
@@ -297,6 +297,7 @@
interrupts = <0 92 4>;
clocks = <&pclk>;
clock-names = "apb_pclk";
+ pl330,dma-memcpy;
};
ethernet at fff50000 {
diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
index 0772f57..2fe1697 100644
--- a/arch/arm/boot/dts/socfpga.dtsi
+++ b/arch/arm/boot/dts/socfpga.dtsi
@@ -71,6 +71,7 @@
compatible = "arm,pl330", "arm,primecell";
reg = <0xffe01000 0x1000>;
interrupts = <0 180 4>;
+ pl330,dma-memcpy;
};
};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
index d12b34c..d82953c 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
@@ -94,6 +94,7 @@
<0 89 4>,
<0 90 4>,
<0 91 4>;
+ pl330,dma-memcpy;
};
timer {
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
index 4890a81..b9e6ba2 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
@@ -114,6 +114,7 @@
<0 89 4>,
<0 90 4>,
<0 91 4>;
+ pl330,dma-memcpy;
};
timer {
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index db7574b..e10290b 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2860,6 +2860,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
struct pl330_info *pi;
struct dma_device *pd;
struct resource *res;
+ struct device_node *node;
int i, ret, irq;
int num_chan;
@@ -2921,12 +2922,14 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
goto probe_err4;
}
+ node = adev->dev.of_node;
+
for (i = 0; i < num_chan; i++) {
pch = &pdmac->peripherals[i];
- if (!adev->dev.of_node)
+ if (!node)
pch->chan.private = pdat ? &pdat->peri_id[i] : NULL;
else
- pch->chan.private = adev->dev.of_node;
+ pch->chan.private = node;
INIT_LIST_HEAD(&pch->work_list);
spin_lock_init(&pch->lock);
@@ -2942,7 +2945,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
if (pdat) {
pd->cap_mask = pdat->cap_mask;
} else {
- dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+ if (adev->dev.of_node &&
+ of_find_property(node, "pl330,dma-memcpy", NULL))
+ dma_cap_set(DMA_MEMCPY, pd->cap_mask);
if (pi->pcfg.num_peri) {
dma_cap_set(DMA_SLAVE, pd->cap_mask);
dma_cap_set(DMA_CYCLIC, pd->cap_mask);
--
1.8.0
^ permalink raw reply related
* [PATCH 3/4] ARM: dts: exynos4: add node for PL330 MDMA1 controller
From: Bartlomiej Zolnierkiewicz @ 2012-10-29 9:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351504796-24788-1-git-send-email-b.zolnierkie@samsung.com>
Add missing PL330 MDMA1 controller node to the device tree (DT).
[ Currently there is no problem with using 'non-secure' mdma1 address
instead of 'secure' one on revision 0 of Exynos4210 SOC (as used by
Universal C210 board) as this SOC revision is unsupported by DT. ]
Cc: Tomasz Figa <t.figa@samsung.com>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
arch/arm/boot/dts/exynos4.dtsi | 6 ++++++
arch/arm/mach-exynos/mach-exynos4-dt.c | 1 +
2 files changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index a26c3dd..96d4462 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -244,5 +244,11 @@
reg = <0x12690000 0x1000>;
interrupts = <0 36 0>;
};
+
+ mdma1: mdma at 12850000 {
+ compatible = "arm,pl330", "arm,primecell";
+ reg = <0x12850000 0x1000>;
+ interrupts = <0 34 0>;
+ };
};
};
diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c
index eadf4b5..d737968 100644
--- a/arch/arm/mach-exynos/mach-exynos4-dt.c
+++ b/arch/arm/mach-exynos/mach-exynos4-dt.c
@@ -77,6 +77,7 @@ static const struct of_dev_auxdata exynos4_auxdata_lookup[] __initconst = {
"exynos4210-spi.2", NULL),
OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_PDMA0, "dma-pl330.0", NULL),
OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_PDMA1, "dma-pl330.1", NULL),
+ OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_NS_MDMA1, "dma-pl330.2", NULL),
{},
};
--
1.8.0
^ permalink raw reply related
* [PATCH 2/4] ARM: EXYNOS: PL330 MDMA1 fix for revision 0 of Exynos4210 SOC
From: Bartlomiej Zolnierkiewicz @ 2012-10-29 9:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351504796-24788-1-git-send-email-b.zolnierkie@samsung.com>
Commit 8214513 ("ARM: EXYNOS: fix address for EXYNOS4 MDMA1")
changed EXYNOS specific setup of PL330 DMA engine to use 'non-secure'
mdma1 address instead of 'secure' one (from 0x12840000 to 0x12850000)
to fix issue with some Exynos4212 SOCs. Unfortunately it brakes
PL330 setup for revision 0 of Exynos4210 SOC (mdma1 device cannot
be found at 'non-secure' address):
[ 0.566245] dma-pl330 dma-pl330.2: PERIPH_ID 0x0, PCELL_ID 0x0 !
[ 0.566278] dma-pl330: probe of dma-pl330.2 failed with error -22
Fix it by using 'secure' mdma1 address on Exynos4210 revision 0 SOC.
Cc: Tomasz Figa <t.figa@samsung.com>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
arch/arm/mach-exynos/dma.c | 5 ++++-
arch/arm/mach-exynos/include/mach/map.h | 3 ++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-exynos/dma.c b/arch/arm/mach-exynos/dma.c
index 21d568b..dcc9f74 100644
--- a/arch/arm/mach-exynos/dma.c
+++ b/arch/arm/mach-exynos/dma.c
@@ -261,7 +261,7 @@ static struct dma_pl330_platdata exynos_mdma1_pdata = {
};
static AMBA_AHB_DEVICE(exynos_mdma1, "dma-pl330.2", 0x00041330,
- EXYNOS4_PA_MDMA1, {EXYNOS4_IRQ_MDMA1}, &exynos_mdma1_pdata);
+ EXYNOS4_PA_NS_MDMA1, {EXYNOS4_IRQ_MDMA1}, &exynos_mdma1_pdata);
static int __init exynos_dma_init(void)
{
@@ -275,6 +275,9 @@ static int __init exynos_dma_init(void)
exynos_pdma1_pdata.nr_valid_peri =
ARRAY_SIZE(exynos4210_pdma1_peri);
exynos_pdma1_pdata.peri_id = exynos4210_pdma1_peri;
+
+ if (samsung_rev() == EXYNOS4210_REV_0)
+ exynos_mdma1_device.res.start = EXYNOS4_PA_S_MDMA1;
} else if (soc_is_exynos4212() || soc_is_exynos4412()) {
exynos_pdma0_pdata.nr_valid_peri =
ARRAY_SIZE(exynos4212_pdma0_peri);
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index 8480849..0abfe78 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -89,7 +89,8 @@
#define EXYNOS4_PA_L2CC 0x10502000
#define EXYNOS4_PA_MDMA0 0x10810000
-#define EXYNOS4_PA_MDMA1 0x12850000
+#define EXYNOS4_PA_NS_MDMA1 0x12850000
+#define EXYNOS4_PA_S_MDMA1 0x12840000
#define EXYNOS4_PA_PDMA0 0x12680000
#define EXYNOS4_PA_PDMA1 0x12690000
#define EXYNOS5_PA_MDMA0 0x10800000
--
1.8.0
^ permalink raw reply related
* [PATCH 1/4] DMA: PL330: fix locking in pl330_free_chan_resources()
From: Bartlomiej Zolnierkiewicz @ 2012-10-29 9:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351504796-24788-1-git-send-email-b.zolnierkie@samsung.com>
tasklet_kill() may sleep so call it before taking pch->lock.
Fixes following lockup:
[ 345.470000] BUG: scheduling while atomic: cat/2383/0x00000002
[ 345.470000] Modules linked in:
[ 345.470000] [<c0015858>] (unwind_backtrace+0x0/0xfc) from [<c004d980>] (__schedule_bug+0x4c/0x58)
[ 345.470000] [<c004d980>] (__schedule_bug+0x4c/0x58) from [<c0360b6c>] (__schedule+0x690/0x6e0)
[ 345.470000] [<c0360b6c>] (__schedule+0x690/0x6e0) from [<c004f2b4>] (sys_sched_yield+0x70/0x78)
[ 345.470000] [<c004f2b4>] (sys_sched_yield+0x70/0x78) from [<c002acec>] (tasklet_kill+0x34/0x8c)
[ 345.470000] [<c002acec>] (tasklet_kill+0x34/0x8c) from [<c01da4cc>] (pl330_free_chan_resources+0x24/0x88)
[ 345.470000] [<c01da4cc>] (pl330_free_chan_resources+0x24/0x88) from [<c01d81f4>] (dma_chan_put+0x4c/0x50)
[ 345.470000] [<c01d81f4>] (dma_chan_put+0x4c/0x50) from [<c01d82c0>] (dma_release_channel+0x28/0x98)
[...]
[ 368.335000] BUG: spinlock lockup suspected on CPU#0, swapper/0/0
[ 368.340000] lock: 0xe52aa04c, .magic: dead4ead, .owner: cat/2383, .owner_cpu: 1
[ 368.350000] [<c0015858>] (unwind_backtrace+0x0/0xfc) from [<c01b3d78>] (do_raw_spin_lock+0x194/0x204)
[ 368.360000] [<c01b3d78>] (do_raw_spin_lock+0x194/0x204) from [<c0361adc>] (_raw_spin_lock_irqsave+0x20/0x28)
[ 368.365000] [<c0361adc>] (_raw_spin_lock_irqsave+0x20/0x28) from [<c01da80c>] (pl330_tasklet+0x2c/0x5a8)
[ 368.375000] [<c01da80c>] (pl330_tasklet+0x2c/0x5a8) from [<c002ac04>] (tasklet_action+0xfc/0x114)
[ 368.385000] [<c002ac04>] (tasklet_action+0xfc/0x114) from [<c002b204>] (__do_softirq+0xe4/0x19c)
[ 368.395000] [<c002b204>] (__do_softirq+0xe4/0x19c) from [<c002b398>] (irq_exit+0x98/0x9c)
[ 368.405000] [<c002b398>] (irq_exit+0x98/0x9c) from [<c0013ebc>] (handle_IPI+0x124/0x16c)
[ 368.410000] [<c0013ebc>] (handle_IPI+0x124/0x16c) from [<c000857c>] (gic_handle_irq+0x64/0x68)
[ 368.420000] [<c000857c>] (gic_handle_irq+0x64/0x68) from [<c000e740>] (__irq_svc+0x40/0x70)
[ 368.430000] Exception stack(0xc04a3f00 to 0xc04a3f48)
[ 368.435000] 3f00: c04a3f48 00000000 6f9e23e8 00000050 c07492c8 c04a3f48 00000000 c04ccc88
[ 368.440000] 3f20: 6f9dbac3 00000050 6f9e23e8 00000050 3b9aca00 c04a3f48 c005cfa4 c02946d4
[ 368.450000] 3f40: 60000013 ffffffff
[ 368.455000] [<c000e740>] (__irq_svc+0x40/0x70) from [<c02946d4>] (cpuidle_wrap_enter+0x4c/0xa0)
[ 368.460000] [<c02946d4>] (cpuidle_wrap_enter+0x4c/0xa0) from [<c02940dc>] (cpuidle_enter_state+0x18/0x68)
[ 368.470000] [<c02940dc>] (cpuidle_enter_state+0x18/0x68) from [<c02948e0>] (cpuidle_idle_call+0xac/0xe0)
[ 368.480000] [<c02948e0>] (cpuidle_idle_call+0xac/0xe0) from [<c00102f8>] (cpu_idle+0xac/0xf0)
[ 368.490000] [<c00102f8>] (cpu_idle+0xac/0xf0) from [<c04796a0>] (start_kernel+0x28c/0x294)
Cc: Jassi Brar <jassisinghbrar@gmail.com>
Cc: Vinod Koul <vinod.koul@linux.intel.com>
Cc: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
drivers/dma/pl330.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 665668b..db7574b 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2459,10 +2459,10 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
struct dma_pl330_chan *pch = to_pchan(chan);
unsigned long flags;
- spin_lock_irqsave(&pch->lock, flags);
-
tasklet_kill(&pch->task);
+ spin_lock_irqsave(&pch->lock, flags);
+
pl330_release_channel(pch->pl330_chid);
pch->pl330_chid = NULL;
--
1.8.0
^ permalink raw reply related
* [PATCH 0/4] DMA: PL330 fixes
From: Bartlomiej Zolnierkiewicz @ 2012-10-29 9:59 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
The first patch fixes lockup caused by pl330 driver. It doesn't add
any dependencies to later patches and can be applied independently.
The second patch fixes PL330 regression on ARM Exynos 4210 revision 0
SOC (used by Universal C210 board).
The third patch adds PL330 MDMA1 controller node to device tree on
ARM Exynos4 SOCs.
The fourth patch adds device tree property for PL330 driver to be used
by controllers that have DMA_MEMCPY capability and updates device tree
files for ARM platforms using PL330. Since this patch depends on
patches #2-3 it would be probably best to also push it through Kukjin's
tree (with the relevant ACKs added of course).
Bartlomiej Zolnierkiewicz (4):
DMA: PL330: fix locking in pl330_free_chan_resources()
ARM: EXYNOS: PL330 MDMA1 fix for revision 0 of Exynos4210 SOC
ARM: dts: exynos4: add node for PL330 MDMA1 controller
DMA: PL330: add device tree property for DMA_MEMCPY capability
Documentation/devicetree/bindings/dma/arm-pl330.txt | 1 +
arch/arm/boot/dts/exynos4.dtsi | 7 +++++++
arch/arm/boot/dts/exynos5250.dtsi | 2 ++
arch/arm/boot/dts/highbank.dts | 1 +
arch/arm/boot/dts/socfpga.dtsi | 1 +
arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts | 1 +
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 1 +
arch/arm/mach-exynos/dma.c | 5 ++++-
arch/arm/mach-exynos/include/mach/map.h | 3 ++-
arch/arm/mach-exynos/mach-exynos4-dt.c | 1 +
drivers/dma/pl330.c | 15 ++++++++++-----
11 files changed, 31 insertions(+), 7 deletions(-)
--
1.8.0
^ permalink raw reply
* [PATCH] ARM: OMAP2+: am33xx: Add PRM warm reset/reboot support
From: Vaibhav Hiremath @ 2012-10-29 9:59 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds reboot capability to all AM33XX based boards/EVM's,
by asserting PRM warm reset bit on 'reboot' command.
Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com>
Tested-by: Daniel Mack <zonque@gmail.com>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Afzal Mohammed <afzal@ti.com>
---
Tested on AM335xEVM and BeagleBone platform.
arch/arm/mach-omap2/board-generic.c | 1 +
arch/arm/mach-omap2/prcm.c | 3 +++
arch/arm/mach-omap2/prm33xx.c | 15 +++++++++++++++
arch/arm/mach-omap2/prm33xx.h | 1 +
4 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 601ecdf..6a69ceb 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -114,6 +114,7 @@ DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
.init_machine = omap_generic_init,
.timer = &omap3_am33xx_timer,
.dt_compat = am33xx_boards_compat,
+ .restart = omap_prcm_restart,
MACHINE_END
#endif
diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c
index cff270a..07f163c 100644
--- a/arch/arm/mach-omap2/prcm.c
+++ b/arch/arm/mach-omap2/prcm.c
@@ -34,6 +34,7 @@
#include "cm2xxx_3xxx.h"
#include "prm2xxx_3xxx.h"
#include "prm44xx.h"
+#include "prm33xx.h"
#include "prminst44xx.h"
#include "cminst44xx.h"
#include "prm-regbits-24xx.h"
@@ -73,6 +74,8 @@ void omap_prcm_restart(char mode, const char *cmd)
omap3_ctrl_write_boot_mode((cmd ? (u8)*cmd : 0));
} else if (cpu_is_omap44xx()) {
omap4_prminst_global_warm_sw_reset(); /* never returns */
+ } else if (soc_is_am33xx()) {
+ am33xx_prm_global_warm_sw_reset(); /* never returns */
} else {
WARN_ON(1);
}
diff --git a/arch/arm/mach-omap2/prm33xx.c b/arch/arm/mach-omap2/prm33xx.c
index 53ec9cb..657c3d0 100644
--- a/arch/arm/mach-omap2/prm33xx.c
+++ b/arch/arm/mach-omap2/prm33xx.c
@@ -135,6 +135,21 @@ int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
return (c == MAX_MODULE_HARDRESET_WAIT) ? -EBUSY : 0;
}
+void am33xx_prm_global_warm_sw_reset(void)
+{
+ u32 v;
+
+ v = am33xx_prm_read_reg(AM33XX_PRM_DEVICE_MOD,
+ AM33XX_PRM_RSTCTRL_OFFSET);
+ v |= AM33XX_GLOBAL_WARM_SW_RST_MASK;
+ am33xx_prm_write_reg(v, AM33XX_PRM_DEVICE_MOD,
+ AM33XX_PRM_RSTCTRL_OFFSET);
+
+ /* OCP barrier */
+ v = am33xx_prm_read_reg(AM33XX_PRM_DEVICE_MOD,
+ AM33XX_PRM_RSTCTRL_OFFSET);
+}
+
static int am33xx_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
{
am33xx_prm_rmw_reg_bits(OMAP_POWERSTATE_MASK,
diff --git a/arch/arm/mach-omap2/prm33xx.h b/arch/arm/mach-omap2/prm33xx.h
index 3f25c56..cdfc694 100644
--- a/arch/arm/mach-omap2/prm33xx.h
+++ b/arch/arm/mach-omap2/prm33xx.h
@@ -126,4 +126,5 @@ extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst,
extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs);
extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst,
u16 rstctrl_offs, u16 rstst_offs);
+extern void am33xx_prm_global_warm_sw_reset(void);
#endif
--
1.7.0.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox