Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Two questions about streaming DMA flushing
From: Li Haifeng @ 2012-10-31  9:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121031090821.GK21164@n2100.arm.linux.org.uk>

2012/10/31 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> On Wed, Oct 31, 2012 at 10:15:04AM +0800, Li Haifeng wrote:
>> Sorry to disturb you.
>>
>> I have two questions for streaming DMA flushing @ arch/arm/mm/cache-v7.S.
>>
>> 1.
>> 332 ENTRY(v7_dma_map_area)
>> 333         add     r1, r1, r0
>> 334         teq     r2, #DMA_FROM_DEVICE
>> 335         beq     v7_dma_inv_range
>> 336         b       v7_dma_clean_range
>> 337 ENDPROC(v7_dma_map_area)
>>
>> The function of v7_dma_map_area will invalidate corresponding cache line
>> firstly and then clean the cache for ?DMA_FROM_DEVICE?. I am confused the
>> sequence of the operations. IMO, the invalidate should be followed by the
>> clean action. Is it right?
>
> Wrong.  beq is not a function call.

Yes, It's my wrong. sorry for my careless.

>
>> 2.
>> 345 ENTRY(v7_dma_unmap_area)
>> 346         add     r1, r1, r0
>> 347         teq     r2, #DMA_TO_DEVICE
>> 348         bne     v7_dma_inv_range
>> 349         mov     pc, lr
>> 350 ENDPROC(v7_dma_unmap_area)
>>
>> v7_dma_unmap_area, will invalidate corresponding cache line for
>> ?DMA_FROM_DEVICE?. But, at v7_dma_map_area, the invalidate has been done.
>> Why do this again?
>
> Cache prefetching.

Thanks!

^ permalink raw reply

* [PATCH V4 7/7] ARM: tegra30: cpuidle: add powered-down state for CPU0
From: Joseph Lo @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351676481-28425-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>
---
V4:
* rebased on next-20121031
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 f88595a..1460c3d 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -20,13 +20,36 @@
 #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 "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)
 {
@@ -42,6 +65,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;
@@ -71,4 +177,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 91548a7..88f4de9 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 "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 bacf549..6e1b949 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 V4 6/7] ARM: tegra30: flowctrl: add cpu_suspend_exter/exit function
From: Joseph Lo @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351676481-28425-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>
---
V4:
* rebased on next-20121031
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 ffaa286..a2250dd 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 "flowctrl.h"
 #include "iomap.h"
@@ -50,6 +51,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);
@@ -59,3 +68,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 V4 5/7] ARM: tegra30: clocks: add CPU low-power function into tegra_cpu_car_ops
From: Joseph Lo @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351676481-28425-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>
---
V4:
* rebased on next-20121031
V3:
* update the commit message
V2:
* no change
---
 arch/arm/mach-tegra/tegra30_clocks.c |  108 ++++++++++++++++++++++++++++++++++
 arch/arm/mach-tegra/tegra_cpu_car.h  |   37 ++++++++++++
 2 files changed, 145 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c
index f5b453f..efc000e 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.c
+++ b/arch/arm/mach-tegra/tegra30_clocks.c
@@ -31,6 +31,8 @@
 
 #include <asm/clkdev.h>
 
+#include <mach/powergate.h>
+
 #include "clock.h"
 #include "fuse.h"
 #include "iomap.h"
@@ -309,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.
 */
@@ -2386,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 V4 4/7] ARM: tegra30: common: enable csite clock
From: Joseph Lo @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351676481-28425-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>
---
V4:
* no change
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 6c04a18..35b7dd3 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 V4 3/7] ARM: tegra30: cpuidle: add powered-down state for secondary CPUs
From: Joseph Lo @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351676481-28425-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>
---
V4:
* rebased on next-20121031
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              |   74 ++++++++++++++++++++++++++++
 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, 253 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..f88595a
--- /dev/null
+++ b/arch/arm/mach-tegra/pm.c
@@ -0,0 +1,74 @@
+/*
+ * 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 "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..c90d8e9 100644
--- a/arch/arm/mach-tegra/reset.h
+++ b/arch/arm/mach-tegra/reset.h
@@ -29,6 +29,8 @@
 
 #ifndef __ASSEMBLY__
 
+#include "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 08e9481..91548a7 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 "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 addb83f..bacf549 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 V4 2/7] ARM: tegra: cpuidle: add CPU resume function
From: Joseph Lo @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351676481-28425-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>
---
V4:
* rebased on next-20121031
* add ifdef of CONFIG_HAVE_ARM_SCU in tegra_resume, this can make the code
  re-usable for the future chips (e.g. A15)
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 |   60 +++++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-tegra/reset.c   |    6 ++++
 arch/arm/mach-tegra/sleep.h   |    1 +
 3 files changed, 67 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
index 93f0370..82dc84b 100644
--- a/arch/arm/mach-tegra/headsmp.S
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -68,6 +68,55 @@ 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
+
+#ifdef CONFIG_HAVE_ARM_SCU
+	/* enable SCU */
+	mov32	r0, TEGRA_ARM_PERIF_BASE
+	ldr	r1, [r0]
+	orr	r1, r1, #1
+	str	r1, [r0]
+#endif
+
+	b	cpu_resume
+ENDPROC(tegra_resume)
+#endif
+
 	.align L1_CACHE_SHIFT
 ENTRY(__tegra_cpu_reset_handler_start)
 
@@ -121,6 +170,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 e05da7d..3fd89ec 100644
--- a/arch/arm/mach-tegra/reset.c
+++ b/arch/arm/mach-tegra/reset.c
@@ -25,6 +25,7 @@
 #include "iomap.h"
 #include "irammap.h"
 #include "reset.h"
+#include "sleep.h"
 #include "fuse.h"
 
 #define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \
@@ -79,5 +80,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 4889b28..addb83f 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 V4 1/7] ARM: tegra: cpuidle: separate cpuidle driver for different chips
From: Joseph Lo @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351676481-28425-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>
---
V4:
* no change
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 V4 0/7] ARM: tegra30: cpuidle: add a powered-down state
From: Joseph Lo @ 2012-10-31  9:41 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:
V4:
* rebased on next-20121031
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                      |   60 ++++++
 arch/arm/mach-tegra/pm.c                           |  218 ++++++++++++++++++++
 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               |  108 ++++++++++
 arch/arm/mach-tegra/tegra_cpu_car.h                |   37 ++++
 18 files changed, 911 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 v2 3/4] ASoC: atmel-ssc-dai: register platform from DAIs
From: Nicolas Ferre @ 2012-10-31  9:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351668420-18447-3-git-send-email-voice.shen@atmel.com>

On 10/31/2012 08:26 AM, Bo Shen :
> Register platform from DAIs
> 
> Add atmel-ssc-dai device tree support
> 
> Although atmel-ssc-dai is a virtual devices, but it needs the ssc
> controller as his parent. So, when use dai based on ssc, you should
> let the dai know which ssc should be used.
> Using ssc_request to implement this, so in dts file, need to assign
> "atmel,dai-master" to dai.
> 
> Signed-off-by: Bo Shen <voice.shen@atmel.com>

Seems ok from an AT91 perspective.
I know little about audio + DT integration, so I am not judging this part.

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>


> ---
>  .../devicetree/bindings/sound/atmel-ssc-dai.txt    |   18 ++
>  arch/arm/mach-at91/board-sam9g20ek.c               |    6 -
>  sound/soc/atmel/atmel-pcm.c                        |   23 +-
>  sound/soc/atmel/atmel-pcm.h                        |    3 +
>  sound/soc/atmel/atmel_ssc_dai.c                    |  251 ++++++--------------
>  sound/soc/atmel/sam9g20_wm8731.c                   |    2 +-
>  6 files changed, 103 insertions(+), 200 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt b/Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt
> new file mode 100644
> index 0000000..5afb0e9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/atmel-ssc-dai.txt
> @@ -0,0 +1,18 @@
> +* Atmel DAI interface based on SSC controller
> +
> +THe ssc is physical device. Which can be used to connect audio codec,
> +DAC, Magnetic card reader, and etc. So, build ssc controller as a
> +library.
> +
> +The dai is a virtual device, which will build on ssc controller.
> +So, use "atmel,dai-master" to let the dai know which ssc as his master.
> +
> +Required properties:
> +  - compatible: "atmel,atmel-ssc-dai"
> +  - atmel,dai-master: this dai base on which ssc controller
> +
> +Example:
> +dai: dai {
> +	compatible = "atmel,atmel-ssc-dai";
> +	atmel,dai-master = <&ssc0>;
> +};
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 5b6a6f9..ebdbf42 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -353,11 +353,6 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = {
>          },
>  };
>  
> -static struct platform_device sam9g20ek_pcm_device = {
> -	.name	= "atmel-pcm-audio",
> -	.id = -1,
> -};
> -
>  static struct platform_device sam9g20ek_audio_device = {
>  	.name   = "at91sam9g20ek-audio",
>  	.id     = -1,
> @@ -365,7 +360,6 @@ static struct platform_device sam9g20ek_audio_device = {
>  
>  static void __init ek_add_device_audio(void)
>  {
> -	platform_device_register(&sam9g20ek_pcm_device);
>  	platform_device_register(&sam9g20ek_audio_device);
>  }
>  
> diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
> index 9b84f98..1e9cd2c 100644
> --- a/sound/soc/atmel/atmel-pcm.c
> +++ b/sound/soc/atmel/atmel-pcm.c
> @@ -473,28 +473,17 @@ static struct snd_soc_platform_driver atmel_soc_platform = {
>  	.resume		= atmel_pcm_resume,
>  };
>  
> -static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
> +int __devinit atmel_pcm_platform_register(struct device *dev)
>  {
> -	return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
> +	return snd_soc_register_platform(dev, &atmel_soc_platform);
>  }
> +EXPORT_SYMBOL(atmel_pcm_platform_register);
>  
> -static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
> +void __devexit atmel_pcm_platform_unregister(struct device *dev)
>  {
> -	snd_soc_unregister_platform(&pdev->dev);
> -	return 0;
> +	snd_soc_unregister_platform(dev);
>  }
> -
> -static struct platform_driver atmel_pcm_driver = {
> -	.driver = {
> -			.name = "atmel-pcm-audio",
> -			.owner = THIS_MODULE,
> -	},
> -
> -	.probe = atmel_soc_platform_probe,
> -	.remove = __devexit_p(atmel_soc_platform_remove),
> -};
> -
> -module_platform_driver(atmel_pcm_driver);
> +EXPORT_SYMBOL(atmel_pcm_platform_unregister);
>  
>  MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
>  MODULE_DESCRIPTION("Atmel PCM module");
> diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
> index 5e0a95e..2d1c60f 100644
> --- a/sound/soc/atmel/atmel-pcm.h
> +++ b/sound/soc/atmel/atmel-pcm.h
> @@ -80,4 +80,7 @@ struct atmel_pcm_dma_params {
>  #define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
>  #define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
>  
> +int __devexit atmel_pcm_platform_register(struct device *dev);
> +void __devexit atmel_pcm_platform_unregister(struct device *dev);
> +
>  #endif /* _ATMEL_PCM_H */
> diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
> index 354341e..5ff0774 100644
> --- a/sound/soc/atmel/atmel_ssc_dai.c
> +++ b/sound/soc/atmel/atmel_ssc_dai.c
> @@ -42,18 +42,13 @@
>  #include <sound/initval.h>
>  #include <sound/soc.h>
>  
> +#include <linux/of.h>
> +
>  #include <mach/hardware.h>
>  
>  #include "atmel-pcm.h"
>  #include "atmel_ssc_dai.h"
>  
> -
> -#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
> -#define NUM_SSC_DEVICES		1
> -#else
> -#define NUM_SSC_DEVICES		3
> -#endif
> -
>  /*
>   * SSC PDC registers required by the PCM DMA engine.
>   */
> @@ -96,63 +91,24 @@ static struct atmel_ssc_mask ssc_rx_mask = {
>  /*
>   * DMA parameters.
>   */
> -static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
> -	{{
> -	.name		= "SSC0 PCM out",
> -	.pdc		= &pdc_tx_reg,
> -	.mask		= &ssc_tx_mask,
> -	},
> +static struct atmel_pcm_dma_params ssc_dma_params[2] = {
>  	{
> -	.name		= "SSC0 PCM in",
> -	.pdc		= &pdc_rx_reg,
> -	.mask		= &ssc_rx_mask,
> -	} },
> -#if NUM_SSC_DEVICES == 3
> -	{{
> -	.name		= "SSC1 PCM out",
> +	.name		= "SSC PCM out",
>  	.pdc		= &pdc_tx_reg,
>  	.mask		= &ssc_tx_mask,
>  	},
>  	{
> -	.name		= "SSC1 PCM in",
> +	.name		= "SSC PCM in",
>  	.pdc		= &pdc_rx_reg,
>  	.mask		= &ssc_rx_mask,
> -	} },
> -	{{
> -	.name		= "SSC2 PCM out",
> -	.pdc		= &pdc_tx_reg,
> -	.mask		= &ssc_tx_mask,
>  	},
> -	{
> -	.name		= "SSC2 PCM in",
> -	.pdc		= &pdc_rx_reg,
> -	.mask		= &ssc_rx_mask,
> -	} },
> -#endif
>  };
>  
> -
> -static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
> -	{
> -	.name		= "ssc0",
> +static struct atmel_ssc_info ssc_info = {
> +	.name		= "ssc",
>  	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
>  	.dir_mask	= SSC_DIR_MASK_UNUSED,
>  	.initialized	= 0,
> -	},
> -#if NUM_SSC_DEVICES == 3
> -	{
> -	.name		= "ssc1",
> -	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
> -	.dir_mask	= SSC_DIR_MASK_UNUSED,
> -	.initialized	= 0,
> -	},
> -	{
> -	.name		= "ssc2",
> -	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
> -	.dir_mask	= SSC_DIR_MASK_UNUSED,
> -	.initialized	= 0,
> -	},
> -#endif
>  };
>  
>  
> @@ -205,7 +161,7 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
>  static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>  			     struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	int dir_mask;
>  
>  	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
> @@ -234,7 +190,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
>  static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
>  			       struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	struct atmel_pcm_dma_params *dma_params;
>  	int dir, dir_mask;
>  
> @@ -285,7 +241,7 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
>  static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
>  		unsigned int fmt)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  
>  	ssc_p->daifmt = fmt;
>  	return 0;
> @@ -297,7 +253,7 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
>  static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
>  	int div_id, int div)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  
>  	switch (div_id) {
>  	case ATMEL_SSC_CMR_DIV:
> @@ -336,8 +292,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>  	struct snd_soc_dai *dai)
>  {
>  	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
> -	int id = dai->id;
> -	struct atmel_ssc_info *ssc_p = &ssc_info[id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	struct atmel_pcm_dma_params *dma_params;
>  	int dir, channels, bits;
>  	u32 tfmr, rfmr, tcmr, rcmr;
> @@ -354,7 +309,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>  	else
>  		dir = 1;
>  
> -	dma_params = &ssc_dma_params[id][dir];
> +	dma_params = &ssc_dma_params[dir];
>  	dma_params->ssc = ssc_p->ssc;
>  	dma_params->substream = substream;
>  
> @@ -603,7 +558,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
>  static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
>  			     struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	struct atmel_pcm_dma_params *dma_params;
>  	int dir;
>  
> @@ -631,7 +586,7 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
>  	if (!cpu_dai->active)
>  		return 0;
>  
> -	ssc_p = &ssc_info[cpu_dai->id];
> +	ssc_p = &ssc_info;
>  
>  	/* Save the status register before disabling transmit and receive */
>  	ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
> @@ -660,7 +615,7 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
>  	if (!cpu_dai->active)
>  		return 0;
>  
> -	ssc_p = &ssc_info[cpu_dai->id];
> +	ssc_p = &ssc_info;
>  
>  	/* restore SSC register settings */
>  	ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
> @@ -689,31 +644,14 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
>  
>  static int atmel_ssc_probe(struct snd_soc_dai *dai)
>  {
> -	struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
> +	struct atmel_ssc_info *ssc_p = &ssc_info;
>  	int ret = 0;
>  
>  	snd_soc_dai_set_drvdata(dai, ssc_p);
>  
> -	/*
> -	 * Request SSC device
> -	 */
> -	ssc_p->ssc = ssc_request(dai->id);
> -	if (IS_ERR(ssc_p->ssc)) {
> -		printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id);
> -		ret = PTR_ERR(ssc_p->ssc);
> -	}
> -
>  	return ret;
>  }
>  
> -static int atmel_ssc_remove(struct snd_soc_dai *dai)
> -{
> -	struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai);
> -
> -	ssc_free(ssc_p->ssc);
> -	return 0;
> -}
> -
>  #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
>  
>  #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
> @@ -728,11 +666,9 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
>  	.set_clkdiv	= atmel_ssc_set_dai_clkdiv,
>  };
>  
> -static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
> -	{
> -		.name = "atmel-ssc-dai.0",
> +static struct snd_soc_dai_driver atmel_ssc_dai = {
> +		.name = "atmel-ssc-dai",
>  		.probe = atmel_ssc_probe,
> -		.remove = atmel_ssc_remove,
>  		.suspend = atmel_ssc_suspend,
>  		.resume = atmel_ssc_resume,
>  		.playback = {
> @@ -746,119 +682,82 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
>  			.rates = ATMEL_SSC_RATES,
>  			.formats = ATMEL_SSC_FORMATS,},
>  		.ops = &atmel_ssc_dai_ops,
> -	},
> -#if NUM_SSC_DEVICES == 3
> -	{
> -		.name = "atmel-ssc-dai.1",
> -		.probe = atmel_ssc_probe,
> -		.remove = atmel_ssc_remove,
> -		.suspend = atmel_ssc_suspend,
> -		.resume = atmel_ssc_resume,
> -		.playback = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.capture = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.ops = &atmel_ssc_dai_ops,
> -	},
> -	{
> -		.name = "atmel-ssc-dai.2",
> -		.probe = atmel_ssc_probe,
> -		.remove = atmel_ssc_remove,
> -		.suspend = atmel_ssc_suspend,
> -		.resume = atmel_ssc_resume,
> -		.playback = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.capture = {
> -			.channels_min = 1,
> -			.channels_max = 2,
> -			.rates = ATMEL_SSC_RATES,
> -			.formats = ATMEL_SSC_FORMATS,},
> -		.ops = &atmel_ssc_dai_ops,
> -	},
> -#endif
>  };
>  
>  static __devinit int asoc_ssc_probe(struct platform_device *pdev)
>  {
> -	BUG_ON(pdev->id < 0);
> -	BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai));
> -	return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]);
> +	struct ssc_device *ssc;
> +	int ret, id;
> +
> +	if (pdev->dev.of_node) {
> +		struct device_node *np = pdev->dev.of_node;
> +		struct device_node *dai_master_np;
> +
> +		dai_master_np = of_parse_phandle(np, "atmel,dai-master", 0);
> +		if (!dai_master_np) {
> +			dev_err(&pdev->dev, "No SSC for atmel dai");
> +			return -EINVAL;
> +		}
> +
> +		id = of_alias_get_id(dai_master_np, "ssc");
> +	} else {
> +		id = to_platform_device(pdev->dev.parent)->id;
> +	}
> +
> +	ssc = ssc_request(id);
> +	if (IS_ERR(ssc)) {
> +		dev_err(&pdev->dev, "Failed to request SSC %d\n", id);
> +		return PTR_ERR(ssc);
> +	}
> +	ssc_info.ssc = ssc;
> +	pdev->dev.parent = &(ssc->pdev->dev);
> +
> +	ret = snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
> +		goto err_unregister_dai;
> +	}
> +
> +	ret = atmel_pcm_platform_register(&pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
> +		goto err;
> +	};
> +
> +	return 0;
> +
> +err_unregister_dai:
> +	snd_soc_unregister_dai(&pdev->dev);
> +err:
> +	return ret;
>  }
>  
>  static int __devexit asoc_ssc_remove(struct platform_device *pdev)
>  {
> +	atmel_pcm_platform_unregister(&pdev->dev);
>  	snd_soc_unregister_dai(&pdev->dev);
>  	return 0;
>  }
>  
> +#ifdef CONFIG_OF
> +static const struct of_device_id atmel_ssc_dai_dt_ids[] = {
> +	{ .compatible = "atmel,atmel-ssc-dai", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, atmel_ssc_dai_dt_ids);
> +#endif
> +
>  static struct platform_driver asoc_ssc_driver = {
>  	.driver = {
> -			.name = "atmel-ssc-dai",
> -			.owner = THIS_MODULE,
> +		.name		= "atmel-ssc-dai",
> +		.owner		= THIS_MODULE,
> +		.of_match_table	= of_match_ptr(atmel_ssc_dai_dt_ids),
>  	},
>  
>  	.probe = asoc_ssc_probe,
>  	.remove = __devexit_p(asoc_ssc_remove),
>  };
>  
> -/**
> - * atmel_ssc_set_audio - Allocate the specified SSC for audio use.
> - */
> -int atmel_ssc_set_audio(int ssc_id)
> -{
> -	struct ssc_device *ssc;
> -	static struct platform_device *dma_pdev;
> -	struct platform_device *ssc_pdev;
> -	int ret;
> -
> -	if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai))
> -		return -EINVAL;
> -
> -	/* Allocate a dummy device for DMA if we don't have one already */
> -	if (!dma_pdev) {
> -		dma_pdev = platform_device_alloc("atmel-pcm-audio", -1);
> -		if (!dma_pdev)
> -			return -ENOMEM;
> -
> -		ret = platform_device_add(dma_pdev);
> -		if (ret < 0) {
> -			platform_device_put(dma_pdev);
> -			dma_pdev = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
> -	if (!ssc_pdev)
> -		return -ENOMEM;
> -
> -	/* If we can grab the SSC briefly to parent the DAI device off it */
> -	ssc = ssc_request(ssc_id);
> -	if (IS_ERR(ssc))
> -		pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n",
> -			PTR_ERR(ssc));
> -	else {
> -		ssc_pdev->dev.parent = &(ssc->pdev->dev);
> -		ssc_free(ssc);
> -	}
> -
> -	ret = platform_device_add(ssc_pdev);
> -	if (ret < 0)
> -		platform_device_put(ssc_pdev);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
> -
>  module_platform_driver(asoc_ssc_driver);
>  
>  /* Module information */
> diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
> index e5e27db..c3e1df5 100644
> --- a/sound/soc/atmel/sam9g20_wm8731.c
> +++ b/sound/soc/atmel/sam9g20_wm8731.c
> @@ -182,7 +182,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
>  	.cpu_dai_name = "atmel-ssc-dai.0",
>  	.codec_dai_name = "wm8731-hifi",
>  	.init = at91sam9g20ek_wm8731_init,
> -	.platform_name = "atmel-pcm-audio",
> +	.platform_name = "atmel-ssc-dai.0",
>  	.codec_name = "wm8731.0-001b",
>  	.ops = &at91sam9g20ek_ops,
>  };
> 


-- 
Nicolas Ferre

^ permalink raw reply

* Two questions about streaming DMA flushing
From: Li Haifeng @ 2012-10-31  9:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121031075047.GA15743@arm.com>

2012/10/31 Catalin Marinas <catalin.marinas@arm.com>:
> On Wed, Oct 31, 2012 at 02:15:04AM +0000, Li Haifeng wrote:
>> Sorry to disturb you.
>>
>> I have two questions for streaming DMA flushing @ arch/arm/mm/cache-v7.S.
>>
>> 1.
>> 332 ENTRY(v7_dma_map_area)
>> 333         add     r1, r1, r0
>> 334         teq     r2, #DMA_FROM_DEVICE
>> 335         beq     v7_dma_inv_range
>> 336         b       v7_dma_clean_range
>> 337 ENDPROC(v7_dma_map_area)
>>
>> The function of v7_dma_map_area will invalidate corresponding cache line
>> firstly and then clean the cache for  DMA_FROM_DEVICE . I am confused the
>> sequence of the operations. IMO, the invalidate should be followed by the clean
>> action. Is it right?
>
> If the direction is DMA_FROM_DEVICE, it only invalidates the cache (beq
> instruction).

Sorry for my careless.

Why doesn't need clean for DMA_FROM_DEVICE? Will the data modified
before dma mapping be lost?

>
>> 2.
>> 345 ENTRY(v7_dma_unmap_area)
>> 346         add     r1, r1, r0
>> 347         teq     r2, #DMA_TO_DEVICE
>> 348         bne     v7_dma_inv_range
>> 349         mov     pc, lr
>> 350 ENDPROC(v7_dma_unmap_area)
>>
>> v7_dma_unmap_area, will invalidate corresponding cache line for
>> DMA_FROM_DEVICE . But, at v7_dma_map_area, the invalidate has been done. Why do
>> this again?
>
> There can be speculative loads into the cache, so once the transfer has
> finished you need to invalidate the range again to avoid reading stale
> data (the first invalidate is needed to make sure there aren't any dirty
> cache lines that could be evicted).
>

It is reasonable!

> --
> Catalin

^ permalink raw reply

* [Patch v2 2/4] ARM: at91: atmel-ssc: add device tree support
From: Nicolas Ferre @ 2012-10-31  9:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351668420-18447-2-git-send-email-voice.shen@atmel.com>

On 10/31/2012 08:26 AM, Bo Shen :
> Add atmel-ssc for device tree support
> 
> Match "atmel,at91rm9200-ssc" for using pdc for data transfer
> Match "atmel,at91sam9g45-ssc" for using pdc for data transfer
> 
> Signed-off-by: Bo Shen <voice.shen@atmel.com>

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

> ---
> Change since v1:
>   change the underscore to dash in atmel-ssc binding document
> ---
>  .../devicetree/bindings/misc/atmel-ssc.txt         |   15 ++++++
>  arch/arm/boot/dts/at91sam9260.dtsi                 |    8 ++++
>  arch/arm/boot/dts/at91sam9263.dtsi                 |   16 +++++++
>  arch/arm/boot/dts/at91sam9g45.dtsi                 |   16 +++++++
>  arch/arm/boot/dts/at91sam9x5.dtsi                  |    8 ++++
>  arch/arm/mach-at91/at91rm9200.c                    |    3 ++
>  arch/arm/mach-at91/at91sam9260.c                   |    1 +
>  arch/arm/mach-at91/at91sam9261.c                   |    3 ++
>  arch/arm/mach-at91/at91sam9263.c                   |    2 +
>  arch/arm/mach-at91/at91sam9g45.c                   |    2 +
>  arch/arm/mach-at91/at91sam9rl.c                    |    2 +
>  arch/arm/mach-at91/at91sam9x5.c                    |    1 +
>  drivers/misc/atmel-ssc.c                           |   49 ++++++++++++++++++--
>  13 files changed, 123 insertions(+), 3 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/misc/atmel-ssc.txt
> 
> diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt
> new file mode 100644
> index 0000000..38e51ad
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/atmel-ssc.txt
> @@ -0,0 +1,15 @@
> +* Atmel SSC driver.
> +
> +Required properties:
> +- compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc"
> +	- atmel,at91rm9200-ssc: support pdc transfer
> +	- atmel,at91sam9g45-ssc: support dma transfer
> +- reg: Should contain SSC registers location and length
> +- interrupts: Should contain SSC interrupt
> +
> +Example:
> +ssc0: ssc at fffbc000 {
> +	compatible = "atmel,at91rm9200-ssc";
> +	reg = <0xfffbc000 0x4000>;
> +	interrupts = <14 4 5>;
> +};
> diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi
> index d410581..aaa42d8 100644
> --- a/arch/arm/boot/dts/at91sam9260.dtsi
> +++ b/arch/arm/boot/dts/at91sam9260.dtsi
> @@ -29,6 +29,7 @@
>  		tcb0 = &tcb0;
>  		tcb1 = &tcb1;
>  		i2c0 = &i2c0;
> +		ssc0 = &ssc0;
>  	};
>  	cpus {
>  		cpu at 0 {
> @@ -212,6 +213,13 @@
>  				status = "disabled";
>  			};
>  
> +			ssc0: ssc at fffbc000 {
> +				compatible = "atmel,at91rm9200-ssc";
> +				reg = <0xfffbc000 0x4000>;
> +				interrupts = <14 4 5>;
> +				status = "disable";
> +			};
> +
>  			adc0: adc at fffe0000 {
>  				compatible = "atmel,at91sam9260-adc";
>  				reg = <0xfffe0000 0x100>;
> diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi
> index 3e6e5c1..3b721ee 100644
> --- a/arch/arm/boot/dts/at91sam9263.dtsi
> +++ b/arch/arm/boot/dts/at91sam9263.dtsi
> @@ -25,6 +25,8 @@
>  		gpio4 = &pioE;
>  		tcb0 = &tcb0;
>  		i2c0 = &i2c0;
> +		ssc0 = &ssc0;
> +		ssc1 = &ssc1;
>  	};
>  	cpus {
>  		cpu at 0 {
> @@ -173,6 +175,20 @@
>  				status = "disabled";
>  			};
>  
> +			ssc0: ssc at fff98000 {
> +				compatible = "atmel,at91rm9200-ssc";
> +				reg = <0xfff98000 0x4000>;
> +				interrupts = <16 4 5>;
> +				status = "disable";
> +			};
> +
> +			ssc1: ssc at fff9c000 {
> +				compatible = "atmel,at91rm9200-ssc";
> +				reg = <0xfff9c000 0x4000>;
> +				interrupts = <17 4 5>;
> +				status = "disable";
> +			};
> +
>  			macb0: ethernet at fffbc000 {
>  				compatible = "cdns,at32ap7000-macb", "cdns,macb";
>  				reg = <0xfffbc000 0x100>;
> diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
> index 3add030..cd9af7c 100644
> --- a/arch/arm/boot/dts/at91sam9g45.dtsi
> +++ b/arch/arm/boot/dts/at91sam9g45.dtsi
> @@ -31,6 +31,8 @@
>  		tcb1 = &tcb1;
>  		i2c0 = &i2c0;
>  		i2c1 = &i2c1;
> +		ssc0 = &ssc0;
> +		ssc1 = &ssc1;
>  	};
>  	cpus {
>  		cpu at 0 {
> @@ -226,6 +228,20 @@
>  				status = "disabled";
>  			};
>  
> +			ssc0: ssc at fff9c000 {
> +				compatible = "atmel,at91sam9g45-ssc";
> +				reg = <0xfff9c000 0x4000>;
> +				interrupts = <16 4 5>;
> +				status = "disable";
> +			};
> +
> +			ssc0: ssc at fffa0000 {
> +				compatible = "atmel,at91sam9g45-ssc";
> +				reg = <0xfffa0000 0x4000>;
> +				interrupts = <17 4 5>;
> +				status = "disable";
> +			};
> +
>  			adc0: adc at fffb0000 {
>  				compatible = "atmel,at91sam9260-adc";
>  				reg = <0xfffb0000 0x100>;
> diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
> index 03fc136..69667d0 100644
> --- a/arch/arm/boot/dts/at91sam9x5.dtsi
> +++ b/arch/arm/boot/dts/at91sam9x5.dtsi
> @@ -30,6 +30,7 @@
>  		i2c0 = &i2c0;
>  		i2c1 = &i2c1;
>  		i2c2 = &i2c2;
> +		ssc0 = &ssc0;
>  	};
>  	cpus {
>  		cpu at 0 {
> @@ -87,6 +88,13 @@
>  				interrupts = <1 4 7>;
>  			};
>  
> +			ssc0: ssc at f0010000 {
> +				compatible = "atmel,at91sam9g45-ssc";
> +				reg = <0xf0010000 0x4000>;
> +				interrupts = <28 4 5>;
> +				status = "disable";
> +			};
> +
>  			tcb0: timer at f8008000 {
>  				compatible = "atmel,at91sam9x5-tcb";
>  				reg = <0xf8008000 0x100>;
> diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
> index 85d53c5..6d65feb 100644
> --- a/arch/arm/mach-at91/at91rm9200.c
> +++ b/arch/arm/mach-at91/at91rm9200.c
> @@ -187,6 +187,9 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffd0000.ssc", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffd4000.ssc", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffd8000.ssc", &ssc2_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200", &twi_clk),
>  	/* fake hclk clock */
>  	CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
> diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
> index 2c8aab0..54d4aea 100644
> --- a/arch/arm/mach-at91/at91sam9260.c
> +++ b/arch/arm/mach-at91/at91sam9260.c
> @@ -211,6 +211,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
>  	CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260", &twi_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20", &twi_clk),
>  	/* more usart lookup table for DT entries */
> diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
> index 4e8c56e..5c7a482 100644
> --- a/arch/arm/mach-at91/at91sam9261.c
> +++ b/arch/arm/mach-at91/at91sam9261.c
> @@ -177,6 +177,9 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc2_clk),
>  	CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261", &twi_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10", &twi_clk),
> diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
> index 95a5471..1f523de 100644
> --- a/arch/arm/mach-at91/at91sam9263.c
> +++ b/arch/arm/mach-at91/at91sam9263.c
> @@ -188,6 +188,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_ID("hclk", &macb_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
>  	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
>  	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
> diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
> index f4f96a6..a4282d3 100644
> --- a/arch/arm/mach-at91/at91sam9g45.c
> +++ b/arch/arm/mach-at91/at91sam9g45.c
> @@ -241,6 +241,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc0_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffa0000.ssc", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk),
> diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
> index 4110b54..b683fdc 100644
> --- a/arch/arm/mach-at91/at91sam9rl.c
> +++ b/arch/arm/mach-at91/at91sam9rl.c
> @@ -186,6 +186,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
>  	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
>  	CLKDEV_CON_ID("pioA", &pioA_clk),
> diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
> index e503538..18fbbb2 100644
> --- a/arch/arm/mach-at91/at91sam9x5.c
> +++ b/arch/arm/mach-at91/at91sam9x5.c
> @@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk),
>  	CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk),
>  	CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk),
> diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
> index f40abd8..a769719 100644
> --- a/drivers/misc/atmel-ssc.c
> +++ b/drivers/misc/atmel-ssc.c
> @@ -18,6 +18,8 @@
>  #include <linux/slab.h>
>  #include <linux/module.h>
>  
> +#include <linux/of.h>
> +
>  /* Serialize access to ssc_list and user count */
>  static DEFINE_SPINLOCK(user_lock);
>  static LIST_HEAD(ssc_list);
> @@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
>  
>  	spin_lock(&user_lock);
>  	list_for_each_entry(ssc, &ssc_list, list) {
> -		if (ssc->pdev->id == ssc_num) {
> +		if (ssc->pdev->dev.of_node) {
> +			if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
> +				== ssc_num) {
> +				ssc_valid = 1;
> +				break;
> +			}
> +		} else if (ssc->pdev->id == ssc_num) {
>  			ssc_valid = 1;
>  			break;
>  		}
> @@ -88,10 +96,41 @@ static const struct platform_device_id atmel_ssc_devtypes[] = {
>  	}
>  };
>  
> +#ifdef CONFIG_OF
> +static const struct of_device_id atmel_ssc_dt_ids[] = {
> +	{
> +		.compatible = "atmel,at91rm9200-ssc",
> +		.data = &at91rm9200_config,
> +	}, {
> +		.compatible = "atmel,at91sam9g45-ssc",
> +		.data = &at91sam9g45_config,
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids);
> +#endif
> +
> +static inline const struct atmel_ssc_platform_data * __init
> +	atmel_ssc_get_driver_data(struct platform_device *pdev)
> +{
> +	if (pdev->dev.of_node) {
> +		const struct of_device_id *match;
> +		match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node);
> +		if (match == NULL)
> +			return NULL;
> +		return match->data;
> +	}
> +
> +	return (struct atmel_ssc_platform_data *)
> +		platform_get_device_id(pdev)->driver_data;
> +}
> +
>  static int ssc_probe(struct platform_device *pdev)
>  {
>  	struct resource *regs;
>  	struct ssc_device *ssc;
> +	const struct atmel_ssc_platform_data *plat_dat;
>  
>  	ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
>  	if (!ssc) {
> @@ -100,8 +139,11 @@ static int ssc_probe(struct platform_device *pdev)
>  	}
>  
>  	ssc->pdev = pdev;
> -	ssc->pdata = (struct atmel_ssc_platform_data *)
> -			platform_get_device_id(pdev)->driver_data;
> +
> +	plat_dat = atmel_ssc_get_driver_data(pdev);
> +	if (!plat_dat)
> +		return -ENODEV;
> +	ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat;
>  
>  	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!regs) {
> @@ -160,6 +202,7 @@ static struct platform_driver ssc_driver = {
>  	.driver		= {
>  		.name		= "ssc",
>  		.owner		= THIS_MODULE,
> +		.of_match_table	= of_match_ptr(atmel_ssc_dt_ids),
>  	},
>  	.id_table	= atmel_ssc_devtypes,
>  	.probe		= ssc_probe,
> 


-- 
Nicolas Ferre

^ permalink raw reply

* [Patch v2 1/4] ARM: at91: atmel-ssc: add platform device id table
From: Nicolas Ferre @ 2012-10-31  9:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351668420-18447-1-git-send-email-voice.shen@atmel.com>

On 10/31/2012 08:26 AM, Bo Shen :
> Add platform device id to check whether the SSC controller support
> pdc or dam for data transfer
> 
> If match "at91rm9200_ssc", which support pdc for data transfer
> If match "at91sam9g45_ssc", which support dma for data transfer
> 
> Signed-off-by: Bo Shen <voice.shen@atmel.com>

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

> ---
> No change between v1 and v2
> ---
>  arch/arm/mach-at91/at91rm9200.c          |    6 +++---
>  arch/arm/mach-at91/at91rm9200_devices.c  |    6 +++---
>  arch/arm/mach-at91/at91sam9260.c         |    2 +-
>  arch/arm/mach-at91/at91sam9260_devices.c |    2 +-
>  arch/arm/mach-at91/at91sam9261.c         |    6 +++---
>  arch/arm/mach-at91/at91sam9261_devices.c |    6 +++---
>  arch/arm/mach-at91/at91sam9263.c         |    4 ++--
>  arch/arm/mach-at91/at91sam9263_devices.c |    4 ++--
>  arch/arm/mach-at91/at91sam9g45.c         |    4 ++--
>  arch/arm/mach-at91/at91sam9g45_devices.c |    4 ++--
>  arch/arm/mach-at91/at91sam9rl.c          |    4 ++--
>  arch/arm/mach-at91/at91sam9rl_devices.c  |    4 ++--
>  drivers/misc/atmel-ssc.c                 |   23 +++++++++++++++++++++++
>  include/linux/atmel-ssc.h                |    5 +++++
>  14 files changed, 54 insertions(+), 26 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
> index b4f0565..85d53c5 100644
> --- a/arch/arm/mach-at91/at91rm9200.c
> +++ b/arch/arm/mach-at91/at91rm9200.c
> @@ -184,9 +184,9 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk),
>  	CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
>  	CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200", &twi_clk),
>  	/* fake hclk clock */
>  	CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
> diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c
> index a563189..59ceea1 100644
> --- a/arch/arm/mach-at91/at91rm9200_devices.c
> +++ b/arch/arm/mach-at91/at91rm9200_devices.c
> @@ -752,7 +752,7 @@ static struct resource ssc0_resources[] = {
>  };
>  
>  static struct platform_device at91rm9200_ssc0_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 0,
>  	.dev	= {
>  		.dma_mask		= &ssc0_dmamask,
> @@ -794,7 +794,7 @@ static struct resource ssc1_resources[] = {
>  };
>  
>  static struct platform_device at91rm9200_ssc1_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 1,
>  	.dev	= {
>  		.dma_mask		= &ssc1_dmamask,
> @@ -836,7 +836,7 @@ static struct resource ssc2_resources[] = {
>  };
>  
>  static struct platform_device at91rm9200_ssc2_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 2,
>  	.dev	= {
>  		.dma_mask		= &ssc2_dmamask,
> diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
> index ad29f93..2c8aab0 100644
> --- a/arch/arm/mach-at91/at91sam9260.c
> +++ b/arch/arm/mach-at91/at91sam9260.c
> @@ -210,7 +210,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk),
>  	CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
>  	CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260", &twi_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20", &twi_clk),
>  	/* more usart lookup table for DT entries */
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 805ef95..9cfdc3f 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -742,7 +742,7 @@ static struct resource ssc_resources[] = {
>  };
>  
>  static struct platform_device at91sam9260_ssc_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 0,
>  	.dev	= {
>  		.dma_mask		= &ssc_dmamask,
> diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
> index 8d999eb..4e8c56e 100644
> --- a/arch/arm/mach-at91/at91sam9261.c
> +++ b/arch/arm/mach-at91/at91sam9261.c
> @@ -174,9 +174,9 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
>  	CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk),
>  	CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
>  	CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261", &twi_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10", &twi_clk),
> diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
> index 9752f17..299637f 100644
> --- a/arch/arm/mach-at91/at91sam9261_devices.c
> +++ b/arch/arm/mach-at91/at91sam9261_devices.c
> @@ -706,7 +706,7 @@ static struct resource ssc0_resources[] = {
>  };
>  
>  static struct platform_device at91sam9261_ssc0_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 0,
>  	.dev	= {
>  		.dma_mask		= &ssc0_dmamask,
> @@ -748,7 +748,7 @@ static struct resource ssc1_resources[] = {
>  };
>  
>  static struct platform_device at91sam9261_ssc1_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 1,
>  	.dev	= {
>  		.dma_mask		= &ssc1_dmamask,
> @@ -790,7 +790,7 @@ static struct resource ssc2_resources[] = {
>  };
>  
>  static struct platform_device at91sam9261_ssc2_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 2,
>  	.dev	= {
>  		.dma_mask		= &ssc2_dmamask,
> diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
> index 6a01d03..95a5471 100644
> --- a/arch/arm/mach-at91/at91sam9263.c
> +++ b/arch/arm/mach-at91/at91sam9263.c
> @@ -186,8 +186,8 @@ static struct clk *periph_clocks[] __initdata = {
>  static struct clk_lookup periph_clocks_lookups[] = {
>  	/* One additional fake clock for macb_hclk */
>  	CLKDEV_CON_ID("hclk", &macb_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
>  	CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
>  	CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
> diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
> index 8dde220..df89a00 100644
> --- a/arch/arm/mach-at91/at91sam9263_devices.c
> +++ b/arch/arm/mach-at91/at91sam9263_devices.c
> @@ -1199,7 +1199,7 @@ static struct resource ssc0_resources[] = {
>  };
>  
>  static struct platform_device at91sam9263_ssc0_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 0,
>  	.dev	= {
>  		.dma_mask		= &ssc0_dmamask,
> @@ -1241,7 +1241,7 @@ static struct resource ssc1_resources[] = {
>  };
>  
>  static struct platform_device at91sam9263_ssc1_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 1,
>  	.dev	= {
>  		.dma_mask		= &ssc1_dmamask,
> diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
> index 84af1b5..f4f96a6 100644
> --- a/arch/arm/mach-at91/at91sam9g45.c
> +++ b/arch/arm/mach-at91/at91sam9g45.c
> @@ -239,8 +239,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.1", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk),
> diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
> index b159607..27e3bf6 100644
> --- a/arch/arm/mach-at91/at91sam9g45_devices.c
> +++ b/arch/arm/mach-at91/at91sam9g45_devices.c
> @@ -1459,7 +1459,7 @@ static struct resource ssc0_resources[] = {
>  };
>  
>  static struct platform_device at91sam9g45_ssc0_device = {
> -	.name	= "ssc",
> +	.name	= "at91sam9g45_ssc",
>  	.id	= 0,
>  	.dev	= {
>  		.dma_mask		= &ssc0_dmamask,
> @@ -1501,7 +1501,7 @@ static struct resource ssc1_resources[] = {
>  };
>  
>  static struct platform_device at91sam9g45_ssc1_device = {
> -	.name	= "ssc",
> +	.name	= "at91sam9g45_ssc",
>  	.id	= 1,
>  	.dev	= {
>  		.dma_mask		= &ssc1_dmamask,
> diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
> index 72e9084..4110b54 100644
> --- a/arch/arm/mach-at91/at91sam9rl.c
> +++ b/arch/arm/mach-at91/at91sam9rl.c
> @@ -184,8 +184,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
>  	CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk),
>  	CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
> -	CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
> +	CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk),
>  	CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
>  	CLKDEV_CON_ID("pioA", &pioA_clk),
> diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
> index d6ca054..01220c7 100644
> --- a/arch/arm/mach-at91/at91sam9rl_devices.c
> +++ b/arch/arm/mach-at91/at91sam9rl_devices.c
> @@ -832,7 +832,7 @@ static struct resource ssc0_resources[] = {
>  };
>  
>  static struct platform_device at91sam9rl_ssc0_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 0,
>  	.dev	= {
>  		.dma_mask		= &ssc0_dmamask,
> @@ -874,7 +874,7 @@ static struct resource ssc1_resources[] = {
>  };
>  
>  static struct platform_device at91sam9rl_ssc1_device = {
> -	.name	= "ssc",
> +	.name	= "at91rm9200_ssc",
>  	.id	= 1,
>  	.dev	= {
>  		.dma_mask		= &ssc1_dmamask,
> diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
> index ac00f83..f40abd8 100644
> --- a/drivers/misc/atmel-ssc.c
> +++ b/drivers/misc/atmel-ssc.c
> @@ -68,6 +68,26 @@ void ssc_free(struct ssc_device *ssc)
>  }
>  EXPORT_SYMBOL(ssc_free);
>  
> +static struct atmel_ssc_platform_data at91rm9200_config = {
> +	.use_dma = 0,
> +};
> +
> +static struct atmel_ssc_platform_data at91sam9g45_config = {
> +	.use_dma = 1,
> +};
> +
> +static const struct platform_device_id atmel_ssc_devtypes[] = {
> +	{
> +		.name = "at91rm9200_ssc",
> +		.driver_data = (unsigned long) &at91rm9200_config,
> +	}, {
> +		.name = "at91sam9g45_ssc",
> +		.driver_data = (unsigned long) &at91sam9g45_config,
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +
>  static int ssc_probe(struct platform_device *pdev)
>  {
>  	struct resource *regs;
> @@ -80,6 +100,8 @@ static int ssc_probe(struct platform_device *pdev)
>  	}
>  
>  	ssc->pdev = pdev;
> +	ssc->pdata = (struct atmel_ssc_platform_data *)
> +			platform_get_device_id(pdev)->driver_data;
>  
>  	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!regs) {
> @@ -139,6 +161,7 @@ static struct platform_driver ssc_driver = {
>  		.name		= "ssc",
>  		.owner		= THIS_MODULE,
>  	},
> +	.id_table	= atmel_ssc_devtypes,
>  	.probe		= ssc_probe,
>  	.remove		= __devexit_p(ssc_remove),
>  };
> diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h
> index 4eb3175..1ca0e32 100644
> --- a/include/linux/atmel-ssc.h
> +++ b/include/linux/atmel-ssc.h
> @@ -5,10 +5,15 @@
>  #include <linux/list.h>
>  #include <linux/io.h>
>  
> +struct atmel_ssc_platform_data {
> +	int			use_dma;
> +};
> +
>  struct ssc_device {
>  	struct list_head	list;
>  	void __iomem		*regs;
>  	struct platform_device	*pdev;
> +	struct atmel_ssc_platform_data *pdata;
>  	struct clk		*clk;
>  	int			user;
>  	int			irq;
> 


-- 
Nicolas Ferre

^ permalink raw reply

* [PATCH RFC] i2c: omap: Fix the revision register read
From: Shubhrajyoti Datta @ 2012-10-31  9:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351673959-5918-1-git-send-email-shubhrajyoti@ti.com>

On Wed, Oct 31, 2012 at 2:29 PM, Shubhrajyoti D <shubhrajyoti@ti.com> wrote:
> The revision register on OMAP4 is a 16-bit lo and a 16-bit
> hi. Currently the driver reads only the lower 8-bits.
> Fix the same by preventing the truncating of the rev register
> for OMAP4.
>
> Also use the scheme bit ie bit-14 of the hi register to know if it
> is OMAP_I2C_IP_VERSION_2.
>
> On platforms previous to OMAP4 the offset 0x04 is IE register whose
> bit-14 reset value is 0, the code uses the same to its advantage.
>
> The dev->regs is populated after reading the rev_hi. A NULL check
> has been added in the resume handler to prevent the access before
> the setting of the regs.
>
tested on omap4sdp, omap3630 based beagle , omap3430sdp.

^ permalink raw reply

* [PATCH 2/2] ARM: dts: mxs: add oled support for the cfa-10036
From: Maxime Ripard @ 2012-10-31  9:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674774-2891-1-git-send-email-maxime.ripard@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Brian Lilly <brian@crystalfontz.com>
---
 arch/arm/boot/dts/imx28-cfa10036.dts |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index c03a577..816cae9 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -33,11 +33,30 @@
 		};
 
 		apbx at 80040000 {
+			pwm: pwm at 80064000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&pwm4_pins_a>;
+				status = "okay";
+			};
+
 			duart: serial at 80074000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&duart_pins_b>;
 				status = "okay";
 			};
+
+			i2c0: i2c at 80058000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&i2c0_pins_b>;
+				status = "okay";
+
+				ssd1307: oled at 3c {
+					compatible = "solomon,ssd1307fb-i2c";
+					reg = <0x3c>;
+					pwms = <&pwm 4 3000>;
+					reset-gpios = <&gpio2 7 0>;
+				};
+			};
 		};
 	};
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 1/2] video: Add support for the Solomon SSD1307 OLED Controller
From: Maxime Ripard @ 2012-10-31  9:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674774-2891-1-git-send-email-maxime.ripard@free-electrons.com>

This patch adds support for the Solomon SSD1307 OLED
controller found on the Crystalfontz CFA10036 board.

This controller can drive a display with a resolution up
to 128x39 and can operate over I2C or SPI.

The current driver has only been tested on the CFA-10036,
that is using this controller over I2C to driver a 96x16
OLED screen.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Brian Lilly <brian@crystalfontz.com>
---
 .../devicetree/bindings/video/ssd1307fb.txt        |   24 ++
 drivers/video/Kconfig                              |   13 +
 drivers/video/Makefile                             |    1 +
 drivers/video/ssd1307fb.c                          |  407 ++++++++++++++++++++
 4 files changed, 445 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/ssd1307fb.txt
 create mode 100644 drivers/video/ssd1307fb.c

diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
new file mode 100644
index 0000000..3d0060c
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -0,0 +1,24 @@
+* Solomon SSD1307 Framebuffer Driver
+
+Required properties:
+  - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for
+    now is i2c.
+  - reg: Should contain address of the controller on the I2C bus. Most likely
+         0x3c or 0x3d
+  - pwm: Should contain the pwm to use according to the OF device tree PWM
+         specification [0]
+  - reset-gpios: Should contain the GPIO used to reset the OLED display
+
+Optional properties:
+  - reset-active-low: Is the reset gpio is active on physical low?
+
+[0]: Documentation/devicetree/bindings/pwm/pwm.txt
+
+Examples:
+ssd1307: oled at 3c {
+        compatible = "solomon,ssd1307fb-i2c";
+        reg = <0x3c>;
+        pwms = <&pwm 4 3000>;
+        reset-gpios = <&gpio2 7>;
+        reset-active-low;
+};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d08d799..294de9c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2442,4 +2442,17 @@ config FB_SH_MOBILE_MERAM
 	  Up to 4 memory channels can be configured, allowing 4 RGB or
 	  2 YCbCr framebuffers to be configured.
 
+config FB_SSD1307
+	tristate "Solomon SSD1307 framebuffer support"
+	depends on FB && I2C
+	select FB_SYS_FOPS
+	select FB_SYS_FILLRECT
+	select FB_SYS_COPYAREA
+	select FB_SYS_IMAGEBLIT
+	select FB_DEFERRED_IO
+	select PWM
+	help
+	  This driver implements support for the Solomon SSD1307
+	  OLED controller over I2C.
+
 endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948e..768a137 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393)        += bfin_adv7393fb.o
 obj-$(CONFIG_FB_MX3)		  += mx3fb.o
 obj-$(CONFIG_FB_DA8XX)		  += da8xx-fb.o
 obj-$(CONFIG_FB_MXS)		  += mxsfb.o
+obj-$(CONFIG_FB_SSD1307)	  += ssd1307fb.o
 
 # the test framebuffer is last
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
new file mode 100644
index 0000000..ed18f19
--- /dev/null
+++ b/drivers/video/ssd1307fb.c
@@ -0,0 +1,407 @@
+/*
+ * Driver for the Solomon SSD1307 OLED controler
+ *
+ * Copyright 2012 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/uaccess.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#define SSD1307FB_WIDTH			96
+#define SSD1307FB_HEIGHT		16
+
+#define SSD1307FB_DATA			0x40
+#define SSD1307FB_COMMAND		0x80
+
+#define SSD1307FB_CONTRAST		0x81
+#define SSD1307FB_SEG_REMAP_ON		0xa1
+#define SSD1307FB_DISPLAY_OFF		0xae
+#define SSD1307FB_DISPLAY_ON		0xaf
+#define SSD1307FB_START_PAGE_ADDRESS	0xb0
+
+struct ssd1307fb_par {
+	struct i2c_client *client;
+	struct fb_info *info;
+	struct pwm_device *pwm;
+	u32 pwm_period;
+	int reset;
+};
+
+static struct fb_fix_screeninfo ssd1307fb_fix __devinitdata = {
+	.id		= "Solomon SSD1307",
+	.type		= FB_TYPE_PACKED_PIXELS,
+	.visual		= FB_VISUAL_MONO10,
+	.xpanstep	= 0,
+	.ypanstep	= 0,
+	.ywrapstep	= 0,
+	.line_length	= SSD1307FB_WIDTH / 8,
+	.accel		= FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo ssd1307fb_var __devinitdata = {
+	.xres		= SSD1307FB_WIDTH,
+	.yres		= SSD1307FB_HEIGHT,
+	.xres_virtual	= SSD1307FB_WIDTH,
+	.yres_virtual	= SSD1307FB_HEIGHT,
+	.bits_per_pixel	= 1,
+};
+
+static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8* cmd, u32 len)
+{
+	u8 *buf;
+	int ret = 0;
+
+	buf = kzalloc(len + 1, GFP_KERNEL);
+	if (!buf) {
+		dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
+		return -ENOMEM;
+	}
+
+	buf[0] = type;
+	memcpy(buf + 1, cmd, len);
+
+	ret = i2c_master_send(client, buf, len + 1);
+	if (ret != len + 1) {
+		dev_err(&client->dev, "Couldn't send I2C command.\n");
+		goto error;
+	}
+
+error:
+	kfree(buf);
+	return ret;
+}
+
+static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8* cmd, u32 len)
+{
+	return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
+}
+
+static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
+{
+	return ssd1307fb_write_cmd_array(client, &cmd, 1);
+}
+
+static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8* cmd, u32 len)
+{
+	return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
+}
+
+static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
+{
+	return ssd1307fb_write_data_array(client, &data, 1);
+}
+
+static void ssd1307fb_update_display(struct ssd1307fb_par *par)
+{
+	u8 *vmem = par->info->screen_base;
+	int i, j, k;
+
+	/*
+	 * The screen is divided in pages, each having a height of 8
+	 * pixels, and the width of the screen. When sending a byte of
+	 * data to the controller, it gives the 8 bits for the current
+	 * column. I.e, the first byte are the 8 bits of the first
+	 * column, then the 8 bits for the second column, etc.
+	 *
+	 *
+	 * Representation of the screen, assuming it is 5 bits
+	 * wide. Each letter-number combination is a bit that controls
+	 * one pixel.
+	 *
+	 * A0 A1 A2 A3 A4
+	 * B0 B1 B2 B3 B4
+	 * C0 C1 C2 C3 C4
+	 * D0 D1 D2 D3 D4
+	 * E0 E1 E2 E3 E4
+	 * F0 F1 F2 F3 F4
+	 * G0 G1 G2 G3 G4
+	 * H0 H1 H2 H3 H4
+	 *
+	 * If you want to update this screen, you need to send 5 bytes:
+	 *  (1) A0 B0 C0 D0 E0 F0 G0 H0
+	 *  (2) A1 B1 C1 D1 E1 F1 G1 H1
+	 *  (3) A2 B2 C2 D2 E2 F2 G2 H2
+	 *  (4) A3 B3 C3 D3 E3 F3 G3 H3
+	 *  (5) A4 B4 C4 D4 E4 F4 G4 H4
+	 */
+
+	for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
+		ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
+		ssd1307fb_write_cmd(par->client, 0x00);
+		ssd1307fb_write_cmd(par->client, 0x10);
+
+		for (j = 0; j < SSD1307FB_WIDTH; j++) {
+			u8 buf = 0;
+			for (k = 0; k < 8; k++) {
+				u32 page_length = SSD1307FB_WIDTH * i;
+				u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
+				u8 byte = *(vmem + index);
+				u8 bit = byte & (1 << (7 - (j % 8)));
+				bit = bit >> (7 - (j % 8));
+				buf |= bit << k;
+			}
+			ssd1307fb_write_data(par->client, buf);
+		}
+	}
+}
+
+
+static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct ssd1307fb_par *par = info->par;
+	unsigned long total_size;
+	unsigned long p = *ppos;
+	u8 __iomem *dst;
+	int err = 0;
+
+	total_size = info->screen_size;
+
+	if (total_size == 0)
+		total_size = info->fix.smem_len;
+
+	if (p > total_size)
+		return -EFBIG;
+
+	if (count > total_size) {
+		err = -EFBIG;
+		count = total_size;
+	}
+
+	if (count + p > total_size) {
+		if (!err)
+			err = -ENOSPC;
+
+		count = total_size - p;
+	}
+
+	dst = (void __force *) (info->screen_base + p);
+
+	if (copy_from_user(dst, buf, count))
+		err = -EFAULT;
+
+	if  (!err)
+		*ppos += count;
+
+	ssd1307fb_update_display(par);
+
+	return (err) ? err : count;
+}
+
+static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+	struct ssd1307fb_par *par = info->par;
+	sys_fillrect(info, rect);
+	ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 
+{
+	struct ssd1307fb_par *par = info->par;
+	sys_copyarea(info, area);
+	ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) 
+{
+	struct ssd1307fb_par *par = info->par;
+	sys_imageblit(info, image);
+	ssd1307fb_update_display(par);
+}
+
+static struct fb_ops ssd1307fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read	= fb_sys_read,
+	.fb_write	= ssd1307fb_write,
+	.fb_fillrect	= ssd1307fb_fillrect,
+	.fb_copyarea	= ssd1307fb_copyarea,
+	.fb_imageblit	= ssd1307fb_imageblit,
+};
+
+static void ssd1307fb_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+	ssd1307fb_update_display(info->par);
+}
+
+static struct fb_deferred_io ssd1307fb_defio = {
+	.delay		= HZ,
+	.deferred_io	= ssd1307fb_deferred_io,
+};
+
+static int __devinit ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct fb_info *info;
+	u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
+	struct ssd1307fb_par *par;
+	u8 *vmem;
+	int ret;
+
+	if (!client->dev.of_node) {
+		dev_err(&client->dev, "No device tree data found!\n");
+		return -EINVAL;
+	}
+
+	info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
+	if (!info) {
+		dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
+		return -ENOMEM;
+	}
+
+	vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
+	if (!vmem) {
+		dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
+		ret = -ENOMEM;
+		goto fb_alloc_error;
+	}
+
+	info->fbops = &ssd1307fb_ops;
+	info->fix = ssd1307fb_fix;
+	info->fbdefio = &ssd1307fb_defio;
+
+	info->var = ssd1307fb_var;
+	info->var.red.length = 1;
+	info->var.red.offset = 0;
+	info->var.green.length = 1;
+	info->var.green.offset = 0;
+	info->var.blue.length = 1;
+	info->var.blue.offset = 0;
+
+	info->screen_base = (u8 __force __iomem *)vmem;
+	info->fix.smem_start = (unsigned long)vmem;
+	info->fix.smem_len = vmem_size;
+
+	fb_deferred_io_init(info);
+
+	par = info->par;
+	par->info = info;
+	par->client = client;
+
+	par->reset = of_get_named_gpio(client->dev.of_node,
+					 "reset-gpios", 0);
+	if (!gpio_is_valid(par->reset)) {
+		ret = -EINVAL;
+		goto reset_oled_error;
+	}
+
+	ret = devm_gpio_request_one(&client->dev, par->reset,
+				    GPIOF_OUT_INIT_HIGH,
+				    "oled-reset");
+	if (ret) {
+		dev_err(&client->dev,
+			"failed to request gpio %d: %d\n",
+			par->reset, ret);
+		goto reset_oled_error;
+	}
+
+	par->pwm = pwm_get(&client->dev, NULL);
+	if (IS_ERR(par->pwm)) {
+		dev_err(&client->dev, "Could not get PWM from device tree!\n");
+		ret = PTR_ERR(par->pwm);
+		goto pwm_error;
+	}
+
+	par->pwm_period = pwm_get_period(par->pwm);
+
+	dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
+
+	ret = register_framebuffer(info);
+	if (ret) {
+		dev_err(&client->dev, "Couldn't register the framebuffer\n");
+		goto fbreg_error;
+	}
+
+	i2c_set_clientdata(client, info);
+
+	/* Reset the screen */
+	gpio_set_value(par->reset, 0);
+	udelay(4);
+	gpio_set_value(par->reset, 1);
+	udelay(4);
+
+	/* Enable the PWM */
+	pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+	pwm_enable(par->pwm);
+
+	/* Map column 127 of the OLED to segment 0 */
+	ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
+	if (ret < 0) {
+		dev_err(&client->dev, "Couldn't remap the screen.\n");
+		goto remap_error;
+	}
+
+	/* Turn on the display */
+	ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
+	if (ret < 0) {
+		dev_err(&client->dev, "Couldn't turn the display on.\n");
+		goto remap_error;
+	}
+
+	dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
+
+	return 0;
+
+remap_error:
+	unregister_framebuffer(info);
+	pwm_disable(par->pwm);
+fbreg_error:
+	pwm_put(par->pwm);
+pwm_error:
+reset_oled_error:
+	fb_deferred_io_cleanup(info);
+fb_alloc_error:
+	framebuffer_release(info);
+	return ret;
+}
+
+static int __devexit ssd1307fb_remove(struct i2c_client *client)
+{
+	struct fb_info *info = i2c_get_clientdata(client);
+	struct ssd1307fb_par *par = info->par;
+
+	unregister_framebuffer(info);
+	pwm_disable(par->pwm);
+	pwm_put(par->pwm);
+	fb_deferred_io_cleanup(info);
+	framebuffer_release(info);
+
+	return 0;
+}
+
+static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+	{ "ssd1307fb", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
+
+static const struct of_device_id ssd1307fb_of_match[] = {
+	{ .compatible = "solomon,ssd1307fb-i2c" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
+
+static struct i2c_driver ssd1307fb_driver = {
+	.probe = ssd1307fb_probe,
+	.remove = __devexit_p(ssd1307fb_remove),
+	.id_table = ssd1307fb_i2c_id,
+	.driver = {
+		.name = "ssd1307fb",
+		.of_match_table = of_match_ptr(ssd1307fb_of_match),
+		.owner = THIS_MODULE,
+	},
+};
+
+module_i2c_driver(ssd1307fb_driver);
+
+MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controler");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

^ permalink raw reply related

* [PATCHv6 0/2] Add support for the OLED in the CFA10036
From: Maxime Ripard @ 2012-10-31  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

Hi everyone,

This patchset adds support for the solomon SSD1307 OLED controller present
in the CFA-10036 board.

It first adds the framebuffer driver for this controller, and then the
needed bits to enable it in the cfa10036 dts.

Considering that the driver has been around since almost three months now, and
I have not received any comments on the framebuffer part, I'd very much like it
to be included in 3.8 if possible.

Thanks,
Maxime

Changes from v5:
  * Fixed bogus reset code

Maxime Ripard (2):
  video: Add support for the Solomon SSD1307 OLED Controller
  ARM: dts: mxs: add oled support for the cfa-10036

 .../devicetree/bindings/video/ssd1307fb.txt        |   24 ++
 arch/arm/boot/dts/imx28-cfa10036.dts               |   19 +
 drivers/video/Kconfig                              |   13 +
 drivers/video/Makefile                             |    1 +
 drivers/video/ssd1307fb.c                          |  407 ++++++++++++++++++++
 5 files changed, 464 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/ssd1307fb.txt
 create mode 100644 drivers/video/ssd1307fb.c

-- 
1.7.9.5

^ permalink raw reply

* Two questions about streaming DMA flushing
From: Russell King - ARM Linux @ 2012-10-31  9:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAFNq8R7Fb5Dn6pMDDNvQp=kgpY9vmdpxyhNAPUXYA0-QbfHoJg@mail.gmail.com>

On Wed, Oct 31, 2012 at 10:15:04AM +0800, Li Haifeng wrote:
> Sorry to disturb you.
> 
> I have two questions for streaming DMA flushing @ arch/arm/mm/cache-v7.S.
> 
> 1.
> 332 ENTRY(v7_dma_map_area)
> 333         add     r1, r1, r0
> 334         teq     r2, #DMA_FROM_DEVICE
> 335         beq     v7_dma_inv_range
> 336         b       v7_dma_clean_range
> 337 ENDPROC(v7_dma_map_area)
> 
> The function of v7_dma_map_area will invalidate corresponding cache line
> firstly and then clean the cache for ?DMA_FROM_DEVICE?. I am confused the
> sequence of the operations. IMO, the invalidate should be followed by the
> clean action. Is it right?

Wrong.  beq is not a function call.

> 2.
> 345 ENTRY(v7_dma_unmap_area)
> 346         add     r1, r1, r0
> 347         teq     r2, #DMA_TO_DEVICE
> 348         bne     v7_dma_inv_range
> 349         mov     pc, lr
> 350 ENDPROC(v7_dma_unmap_area)
> 
> v7_dma_unmap_area, will invalidate corresponding cache line for
> ?DMA_FROM_DEVICE?. But, at v7_dma_map_area, the invalidate has been done.
> Why do this again?

Cache prefetching.

^ permalink raw reply

* OMAP baseline test results for v3.7-rc1
From: Rafael J. Wysocki @ 2012-10-31  9:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAORVsuX_8ryT7jmPWoQ+Lb1ztmQC-E59j64Qt1hzvsEkQ7=w+g@mail.gmail.com>

On Wednesday, October 31, 2012 04:34:21 AM Jean Pihet wrote:
> Paul,
> 
> - Added Rafael for the PM QoS discussion -
> 
> On Tue, Oct 30, 2012 at 10:04 AM, Paul Walmsley <paul@pwsan.com> wrote:
> > On Tue, 30 Oct 2012, Paul Walmsley wrote:
> >
> >> Based on a very quick look, I'd say the original patch 3db11fe is broken.
> >> I don't see how it can ensure that its PM_QOS_CPU_DMA_LATENCY request is
> >> honored when CONFIG_CPU_IDLE=n. CONFIG_CPU_IDLE=n is the default for
> >> omap2plus_defconfig.
> >
> > So in fact to follow up on this, looks like one of two changes are needed:
> >
> > 1. Revert 3db11fe
> >
> > or
> >
> > 2. If CONFIG_CPU_IDLE=n, the driver needs to call disable_hlt() in place
> > of the pm_qos constraint add, and call enable_hlt() in place of the pm_qos
> > constraint remove.
> I do not think this is correct. Using disable_hlt is a too radical
> solution and will prevent the idle completely, this is not what we
> want.
> 
> Rafael, what do you think?

Well, I agree.

Thanks,
Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* [PATCH 1/3] irqchip: Move ARM GIC to drivers/irqchip
From: Russell King - ARM Linux @ 2012-10-31  9:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50906B16.8060204@gmail.com>

On Tue, Oct 30, 2012 at 07:04:38PM -0500, Rob Herring wrote:
> On 10/30/2012 05:47 PM, Russell King - ARM Linux wrote:
> > On Tue, Oct 30, 2012 at 12:21:20PM -0500, Rob Herring wrote:
> >> Looking at this some more, arm64 doesn't need most of what's in gic.h.
> >> The register defines should be moved into the .c file. The remaining
> >> function declarations either are not needed (i.e. gic_init) or should
> >> should be done like the handle_irq function pointer init. We don't want
> >> to have platform code calling gic_cascade_irq or gic_raise_softirq
> >> directly.
> > 
> > Softirqs are about the SPIs which are used for SMP IPIs and platform
> > specific wakeup of CPUs.  And platform code _needs_ to specify the
> > way IPIs are delivered on the platform.  irqchip can't do that because
> > irqchip knows nothing about SPIs (neither does genirq.)
> 
> Right. v7 is unchanged, so the question is really only about how v8 will
> do this. Hopefully, ARM is standardizing this for v8. We probably want
> the gic (or other irqchip) to setup a raise_softirq function ptr on init
> rather than having a direct call to gic_raise_softirq.

We already have that, except it's up to platforms to setup that pointer
via a function call into the SMP code - it's called set_smp_cross_call().
We could move that into the GIC code, as we don't have anyone with a GIC
which doesn't use gic_raise_softirq.

The only thing to remember is that there's non-SMP platforms with GICs.

^ permalink raw reply

* [PATCH V4 0/4] ARM: tegra: Enable SLINK controller driver
From: Laxman Dewangan @ 2012-10-31  9:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674176-20603-1-git-send-email-ldewangan@nvidia.com>

This series modify the dts file to add the slink addresses,
make AUXDATA in board dt files, enable slink4 for tegra30-cardhu and 
enable slink controller defconfig.

Changes from V1: 
- Remove changes from clock tables.
- Make the dma req dt property name to nvidia,dma-request-selector.
- change the spi max frequency prop to spi-max-frequency.

Changes from V2: 
- make node name to spi.
- get rid of iomap.h.

Changes from V3:
- Enable spi flash connected on slink4-cs1.
- Enable require defconfigs.

Laxman Dewangan (4):
  ARM: tegra: dts: add slink controller dt entry
  ARM: tegra: Add OF_DEV_AUXDATA for SLINK driver in board dt
  ARM: tegra: dts: cardhu: enable SLINK4
  ARM: tegra: config: enable spi driver for Tegra SLINK controller

 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 arch/arm/boot/dts/tegra20.dtsi                     |   41 +++++++++++++
 arch/arm/boot/dts/tegra30-cardhu.dtsi              |   10 +++
 arch/arm/boot/dts/tegra30.dtsi                     |   60 ++++++++++++++++++++
 arch/arm/configs/tegra_defconfig                   |    5 +-
 arch/arm/mach-tegra/board-dt-tegra20.c             |    8 +++
 arch/arm/mach-tegra/board-dt-tegra30.c             |   12 ++++
 7 files changed, 136 insertions(+), 1 deletions(-)

^ permalink raw reply

* [PATCH V4 4/4] ARM: tegra: config: enable spi driver for Tegra SLINK controller
From: Laxman Dewangan @ 2012-10-31  9:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674176-20603-1-git-send-email-ldewangan@nvidia.com>

Also enable the MTD, MTD_M25P80 and MTD_CHAR driver to enable
SPI flash.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
No change from V1 and V2.

Changes from V2:
- enable MTD, MTD_M25P80 and MTD_CHAR drivers.

 arch/arm/configs/tegra_defconfig |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
index fb29680..9dac09b 100644
--- a/arch/arm/configs/tegra_defconfig
+++ b/arch/arm/configs/tegra_defconfig
@@ -80,6 +80,9 @@ CONFIG_RFKILL_GPIO=y
 CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_MTD=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_M25P80=y
 CONFIG_PROC_DEVICETREE=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_AD525X_DPOT=y
@@ -117,7 +120,7 @@ CONFIG_I2C_MUX=y
 CONFIG_I2C_MUX_PINCTRL=y
 CONFIG_I2C_TEGRA=y
 CONFIG_SPI=y
-CONFIG_SPI_TEGRA=y
+CONFIG_SPI_TEGRA20_SLINK=y
 CONFIG_GPIO_PCA953X_IRQ=y
 CONFIG_GPIO_TPS6586X=y
 CONFIG_GPIO_TPS65910=y
-- 
1.7.1.1

^ permalink raw reply related

* [PATCH V4 3/4] ARM: tegra: dts: cardhu: enable SLINK4
From: Laxman Dewangan @ 2012-10-31  9:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674176-20603-1-git-send-email-ldewangan@nvidia.com>

Enable SLINK4 and connected device in Tegra30 based
platform Cardhu.
Setting maximum spi frequency to 25MHz.

Spi serial flash is connected on CS1 of SLINK4 on
cardhu platform.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
Changes from V1:
- rename the max freq prop to spi-max-frequency

Changes from V2:
- make node name to spi@

Changes from V3:
- Add winbond spi flash as cs1 for slink4.
- Add winbond company name in vendor list.

 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 arch/arm/boot/dts/tegra30-cardhu.dtsi              |   10 ++++++++++
 2 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 9de2b9f..ac2c2c4 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -51,4 +51,5 @@ ti	Texas Instruments
 via	VIA Technologies, Inc.
 wlf	Wolfson Microelectronics
 wm	Wondermedia Technologies, Inc.
+winbond Winbond Electronics corp.
 xlnx	Xilinx
diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi
index b245e6c..b4e0eda 100644
--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
+++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
@@ -275,6 +275,16 @@
 		};
 	};
 
+	spi at 7000da00 {
+		status = "okay";
+		spi-max-frequency = <25000000>;
+		spi-flash at 1 {
+			compatible = "winbond,w25q32";
+			spi-max-frequency = <20000000>;
+			reg = <1>;
+		};
+	};
+
 	ahub {
 		i2s at 70080400 {
 			status = "okay";
-- 
1.7.1.1

^ permalink raw reply related

* [PATCH V4 2/4] ARM: tegra: Add OF_DEV_AUXDATA for SLINK driver in board dt
From: Laxman Dewangan @ 2012-10-31  9:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674176-20603-1-git-send-email-ldewangan@nvidia.com>

Add OF_DEV_AUXDATA for slink driver for Tegra20 and Tegra30
board dt files.
Set the parent clock of slink controller to PLLP and configure
clock to 100MHz.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
Changes from V1:
- Revert the changes in clock table to get the driver name.

Changes from V2:
- Get rid of the iomap.h

Changes from V3:
- None

 arch/arm/mach-tegra/board-dt-tegra20.c |    8 ++++++++
 arch/arm/mach-tegra/board-dt-tegra30.c |   12 ++++++++++++
 2 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c
index 2053f74..5a19589 100644
--- a/arch/arm/mach-tegra/board-dt-tegra20.c
+++ b/arch/arm/mach-tegra/board-dt-tegra20.c
@@ -90,6 +90,10 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
 		       &tegra_ehci3_pdata),
 	OF_DEV_AUXDATA("nvidia,tegra20-apbdma", TEGRA_APB_DMA_BASE, "tegra-apbdma", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra20-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000D400, "spi_tegra.0", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000D600, "spi_tegra.1", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000D800, "spi_tegra.2", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra20-slink", 0x7000DA00, "spi_tegra.3", NULL),
 	{}
 };
 
@@ -109,6 +113,10 @@ static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
 	{ "sdmmc1",	"pll_p",	48000000,	false},
 	{ "sdmmc3",	"pll_p",	48000000,	false},
 	{ "sdmmc4",	"pll_p",	48000000,	false},
+	{ "sbc1",	"pll_p",	100000000,	false },
+	{ "sbc2",	"pll_p",	100000000,	false },
+	{ "sbc3",	"pll_p",	100000000,	false },
+	{ "sbc4",	"pll_p",	100000000,	false },
 	{ NULL,		NULL,		0,		0},
 };
 
diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c
index 9e6f79a..b26cfef 100644
--- a/arch/arm/mach-tegra/board-dt-tegra30.c
+++ b/arch/arm/mach-tegra/board-dt-tegra30.c
@@ -52,6 +52,12 @@ struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = {
 	OF_DEV_AUXDATA("nvidia,tegra30-ahub", 0x70080000, "tegra30-ahub", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra30-apbdma", 0x6000a000, "tegra-apbdma", NULL),
 	OF_DEV_AUXDATA("nvidia,tegra30-pwm", TEGRA_PWFM_BASE, "tegra-pwm", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000D400, "spi_tegra.0", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000D600, "spi_tegra.1", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000D800, "spi_tegra.2", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000DA00, "spi_tegra.3", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000DC00, "spi_tegra.4", NULL),
+	OF_DEV_AUXDATA("nvidia,tegra30-slink", 0x7000DE00, "spi_tegra.5", NULL),
 	{}
 };
 
@@ -71,6 +77,12 @@ static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
 	{ "sdmmc1",	"pll_p",	48000000,	false},
 	{ "sdmmc3",	"pll_p",	48000000,	false},
 	{ "sdmmc4",	"pll_p",	48000000,	false},
+	{ "sbc1",	"pll_p",	100000000,	false},
+	{ "sbc2",	"pll_p",	100000000,	false},
+	{ "sbc3",	"pll_p",	100000000,	false},
+	{ "sbc4",	"pll_p",	100000000,	false},
+	{ "sbc5",	"pll_p",	100000000,	false},
+	{ "sbc6",	"pll_p",	100000000,	false},
 	{ NULL,		NULL,		0,		0},
 };
 
-- 
1.7.1.1

^ permalink raw reply related

* [PATCH V4 0/4] ARM: tegra: Enable SLINK controller driver
From: Laxman Dewangan @ 2012-10-31  9:02 UTC (permalink / raw)
  To: linux-arm-kernel

This series modify the dts file to add the slink addresses,
make AUXDATA in board dt files, enable slink4 for tegra30-cardhu and 
enable slink controller defconfig.

Changes from V1: 
- Remove changes from clock tables.
- Make the dma req dt property name to nvidia,dma-request-selector.
- change the spi max frequency prop to spi-max-frequency.

Changes from V2: 
- make node name to spi.
- get rid of iomap.h.

Changes from V3:
- Enable spi flash connected on slink4-cs1.
- Enable require defconfigs.

Laxman Dewangan (4):
  ARM: tegra: dts: add slink controller dt entry
  ARM: tegra: Add OF_DEV_AUXDATA for SLINK driver in board dt
  ARM: tegra: dts: cardhu: enable SLINK4
  ARM: tegra: config: enable spi driver for Tegra SLINK controller

 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 arch/arm/boot/dts/tegra20.dtsi                     |   41 +++++++++++++
 arch/arm/boot/dts/tegra30-cardhu.dtsi              |   10 +++
 arch/arm/boot/dts/tegra30.dtsi                     |   60 ++++++++++++++++++++
 arch/arm/configs/tegra_defconfig                   |    5 +-
 arch/arm/mach-tegra/board-dt-tegra20.c             |    8 +++
 arch/arm/mach-tegra/board-dt-tegra30.c             |   12 ++++
 7 files changed, 136 insertions(+), 1 deletions(-)

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox