Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH resend 1/2] arm64: defer reloading a task's FPSIMD state to userland resume
From: Ard Biesheuvel @ 2014-01-31 10:13 UTC (permalink / raw)
  To: linux-arm-kernel

If a task gets scheduled out and back in again and nothing has touched
its FPSIMD state in the mean time, there is really no reason to reload
it from memory. Similarly, repeated calls to kernel_neon_begin() and
kernel_neon_end() will preserve and restore the FPSIMD state every time.

This patch defers the FPSIMD state restore to the last possible moment,
i.e., right before the task re-enters userland. If a task does not enter
userland at all (for any reason), the existing FPSIMD state is preserved
and may be reused by the owning task if it gets scheduled in again on the
same CPU.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/include/asm/fpsimd.h      |  3 ++
 arch/arm64/include/asm/thread_info.h |  4 +-
 arch/arm64/kernel/entry.S            |  2 +-
 arch/arm64/kernel/fpsimd.c           | 79 +++++++++++++++++++++++++++++++-----
 arch/arm64/kernel/process.c          |  3 +-
 arch/arm64/kernel/signal.c           |  3 ++
 6 files changed, 81 insertions(+), 13 deletions(-)

diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index c43b4ac13008..609bc44ceb8d 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -37,6 +37,8 @@ struct fpsimd_state {
 			u32 fpcr;
 		};
 	};
+	/* the id of the last cpu to have restored this state */
+	unsigned int last_cpu;
 };
 
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
@@ -57,6 +59,7 @@ extern void fpsimd_load_state(struct fpsimd_state *state);
 
 extern void fpsimd_thread_switch(struct task_struct *next);
 extern void fpsimd_flush_thread(void);
+extern void fpsimd_reload_fpstate(void);
 
 #endif
 
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 720e70b66ffd..4a1ca1cfb2f8 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -100,6 +100,7 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_SIGPENDING		0
 #define TIF_NEED_RESCHED	1
 #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */
+#define TIF_FOREIGN_FPSTATE	3	/* CPU's FP state is not current's */
 #define TIF_SYSCALL_TRACE	8
 #define TIF_POLLING_NRFLAG	16
 #define TIF_MEMDIE		18	/* is terminating due to OOM killer */
@@ -112,10 +113,11 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
+#define _TIF_FOREIGN_FPSTATE	(1 << TIF_FOREIGN_FPSTATE)
 #define _TIF_32BIT		(1 << TIF_32BIT)
 
 #define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
-				 _TIF_NOTIFY_RESUME)
+				 _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE)
 
 #endif /* __KERNEL__ */
 #endif /* __ASM_THREAD_INFO_H */
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 39ac630d83de..80464e2fb1a5 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -576,7 +576,7 @@ fast_work_pending:
 	str	x0, [sp, #S_X0]			// returned x0
 work_pending:
 	tbnz	x1, #TIF_NEED_RESCHED, work_resched
-	/* TIF_SIGPENDING or TIF_NOTIFY_RESUME case */
+	/* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */
 	ldr	x2, [sp, #S_PSTATE]
 	mov	x0, sp				// 'regs'
 	tst	x2, #PSR_MODE_MASK		// user mode regs?
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 4aef42a04bdc..226a495e019c 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -35,6 +35,23 @@
 #define FPEXC_IDF	(1 << 7)
 
 /*
+ * In order to reduce the number of times the fpsimd state is needlessly saved
+ * and restored, keep track here of which task's userland owns the current state
+ * of the FPSIMD register file.
+ *
+ * This percpu variable points to the fpsimd_state.last_cpu field of the task
+ * whose FPSIMD state was most recently loaded onto this cpu. The last_cpu field
+ * itself contains the id of the cpu onto which the task's FPSIMD state was
+ * loaded most recently. So, to decide whether we can skip reloading the FPSIMD
+ * state, we need to check
+ * (a) whether this task was the last one to have its FPSIMD state loaded onto
+ *     this cpu
+ * (b) whether this task may have manipulated its FPSIMD state on another cpu in
+ *     the meantime
+ */
+static DEFINE_PER_CPU(unsigned int *, fpsimd_last_task);
+
+/*
  * Trapped FP/ASIMD access.
  */
 void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs)
@@ -72,18 +89,56 @@ void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs)
 
 void fpsimd_thread_switch(struct task_struct *next)
 {
-	/* check if not kernel threads */
-	if (current->mm)
+	/*
+	 * The thread flag TIF_FOREIGN_FPSTATE conveys that the userland FPSIMD
+	 * state belonging to the current task is not present in the registers
+	 * but has (already) been saved to memory in order for the kernel to be
+	 * able to go off and use the registers for something else. Therefore,
+	 * we must not (re)save the register contents if this flag is set.
+	 */
+	if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE))
 		fpsimd_save_state(&current->thread.fpsimd_state);
-	if (next->mm)
-		fpsimd_load_state(&next->thread.fpsimd_state);
+
+	if (next->mm) {
+		/*
+		 * If we are switching to a task whose most recent userland NEON
+		 * contents are already in the registers of *this* cpu, we can
+		 * skip loading the state from memory. Otherwise, set the
+		 * TIF_FOREIGN_FPSTATE flag so the state will be loaded upon the
+		 * next entry of userland.
+		 */
+		struct fpsimd_state *st = &next->thread.fpsimd_state;
+
+		if (__get_cpu_var(fpsimd_last_task) == &st->last_cpu
+		    && st->last_cpu == smp_processor_id())
+			clear_ti_thread_flag(task_thread_info(next),
+					     TIF_FOREIGN_FPSTATE);
+		else
+			set_ti_thread_flag(task_thread_info(next),
+					   TIF_FOREIGN_FPSTATE);
+	}
 }
 
 void fpsimd_flush_thread(void)
 {
-	preempt_disable();
 	memset(&current->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
-	fpsimd_load_state(&current->thread.fpsimd_state);
+	set_thread_flag(TIF_FOREIGN_FPSTATE);
+}
+
+void fpsimd_reload_fpstate(void)
+{
+	preempt_disable();
+	if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) {
+		/*
+		 * We are entering userland and the userland context is not yet
+		 * present in the registers.
+		 */
+		struct fpsimd_state *st = &current->thread.fpsimd_state;
+
+		fpsimd_load_state(st);
+		__get_cpu_var(fpsimd_last_task) = &st->last_cpu;
+		st->last_cpu = smp_processor_id();
+	}
 	preempt_enable();
 }
 
@@ -98,16 +153,20 @@ void kernel_neon_begin(void)
 	BUG_ON(in_interrupt());
 	preempt_disable();
 
-	if (current->mm)
+	/*
+	 * Save the userland FPSIMD state if we have one and if we haven't done
+	 * so already. Clear fpsimd_last_task to indicate that there is no
+	 * longer userland context in the registers.
+	 */
+	if (current->mm && !test_and_set_thread_flag(TIF_FOREIGN_FPSTATE))
 		fpsimd_save_state(&current->thread.fpsimd_state);
+	__get_cpu_var(fpsimd_last_task) = NULL;
+
 }
 EXPORT_SYMBOL(kernel_neon_begin);
 
 void kernel_neon_end(void)
 {
-	if (current->mm)
-		fpsimd_load_state(&current->thread.fpsimd_state);
-
 	preempt_enable();
 }
 EXPORT_SYMBOL(kernel_neon_end);
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 248a15db37f2..274316df860f 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -205,7 +205,8 @@ void release_thread(struct task_struct *dead_task)
 
 int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
-	fpsimd_save_state(&current->thread.fpsimd_state);
+	if (!test_thread_flag(TIF_FOREIGN_FPSTATE))
+		fpsimd_save_state(&current->thread.fpsimd_state);
 	*dst = *src;
 	return 0;
 }
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 890a591f75dd..0a9eccf4fc0f 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -416,4 +416,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
 		clear_thread_flag(TIF_NOTIFY_RESUME);
 		tracehook_notify_resume(regs);
 	}
+
+	if (thread_flags & _TIF_FOREIGN_FPSTATE)
+		fpsimd_reload_fpstate();
 }
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH resend 2/2] arm64: add support for kernel mode NEON in interrupt context
From: Ard Biesheuvel @ 2014-01-31 10:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391163196-27619-1-git-send-email-ard.biesheuvel@linaro.org>

This patch modifies kernel_neon_begin() and kernel_neon_end(), so
they may be called from any context. To address the case where only
a couple of registers are needed, kernel_neon_begin_partial(u32) is
introduced which takes as a parameter the number of bottom 'n' NEON
q-registers required. To mark the end of such a partial section, the
regular kernel_neon_end() should be used.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 arch/arm64/include/asm/fpsimd.h       | 17 ++++++++++++++
 arch/arm64/include/asm/fpsimdmacros.h | 37 +++++++++++++++++++++++++++++
 arch/arm64/include/asm/neon.h         |  6 ++++-
 arch/arm64/kernel/entry-fpsimd.S      | 24 +++++++++++++++++++
 arch/arm64/kernel/fpsimd.c            | 44 +++++++++++++++++++++++------------
 5 files changed, 112 insertions(+), 16 deletions(-)

diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index 609bc44ceb8d..dc9ef741c648 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -41,6 +41,19 @@ struct fpsimd_state {
 	unsigned int last_cpu;
 };
 
+/*
+ * Struct for stacking the bottom 'n' FP/SIMD registers.
+ * Mainly intended for kernel use of v8 Crypto Extensions which only
+ * needs a few registers and may need to execute in atomic context.
+ */
+struct fpsimd_partial_state {
+	u32		num_regs;
+	u32		fpsr;
+	u32		fpcr;
+	__uint128_t	vregs[32] __aligned(16);
+} __aligned(16);
+
+
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /* Masks for extracting the FPSR and FPCR from the FPSCR */
 #define VFP_FPSCR_STAT_MASK	0xf800009f
@@ -57,6 +70,10 @@ struct task_struct;
 extern void fpsimd_save_state(struct fpsimd_state *state);
 extern void fpsimd_load_state(struct fpsimd_state *state);
 
+extern void fpsimd_save_partial_state(struct fpsimd_partial_state *state,
+				      u32 num_regs);
+extern void fpsimd_load_partial_state(struct fpsimd_partial_state *state);
+
 extern void fpsimd_thread_switch(struct task_struct *next);
 extern void fpsimd_flush_thread(void);
 extern void fpsimd_reload_fpstate(void);
diff --git a/arch/arm64/include/asm/fpsimdmacros.h b/arch/arm64/include/asm/fpsimdmacros.h
index bbec599c96bd..42990a82c671 100644
--- a/arch/arm64/include/asm/fpsimdmacros.h
+++ b/arch/arm64/include/asm/fpsimdmacros.h
@@ -62,3 +62,40 @@
 	ldr	w\tmpnr, [\state, #16 * 2 + 4]
 	msr	fpcr, x\tmpnr
 .endm
+
+.altmacro
+.macro	q2op, op, q1, q2, state
+	\op	q\q1, q\q2, [\state, # -16 * \q1 - 16]
+.endm
+
+.macro fpsimd_save_partial state, numnr, tmpnr1, tmpnr2
+	mrs	x\tmpnr1, fpsr
+	str	w\numnr, [\state]
+	mrs	x\tmpnr2, fpcr
+	stp	w\tmpnr1, w\tmpnr2, [\state, #4]
+	adr	x\tmpnr1, 0f
+	add	\state, \state, x\numnr, lsl #4
+	sub	x\tmpnr1, x\tmpnr1, x\numnr, lsl #1
+	br	x\tmpnr1
+	.irp	qa, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0
+		qb = \qa + 1
+	q2op	stp, \qa, %qb, \state
+	.endr
+0:
+.endm
+
+.macro fpsimd_restore_partial state, tmpnr1, tmpnr2
+	ldp	w\tmpnr1, w\tmpnr2, [\state, #4]
+	msr	fpsr, x\tmpnr1
+	msr	fpcr, x\tmpnr2
+	adr	x\tmpnr1, 0f
+	ldr	w\tmpnr2, [\state]
+	add	\state, \state, x\tmpnr2, lsl #4
+	sub	x\tmpnr1, x\tmpnr1, x\tmpnr2, lsl #1
+	br	x\tmpnr1
+	.irp	qa, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0
+		qb = \qa + 1
+	q2op	ldp, \qa, %qb, \state
+	.endr
+0:
+.endm
diff --git a/arch/arm64/include/asm/neon.h b/arch/arm64/include/asm/neon.h
index b0cc58a97780..13ce4cc18e26 100644
--- a/arch/arm64/include/asm/neon.h
+++ b/arch/arm64/include/asm/neon.h
@@ -8,7 +8,11 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/types.h>
+
 #define cpu_has_neon()		(1)
 
-void kernel_neon_begin(void);
+#define kernel_neon_begin()	kernel_neon_begin_partial(32)
+
+void kernel_neon_begin_partial(u32 num_regs);
 void kernel_neon_end(void);
diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S
index 6a27cd6dbfa6..d358ccacfc00 100644
--- a/arch/arm64/kernel/entry-fpsimd.S
+++ b/arch/arm64/kernel/entry-fpsimd.S
@@ -41,3 +41,27 @@ ENTRY(fpsimd_load_state)
 	fpsimd_restore x0, 8
 	ret
 ENDPROC(fpsimd_load_state)
+
+#ifdef CONFIG_KERNEL_MODE_NEON
+
+/*
+ * Save the bottom n FP registers.
+ *
+ * x0 - pointer to struct fpsimd_partial_state
+ */
+ENTRY(fpsimd_save_partial_state)
+	fpsimd_save_partial x0, 1, 8, 9
+	ret
+ENDPROC(fpsimd_load_partial_state)
+
+/*
+ * Load the bottom n FP registers.
+ *
+ * x0 - pointer to struct fpsimd_partial_state
+ */
+ENTRY(fpsimd_load_partial_state)
+	fpsimd_restore_partial x0, 8, 9
+	ret
+ENDPROC(fpsimd_load_partial_state)
+
+#endif
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 226a495e019c..970c2fa86530 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -144,30 +144,44 @@ void fpsimd_reload_fpstate(void)
 
 #ifdef CONFIG_KERNEL_MODE_NEON
 
+static DEFINE_PER_CPU(struct fpsimd_partial_state, hardirq_fpsimdstate);
+static DEFINE_PER_CPU(struct fpsimd_partial_state, softirq_fpsimdstate);
+
 /*
  * Kernel-side NEON support functions
  */
-void kernel_neon_begin(void)
+void kernel_neon_begin_partial(u32 num_regs)
 {
-	/* Avoid using the NEON in interrupt context */
-	BUG_ON(in_interrupt());
-	preempt_disable();
-
-	/*
-	 * Save the userland FPSIMD state if we have one and if we haven't done
-	 * so already. Clear fpsimd_last_task to indicate that there is no
-	 * longer userland context in the registers.
-	 */
-	if (current->mm && !test_and_set_thread_flag(TIF_FOREIGN_FPSTATE))
-		fpsimd_save_state(&current->thread.fpsimd_state);
-	__get_cpu_var(fpsimd_last_task) = NULL;
+	if (in_interrupt()) {
+		struct fpsimd_partial_state *s = this_cpu_ptr(
+			in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate);
 
+		BUG_ON(num_regs > 32);
+		fpsimd_save_partial_state(s, roundup(num_regs, 2));
+	} else {
+		/*
+		 * Save the userland FPSIMD state if we have one and if we
+		 * haven't done so already. Clear fpsimd_last_task to indicate
+		 * that there is no longer userland context in the registers.
+		 */
+		preempt_disable();
+		if (current->mm &&
+		    !test_and_set_thread_flag(TIF_FOREIGN_FPSTATE))
+			fpsimd_save_state(&current->thread.fpsimd_state);
+		__get_cpu_var(fpsimd_last_task) = NULL;
+	}
 }
-EXPORT_SYMBOL(kernel_neon_begin);
+EXPORT_SYMBOL(kernel_neon_begin_partial);
 
 void kernel_neon_end(void)
 {
-	preempt_enable();
+	if (in_interrupt()) {
+		struct fpsimd_partial_state *s = this_cpu_ptr(
+			in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate);
+		fpsimd_load_partial_state(s);
+	} else {
+		preempt_enable();
+	}
 }
 EXPORT_SYMBOL(kernel_neon_end);
 
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH v2 09/21] pinctrl: mvebu: dove: provide generic mpp callbacks
From: Linus Walleij @ 2014-01-31 10:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390869573-27624-10-git-send-email-sebastian.hesselbarth@gmail.com>

On Tue, Jan 28, 2014 at 1:39 AM, Sebastian Hesselbarth
<sebastian.hesselbarth@gmail.com> wrote:

> We want to get rid of passing register addresses to common pinctrl
> driver, so provide set/get callbacks for generic mpp pins.
>
> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>

OK...

> +static int dove_mpp_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
> +                            unsigned long *config)
> +{


(...)

This is fine.

> @@ -374,7 +398,7 @@ static struct mvebu_mpp_ctrl dove_mpp_controls[] = {
>         MPP_FUNC_CTRL(13, 13, "mpp13", dove_pmu_mpp_ctrl),
>         MPP_FUNC_CTRL(14, 14, "mpp14", dove_pmu_mpp_ctrl),
>         MPP_FUNC_CTRL(15, 15, "mpp15", dove_pmu_mpp_ctrl),
> -       MPP_REG_CTRL(16, 23),
> +       MPP_FUNC_CTRL(16, 23, NULL, dove_mpp_ctrl),

What does this have to do with the rest of the patch?

(Maybe I'm stupid, beware I'm just glancing over this stuff as I
see the patch set is still in development.)

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH v2 00/21] pinctrl: mvebu: restructure and remove hardcoded addresses from Dove pinctrl
From: Linus Walleij @ 2014-01-31 10:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390869573-27624-1-git-send-email-sebastian.hesselbarth@gmail.com>

On Tue, Jan 28, 2014 at 1:39 AM, Sebastian Hesselbarth
<sebastian.hesselbarth@gmail.com> wrote:

> This patch set is one required step for Dove to hop into mach-mvebu.
> Until now, pinctrl-dove was hardcoding some registers that do not
> directly belong to MPP core registers. This is not compatible with
> what we want for mach-mvebu.

All this stuff is really nice, so if you can get Thomas to add his
review-tag on this stuff and maybe have some Tested-by:s
from other parties, I'd be happy to pull the end result to the
pinctrl tree, or alternatively provide my ACK for you to take this
through ARM SoC.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH v2 09/21] pinctrl: mvebu: dove: provide generic mpp callbacks
From: Sebastian Hesselbarth @ 2014-01-31 10:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdYpGm-KAFqU5cpzbhrM1NaZJYtQa86wP7qJFrai2bDYbw@mail.gmail.com>

On 01/31/14 11:13, Linus Walleij wrote:
> On Tue, Jan 28, 2014 at 1:39 AM, Sebastian Hesselbarth
> <sebastian.hesselbarth@gmail.com> wrote:
>
>> We want to get rid of passing register addresses to common pinctrl
>> driver, so provide set/get callbacks for generic mpp pins.
>>
>> Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
[...]
>> @@ -374,7 +398,7 @@ static struct mvebu_mpp_ctrl dove_mpp_controls[] = {
>>          MPP_FUNC_CTRL(13, 13, "mpp13", dove_pmu_mpp_ctrl),
>>          MPP_FUNC_CTRL(14, 14, "mpp14", dove_pmu_mpp_ctrl),
>>          MPP_FUNC_CTRL(15, 15, "mpp15", dove_pmu_mpp_ctrl),
>> -       MPP_REG_CTRL(16, 23),
>> +       MPP_FUNC_CTRL(16, 23, NULL, dove_mpp_ctrl),
>
> What does this have to do with the rest of the patch?
>
> (Maybe I'm stupid, beware I'm just glancing over this stuff as I
> see the patch set is still in development.)

Not really, the driver is slightly misdesigned and this patches
try to clean this up a bit. I have prepared a v3 branch for the
MVEBU guys to have a look at first.

Short version: Thanks for trying to review this, but I guess your time
would be better spent on a soon to be posted v3.

Long version:
Not having a callback identifies each control as "generic mpp pins",
so by passing a callback we make them "SoC specific" as a preparation
to remove the common "generic mpp" set/get function.

Sebastian

^ permalink raw reply

* [PATCH v2 00/21] pinctrl: mvebu: restructure and remove hardcoded addresses from Dove pinctrl
From: Sebastian Hesselbarth @ 2014-01-31 10:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkda5HRxpLWWizT_58SJ6Fb=TQpO=Y5yFThpZkUbUYmU8fg@mail.gmail.com>

On 01/31/14 11:17, Linus Walleij wrote:
> On Tue, Jan 28, 2014 at 1:39 AM, Sebastian Hesselbarth
> <sebastian.hesselbarth@gmail.com> wrote:
>
>> This patch set is one required step for Dove to hop into mach-mvebu.
>> Until now, pinctrl-dove was hardcoding some registers that do not
>> directly belong to MPP core registers. This is not compatible with
>> what we want for mach-mvebu.
>
> All this stuff is really nice, so if you can get Thomas to add his
> review-tag on this stuff and maybe have some Tested-by:s
> from other parties, I'd be happy to pull the end result to the
> pinctrl tree, or alternatively provide my ACK for you to take this
> through ARM SoC.

Luckily, we have a "pinctrl/mvebu" subfolder these patches will mess
with. I think it will be better to keep then all together and take
them through the MVEBU branch. This way we can have an eye on it and 
base off subsequent patches without bothering you.

I'll come back to request your ACK as soon as we have sorted it out.

Sebastian

^ permalink raw reply

* [PATCH 0/3] spi: core: Introduce devm_spi_alloc_master
From: Maxime Ripard @ 2014-01-31 10:23 UTC (permalink / raw)
  To: linux-arm-kernel

Hi!

This patchset introduces a devm_spi_alloc_master to the spi core. While most of
the drivers have a spi_master_put call in the probe, a lot of them using the
devm_spi_register_master function are missing it in the remove function,
leading to leaked resources.

Hence, we introduced a devm_spi_alloc_master, and converted the users of
devm_spi_register_master to use it.

Maxime Ripard (3):
  spi: core: Add devm_spi_alloc_master
  spi: core: Update the devm_spi_register_master documentation
  spi: switch to devm_spi_alloc_master

 drivers/spi/spi-atmel.c          |  8 +++-----
 drivers/spi/spi-bcm2835.c        | 15 +++++----------
 drivers/spi/spi-bcm63xx-hsspi.c  |  8 +++-----
 drivers/spi/spi-bcm63xx.c        | 16 ++++++----------
 drivers/spi/spi-bfin-v3.c        | 13 ++++---------
 drivers/spi/spi-clps711x.c       | 37 +++++++++++++++----------------------
 drivers/spi/spi-coldfire-qspi.c  | 15 +++++----------
 drivers/spi/spi-dw.c             |  6 ++----
 drivers/spi/spi-ep93xx.c         | 15 +++++----------
 drivers/spi/spi-falcon.c         |  5 ++---
 drivers/spi/spi-mpc512x-psc.c    | 19 ++++++++-----------
 drivers/spi/spi-mxs.c            |  9 +++------
 drivers/spi/spi-octeon.c         | 12 ++++--------
 drivers/spi/spi-omap-100k.c      | 16 +++++-----------
 drivers/spi/spi-omap2-mcspi.c    | 18 ++++++------------
 drivers/spi/spi-orion.c          | 10 +++-------
 drivers/spi/spi-pl022.c          |  3 +--
 drivers/spi/spi-pxa2xx.c         |  3 +--
 drivers/spi/spi-rspi.c           | 13 ++++---------
 drivers/spi/spi-s3c64xx.c        | 23 ++++++++---------------
 drivers/spi/spi-sc18is602.c      | 12 ++----------
 drivers/spi/spi-sh-hspi.c        |  7 ++-----
 drivers/spi/spi-tegra114.c       | 15 +++++----------
 drivers/spi/spi-tegra20-sflash.c | 12 ++++--------
 drivers/spi/spi-tegra20-slink.c  | 15 +++++----------
 drivers/spi/spi-ti-qspi.c        | 32 +++++++++-----------------------
 drivers/spi/spi-txx9.c           |  3 +--
 drivers/spi/spi-xcomm.c          |  8 ++------
 drivers/spi/spi.c                | 38 +++++++++++++++++++++++++++++++++++++-
 include/linux/spi/spi.h          |  2 ++
 30 files changed, 162 insertions(+), 246 deletions(-)

-- 
1.8.4.2

^ permalink raw reply

* [PATCH 1/3] spi: core: Add devm_spi_alloc_master
From: Maxime Ripard @ 2014-01-31 10:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391163792-21819-1-git-send-email-maxime.ripard@free-electrons.com>

Using devm_spi_register_master leads to a memory leak on the spi_master
structure.

spi_alloc_master uses kzalloc to allocate the spi_master but the introduction
of devm_spi_register_master removed all the matching calls to spi_master_put,
leaking the spi_master structure.

Add a devm_spi_alloc_master to provide the intended behaviour.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/spi/spi.c       | 36 ++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |  2 ++
 2 files changed, 38 insertions(+)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 63613a9..eb728ec 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1266,6 +1266,42 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
 }
 EXPORT_SYMBOL_GPL(spi_alloc_master);
 
+static void devm_spi_put(struct device *dev, void *res)
+{
+	spi_master_put(*(struct spi_master **)res);
+}
+
+/**
+ * devm_spi_alloc_master - allocate SPI master controller
+ * @dev: the controller, possibly using the platform_bus
+ * @size: how much zeroed driver-private data to allocate; the pointer to this
+ *	memory is in the driver_data field of the returned device,
+ *	accessible with spi_master_get_devdata().
+ * Context: can sleep
+ *
+ * Allocates a master controller as with spi_alloc_master() which will
+ * automatically be freed
+ */
+struct spi_master *devm_spi_alloc_master(struct device *dev, unsigned size)
+{
+	struct spi_master **ptr, *master;
+
+	ptr = devres_alloc(devm_spi_put, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return NULL;
+
+	master = spi_alloc_master(dev, size);
+	if (master) {
+		*ptr = master;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return master;
+}
+EXPORT_SYMBOL_GPL(devm_spi_alloc_master);
+
 #ifdef CONFIG_OF
 static int of_spi_register_master(struct spi_master *master)
 {
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index a1d4ca2..6fbdb2b 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -462,6 +462,8 @@ extern void spi_finalize_current_transfer(struct spi_master *master);
 /* the spi driver core manages memory for the spi_master classdev */
 extern struct spi_master *
 spi_alloc_master(struct device *host, unsigned size);
+extern struct spi_master *
+devm_spi_alloc_master(struct device *host, unsigned size);
 
 extern int spi_register_master(struct spi_master *master);
 extern int devm_spi_register_master(struct device *dev,
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH 2/3] spi: core: Update the devm_spi_register_master documentation
From: Maxime Ripard @ 2014-01-31 10:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391163792-21819-1-git-send-email-maxime.ripard@free-electrons.com>

If the devm_spi_register_master function is used together with the
spi_alloc_master as advertised in the documentation, it will either lead to a
memory leak if spi_put_master is removed, or we will try to access an already
freed memory area during the unregistration function.

Advertise that you want to use the devm_spi_alloc_master function in such case.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/spi/spi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index eb728ec..dc577b7 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1444,7 +1444,7 @@ static void devm_spi_unregister(struct device *dev, void *res)
 /**
  * dev_spi_register_master - register managed SPI master controller
  * @dev:    device managing SPI master
- * @master: initialized master, originally from spi_alloc_master()
+ * @master: initialized master, originally from devm_spi_alloc_master()
  * Context: can sleep
  *
  * Register a SPI device as with spi_register_master() which will
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH 3/3] spi: switch to devm_spi_alloc_master
From: Maxime Ripard @ 2014-01-31 10:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391163792-21819-1-git-send-email-maxime.ripard@free-electrons.com>

Make the existing users of devm_spi_register_master use the
devm_spi_alloc_master function to avoid leaking memory.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/spi/spi-atmel.c          |  8 +++-----
 drivers/spi/spi-bcm2835.c        | 15 +++++----------
 drivers/spi/spi-bcm63xx-hsspi.c  |  8 +++-----
 drivers/spi/spi-bcm63xx.c        | 16 ++++++----------
 drivers/spi/spi-bfin-v3.c        | 13 ++++---------
 drivers/spi/spi-clps711x.c       | 37 +++++++++++++++----------------------
 drivers/spi/spi-coldfire-qspi.c  | 15 +++++----------
 drivers/spi/spi-dw.c             |  6 ++----
 drivers/spi/spi-ep93xx.c         | 15 +++++----------
 drivers/spi/spi-falcon.c         |  5 ++---
 drivers/spi/spi-mpc512x-psc.c    | 19 ++++++++-----------
 drivers/spi/spi-mxs.c            |  9 +++------
 drivers/spi/spi-octeon.c         | 12 ++++--------
 drivers/spi/spi-omap-100k.c      | 16 +++++-----------
 drivers/spi/spi-omap2-mcspi.c    | 18 ++++++------------
 drivers/spi/spi-orion.c          | 10 +++-------
 drivers/spi/spi-pl022.c          |  3 +--
 drivers/spi/spi-pxa2xx.c         |  3 +--
 drivers/spi/spi-rspi.c           | 13 ++++---------
 drivers/spi/spi-s3c64xx.c        | 23 ++++++++---------------
 drivers/spi/spi-sc18is602.c      | 12 ++----------
 drivers/spi/spi-sh-hspi.c        |  7 ++-----
 drivers/spi/spi-tegra114.c       | 15 +++++----------
 drivers/spi/spi-tegra20-sflash.c | 12 ++++--------
 drivers/spi/spi-tegra20-slink.c  | 15 +++++----------
 drivers/spi/spi-ti-qspi.c        | 32 +++++++++-----------------------
 drivers/spi/spi-txx9.c           |  3 +--
 drivers/spi/spi-xcomm.c          |  8 ++------
 28 files changed, 123 insertions(+), 245 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index b0842f7..fdaa92f 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1317,9 +1317,9 @@ static int atmel_spi_probe(struct platform_device *pdev)
 
 	/* setup spi core then atmel-specific driver state */
 	ret = -ENOMEM;
-	master = spi_alloc_master(&pdev->dev, sizeof(*as));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*as));
 	if (!master)
-		goto out_free;
+		return ret;
 
 	/* the spi->mode bits understood by this driver: */
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
@@ -1341,7 +1341,7 @@ static int atmel_spi_probe(struct platform_device *pdev)
 	as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
 					&as->buffer_dma, GFP_KERNEL);
 	if (!as->buffer)
-		goto out_free;
+		return ret;
 
 	spin_lock_init(&as->lock);
 
@@ -1420,8 +1420,6 @@ out_unmap_regs:
 out_free_buffer:
 	dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
 			as->buffer_dma);
-out_free:
-	spi_master_put(master);
 	return ret;
 }
 
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index 8a89dd1..ece406e 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -305,7 +305,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
 	struct resource *res;
 	int err;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*bs));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*bs));
 	if (!master) {
 		dev_err(&pdev->dev, "spi_alloc_master() failed\n");
 		return -ENOMEM;
@@ -326,23 +326,19 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	bs->regs = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(bs->regs)) {
-		err = PTR_ERR(bs->regs);
-		goto out_master_put;
-	}
+	if (IS_ERR(bs->regs))
+		return PTR_ERR(bs->regs);
 
 	bs->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(bs->clk)) {
-		err = PTR_ERR(bs->clk);
 		dev_err(&pdev->dev, "could not get clk: %d\n", err);
-		goto out_master_put;
+		return PTR_ERR(bs->clk);
 	}
 
 	bs->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
 	if (bs->irq <= 0) {
 		dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq);
-		err = bs->irq ? bs->irq : -ENODEV;
-		goto out_master_put;
+		return bs->irq ? bs->irq : -ENODEV;
 	}
 
 	clk_prepare_enable(bs->clk);
@@ -369,7 +365,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
 out_clk_disable:
 	clk_disable_unprepare(bs->clk);
 out_master_put:
-	spi_master_put(master);
 	return err;
 }
 
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index b528f9f..df797cb 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -355,7 +355,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*bs));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*bs));
 	if (!master) {
 		ret = -ENOMEM;
 		goto out_disable_clk;
@@ -396,17 +396,15 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
 			       pdev->name, bs);
 
 	if (ret)
-		goto out_put_master;
+		goto out_disable_clk;
 
 	/* register and we are done */
 	ret = devm_spi_register_master(dev, master);
 	if (ret)
-		goto out_put_master;
+		goto out_disable_clk;
 
 	return 0;
 
-out_put_master:
-	spi_master_put(master);
 out_disable_clk:
 	clk_disable_unprepare(clk);
 	return ret;
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 77286ae..dae739f 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -346,7 +346,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
 		return PTR_ERR(clk);
 	}
 
-	master = spi_alloc_master(dev, sizeof(*bs));
+	master = devm_spi_alloc_master(dev, sizeof(*bs));
 	if (!master) {
 		dev_err(dev, "out of memory\n");
 		return -ENOMEM;
@@ -359,10 +359,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	bs->regs = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(bs->regs)) {
-		ret = PTR_ERR(bs->regs);
-		goto out_err;
-	}
+	if (IS_ERR(bs->regs))
+		return PTR_ERR(bs->regs);
 
 	bs->irq = irq;
 	bs->clk = clk;
@@ -372,7 +370,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
 							pdev->name, master);
 	if (ret) {
 		dev_err(dev, "unable to request irq\n");
-		goto out_err;
+		return ret;
 	}
 
 	master->bus_num = pdata->bus_num;
@@ -393,13 +391,13 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
 	default:
 		dev_err(dev, "unsupported MSG_CTL width: %d\n",
 			 bs->msg_ctl_width);
-		goto out_err;
+		return ret;
 	}
 
 	/* Initialize hardware */
 	ret = clk_prepare_enable(bs->clk);
 	if (ret)
-		goto out_err;
+		return ret;;
 
 	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
 
@@ -417,8 +415,6 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
 
 out_clk_disable:
 	clk_disable_unprepare(clk);
-out_err:
-	spi_master_put(master);
 	return ret;
 }
 
diff --git a/drivers/spi/spi-bfin-v3.c b/drivers/spi/spi-bfin-v3.c
index 8f85988..140b066 100644
--- a/drivers/spi/spi-bfin-v3.c
+++ b/drivers/spi/spi-bfin-v3.c
@@ -807,7 +807,7 @@ static int bfin_spi_probe(struct platform_device *pdev)
 	rx_dma = res->start;
 
 	/* allocate master with space for drv_data */
-	master = spi_alloc_master(dev, sizeof(*drv_data));
+	master = devm_spi_alloc_master(dev, sizeof(*drv_data));
 	if (!master) {
 		dev_err(dev, "can not alloc spi_master\n");
 		return -ENOMEM;
@@ -833,16 +833,14 @@ static int bfin_spi_probe(struct platform_device *pdev)
 
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	drv_data->regs = devm_ioremap_resource(dev, mem);
-	if (IS_ERR(drv_data->regs)) {
-		ret = PTR_ERR(drv_data->regs);
-		goto err_put_master;
-	}
+	if (IS_ERR(drv_data->regs))
+		return PTR_ERR(drv_data->regs);
 
 	/* request tx and rx dma */
 	ret = request_dma(tx_dma, "SPI_TX_DMA");
 	if (ret) {
 		dev_err(dev, "can not request SPI TX DMA channel\n");
-		goto err_put_master;
+		return ret;
 	}
 	set_dma_callback(tx_dma, bfin_spi_tx_dma_isr, drv_data);
 
@@ -881,9 +879,6 @@ err_free_rx_dma:
 	free_dma(rx_dma);
 err_free_tx_dma:
 	free_dma(tx_dma);
-err_put_master:
-	spi_master_put(master);
-
 	return ret;
 }
 
diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c
index 374ba4a..908c056 100644
--- a/drivers/spi/spi-clps711x.c
+++ b/drivers/spi/spi-clps711x.c
@@ -174,9 +174,9 @@ static int spi_clps711x_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	master = spi_alloc_master(&pdev->dev,
-				  sizeof(struct spi_clps711x_data) +
-				  sizeof(int) * pdata->num_chipselect);
+	master = devm_spi_alloc_master(&pdev->dev,
+				       sizeof(struct spi_clps711x_data) +
+				       sizeof(int) * pdata->num_chipselect);
 	if (!master) {
 		dev_err(&pdev->dev, "SPI allocating memory error\n");
 		return -ENOMEM;
@@ -195,21 +195,18 @@ static int spi_clps711x_probe(struct platform_device *pdev)
 		hw->chipselect[i] = pdata->chipselect[i];
 		if (!gpio_is_valid(hw->chipselect[i])) {
 			dev_err(&pdev->dev, "Invalid CS GPIO %i\n", i);
-			ret = -EINVAL;
-			goto err_out;
+			return -EINVAL;
 		}
 		if (devm_gpio_request(&pdev->dev, hw->chipselect[i], NULL)) {
 			dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i);
-			ret = -EINVAL;
-			goto err_out;
+			return -EINVAL;
 		}
 	}
 
 	hw->spi_clk = devm_clk_get(&pdev->dev, "spi");
 	if (IS_ERR(hw->spi_clk)) {
 		dev_err(&pdev->dev, "Can't get clocks\n");
-		ret = PTR_ERR(hw->spi_clk);
-		goto err_out;
+		return PTR_ERR(hw->spi_clk);
 	}
 	hw->max_speed_hz = clk_get_rate(hw->spi_clk);
 
@@ -226,23 +223,19 @@ static int spi_clps711x_probe(struct platform_device *pdev)
 			       dev_name(&pdev->dev), hw);
 	if (ret) {
 		dev_err(&pdev->dev, "Can't request IRQ\n");
-		goto err_out;
+		return ret;
 	}
 
 	ret = devm_spi_register_master(&pdev->dev, master);
-	if (!ret) {
-		dev_info(&pdev->dev,
-			 "SPI bus driver initialized. Master clock %u Hz\n",
-			 hw->max_speed_hz);
-		return 0;
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register master\n");
+		return ret;
 	}
-
-	dev_err(&pdev->dev, "Failed to register master\n");
-
-err_out:
-	spi_master_put(master);
-
-	return ret;
+	
+	dev_info(&pdev->dev,
+		 "SPI bus driver initialized. Master clock %u Hz\n",
+		 hw->max_speed_hz);
+	return 0;
 }
 
 static struct platform_driver clps711x_spi_driver = {
diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c
index cabed8f..41f6c9c 100644
--- a/drivers/spi/spi-coldfire-qspi.c
+++ b/drivers/spi/spi-coldfire-qspi.c
@@ -388,7 +388,7 @@ static int mcfqspi_probe(struct platform_device *pdev)
 		return -ENOENT;
 	}
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*mcfqspi));
 	if (master == NULL) {
 		dev_dbg(&pdev->dev, "spi_alloc_master failed\n");
 		return -ENOMEM;
@@ -399,29 +399,26 @@ static int mcfqspi_probe(struct platform_device *pdev)
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	mcfqspi->iobase = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(mcfqspi->iobase)) {
-		status = PTR_ERR(mcfqspi->iobase);
-		goto fail0;
+		return PTR_ERR(mcfqspi->iobase);
 	}
 
 	mcfqspi->irq = platform_get_irq(pdev, 0);
 	if (mcfqspi->irq < 0) {
 		dev_dbg(&pdev->dev, "platform_get_irq failed\n");
-		status = -ENXIO;
-		goto fail0;
+		return -ENXIO;
 	}
 
 	status = devm_request_irq(&pdev->dev, mcfqspi->irq, mcfqspi_irq_handler,
 				0, pdev->name, mcfqspi);
 	if (status) {
 		dev_dbg(&pdev->dev, "request_irq failed\n");
-		goto fail0;
+		return status;
 	}
 
 	mcfqspi->clk = devm_clk_get(&pdev->dev, "qspi_clk");
 	if (IS_ERR(mcfqspi->clk)) {
 		dev_dbg(&pdev->dev, "clk_get failed\n");
-		status = PTR_ERR(mcfqspi->clk);
-		goto fail0;
+		return PTR_ERR(mcfqspi->clk);
 	}
 	clk_enable(mcfqspi->clk);
 
@@ -461,8 +458,6 @@ fail2:
 	mcfqspi_cs_teardown(mcfqspi);
 fail1:
 	clk_disable(mcfqspi->clk);
-fail0:
-	spi_master_put(master);
 
 	dev_dbg(&pdev->dev, "Coldfire QSPI probe failed\n");
 
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index bf98d63..bcb172c 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -783,7 +783,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 
 	BUG_ON(dws == NULL);
 
-	master = spi_alloc_master(dev, 0);
+	master = devm_spi_alloc_master(dev, 0);
 	if (!master)
 		return -ENOMEM;
 
@@ -799,7 +799,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 			dws->name, dws);
 	if (ret < 0) {
 		dev_err(&master->dev, "can not get IRQ\n");
-		goto err_free_master;
+		return ret;
 	}
 
 	master->mode_bits = SPI_CPOL | SPI_CPHA;
@@ -849,8 +849,6 @@ err_queue_alloc:
 		dws->dma_ops->dma_exit(dws);
 err_diable_hw:
 	spi_enable_chip(dws, 0);
-err_free_master:
-	spi_master_put(master);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(dw_spi_add_host);
diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c
index 1bfaed6..7177435 100644
--- a/drivers/spi/spi-ep93xx.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -888,7 +888,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*espi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*espi));
 	if (!master)
 		return -ENOMEM;
 
@@ -907,8 +907,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
 	espi->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(espi->clk)) {
 		dev_err(&pdev->dev, "unable to get spi clock\n");
-		error = PTR_ERR(espi->clk);
-		goto fail_release_master;
+		return PTR_ERR(espi->clk);
 	}
 
 	init_completion(&espi->wait);
@@ -924,16 +923,14 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
 	espi->sspdr_phys = res->start + SSPDR;
 
 	espi->regs_base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(espi->regs_base)) {
-		error = PTR_ERR(espi->regs_base);
-		goto fail_release_master;
-	}
+	if (IS_ERR(espi->regs_base))
+		return PTR_ERR(espi->regs_base);
 
 	error = devm_request_irq(&pdev->dev, irq, ep93xx_spi_interrupt,
 				0, "ep93xx-spi", espi);
 	if (error) {
 		dev_err(&pdev->dev, "failed to request irq\n");
-		goto fail_release_master;
+		return error;
 	}
 
 	if (info->use_dma && ep93xx_spi_setup_dma(espi))
@@ -955,8 +952,6 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
 
 fail_free_dma:
 	ep93xx_spi_release_dma(espi);
-fail_release_master:
-	spi_master_put(master);
 
 	return error;
 }
diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
index dd5bd46..a6aa66f 100644
--- a/drivers/spi/spi-falcon.c
+++ b/drivers/spi/spi-falcon.c
@@ -414,7 +414,7 @@ static int falcon_sflash_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*priv));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*priv));
 	if (!master)
 		return -ENOMEM;
 
@@ -434,8 +434,7 @@ static int falcon_sflash_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, priv);
 
 	ret = devm_spi_register_master(&pdev->dev, master);
-	if (ret)
-		spi_master_put(master);
+
 	return ret;
 }
 
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index 46d2313..f376595 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -479,7 +479,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
 	char clk_name[16];
 	struct clk *clk;
 
-	master = spi_alloc_master(dev, sizeof *mps);
+	master = devm_spi_alloc_master(dev, sizeof *mps);
 	if (master == NULL)
 		return -ENOMEM;
 
@@ -507,8 +507,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
 	tempp = devm_ioremap(dev, regaddr, size);
 	if (!tempp) {
 		dev_err(dev, "could not ioremap I/O port range\n");
-		ret = -EFAULT;
-		goto free_master;
+		return -EFAULT;
 	}
 	mps->psc = tempp;
 	mps->fifo =
@@ -516,19 +515,19 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
 	ret = devm_request_irq(dev, mps->irq, mpc512x_psc_spi_isr, IRQF_SHARED,
 				"mpc512x-psc-spi", mps);
 	if (ret)
-		goto free_master;
+		return ret;
 	init_completion(&mps->txisrdone);
 
 	psc_num = master->bus_num;
 	snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num);
 	clk = devm_clk_get(dev, clk_name);
-	if (IS_ERR(clk)) {
-		ret = PTR_ERR(clk);
-		goto free_master;
-	}
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
 	ret = clk_prepare_enable(clk);
 	if (ret)
-		goto free_master;
+		return ret;
+
 	mps->clk_mclk = clk;
 	mps->mclk_rate = clk_get_rate(clk);
 
@@ -544,8 +543,6 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
 
 free_clock:
 	clk_disable_unprepare(mps->clk_mclk);
-free_master:
-	spi_master_put(master);
 
 	return ret;
 }
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
index 79e5aa2..2c63bed 100644
--- a/drivers/spi/spi-mxs.c
+++ b/drivers/spi/spi-mxs.c
@@ -489,7 +489,7 @@ static int mxs_spi_probe(struct platform_device *pdev)
 	if (ret)
 		clk_freq = clk_freq_default;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*spi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*spi));
 	if (!master)
 		return -ENOMEM;
 
@@ -512,13 +512,12 @@ static int mxs_spi_probe(struct platform_device *pdev)
 	ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0,
 			       DRIVER_NAME, ssp);
 	if (ret)
-		goto out_master_free;
+		return ret;
 
 	ssp->dmach = dma_request_slave_channel(&pdev->dev, "rx-tx");
 	if (!ssp->dmach) {
 		dev_err(ssp->dev, "Failed to request DMA\n");
-		ret = -ENODEV;
-		goto out_master_free;
+		return -ENODEV;
 	}
 
 	ret = clk_prepare_enable(ssp->clk);
@@ -545,8 +544,6 @@ out_disable_clk:
 	clk_disable_unprepare(ssp->clk);
 out_dma_release:
 	dma_release_channel(ssp->dmach);
-out_master_free:
-	spi_master_put(master);
 	return ret;
 }
 
diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c
index 67249a4..192ca3d 100644
--- a/drivers/spi/spi-octeon.c
+++ b/drivers/spi/spi-octeon.c
@@ -236,7 +236,7 @@ static int octeon_spi_probe(struct platform_device *pdev)
 	struct octeon_spi *p;
 	int err = -ENOENT;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi));
 	if (!master)
 		return -ENOMEM;
 	p = spi_master_get_devdata(master);
@@ -246,13 +246,12 @@ static int octeon_spi_probe(struct platform_device *pdev)
 
 	if (res_mem == NULL) {
 		dev_err(&pdev->dev, "found no memory resource\n");
-		err = -ENXIO;
-		goto fail;
+		return -ENXIO;
 	}
 	if (!devm_request_mem_region(&pdev->dev, res_mem->start,
 				     resource_size(res_mem), res_mem->name)) {
 		dev_err(&pdev->dev, "request_mem_region failed\n");
-		goto fail;
+		return err;
 	}
 	p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start,
 					     resource_size(res_mem));
@@ -275,15 +274,12 @@ static int octeon_spi_probe(struct platform_device *pdev)
 	err = devm_spi_register_master(&pdev->dev, master);
 	if (err) {
 		dev_err(&pdev->dev, "register master failed: %d\n", err);
-		goto fail;
+		return err;
 	}
 
 	dev_info(&pdev->dev, "OCTEON SPI bus driver\n");
 
 	return 0;
-fail:
-	spi_master_put(master);
-	return err;
 }
 
 static int octeon_spi_remove(struct platform_device *pdev)
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c
index 0d32054..4ceae27 100644
--- a/drivers/spi/spi-omap-100k.c
+++ b/drivers/spi/spi-omap-100k.c
@@ -411,7 +411,7 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
 	if (!pdev->id)
 		return -EINVAL;
 
-	master = spi_alloc_master(&pdev->dev, sizeof *spi100k);
+	master = devm_spi_alloc_master(&pdev->dev, sizeof *spi100k);
 	if (master == NULL) {
 		dev_dbg(&pdev->dev, "master allocation failed\n");
 		return -ENOMEM;
@@ -446,28 +446,22 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
 	spi100k->ick = devm_clk_get(&pdev->dev, "ick");
 	if (IS_ERR(spi100k->ick)) {
 		dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
-		status = PTR_ERR(spi100k->ick);
-		goto err;
+		return PTR_ERR(spi100k->ick);
 	}
 
 	spi100k->fck = devm_clk_get(&pdev->dev, "fck");
 	if (IS_ERR(spi100k->fck)) {
 		dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
-		status = PTR_ERR(spi100k->fck);
-		goto err;
+		return PTR_ERR(spi100k->fck);
 	}
 
 	status = devm_spi_register_master(&pdev->dev, master);
 	if (status < 0)
-		goto err;
+		return status;
 
 	spi100k->state = SPI_RUNNING;
 
-	return status;
-
-err:
-	spi_master_put(master);
-	return status;
+	return 0;
 }
 
 static struct platform_driver omap1_spi100k_driver = {
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index a72127f..a4b105e 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -1297,7 +1297,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
 	struct device_node	*node = pdev->dev.of_node;
 	const struct of_device_id *match;
 
-	master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
+	master = devm_spi_alloc_master(&pdev->dev, sizeof *mcspi);
 	if (master == NULL) {
 		dev_dbg(&pdev->dev, "master allocation failed\n");
 		return -ENOMEM;
@@ -1337,20 +1337,16 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
 	regs_offset = pdata->regs_offset;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (r == NULL) {
-		status = -ENODEV;
-		goto free_master;
-	}
+	if (r == NULL)
+		return -ENODEV;
 
 	r->start += regs_offset;
 	r->end += regs_offset;
 	mcspi->phys = r->start;
 
 	mcspi->base = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(mcspi->base)) {
-		status = PTR_ERR(mcspi->base);
-		goto free_master;
-	}
+	if (IS_ERR(mcspi->base))
+		return PTR_ERR(mcspi->base);
 
 	mcspi->dev = &pdev->dev;
 
@@ -1361,7 +1357,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
 			GFP_KERNEL);
 
 	if (mcspi->dma_channels == NULL)
-		goto free_master;
+		return status;
 
 	for (i = 0; i < master->num_chipselect; i++) {
 		char *dma_rx_ch_name = mcspi->dma_channels[i].dma_rx_ch_name;
@@ -1423,8 +1419,6 @@ disable_pm:
 	pm_runtime_disable(&pdev->dev);
 dma_chnl_free:
 	kfree(mcspi->dma_channels);
-free_master:
-	spi_master_put(master);
 	return status;
 }
 
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 7f2121f..1ca04af 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -407,7 +407,7 @@ static int orion_spi_probe(struct platform_device *pdev)
 	const u32 *iprop;
 	int size;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*spi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*spi));
 	if (master == NULL) {
 		dev_dbg(&pdev->dev, "master allocation failed\n");
 		return -ENOMEM;
@@ -435,10 +435,8 @@ static int orion_spi_probe(struct platform_device *pdev)
 	spi->master = master;
 
 	spi->clk = devm_clk_get(&pdev->dev, NULL);
-	if (IS_ERR(spi->clk)) {
-		status = PTR_ERR(spi->clk);
-		goto out;
-	}
+	if (IS_ERR(spi->clk))
+		return PTR_ERR(spi->clk);
 
 	clk_prepare(spi->clk);
 	clk_enable(spi->clk);
@@ -465,8 +463,6 @@ static int orion_spi_probe(struct platform_device *pdev)
 
 out_rel_clk:
 	clk_disable_unprepare(spi->clk);
-out:
-	spi_master_put(master);
 	return status;
 }
 
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 2789b45..69e4d75 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -2095,7 +2095,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
 	}
 
 	/* Allocate master with space for data */
-	master = spi_alloc_master(dev, sizeof(struct pl022));
+	master = devm_spi_alloc_master(dev, sizeof(struct pl022));
 	if (master == NULL) {
 		dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
 		return -ENOMEM;
@@ -2259,7 +2259,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
 	amba_release_regions(adev);
  err_no_ioregion:
  err_no_gpio:
-	spi_master_put(master);
 	return status;
 }
 
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index c702fc5..51017e8 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1117,7 +1117,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
 	}
 
 	/* Allocate master with space for drv_data and null dma buffer */
-	master = spi_alloc_master(dev, sizeof(struct driver_data) + 16);
+	master = devm_spi_alloc_master(dev, sizeof(struct driver_data) + 16);
 	if (!master) {
 		dev_err(&pdev->dev, "cannot alloc spi_master\n");
 		pxa_ssp_free(ssp);
@@ -1224,7 +1224,6 @@ out_error_clock_enabled:
 	free_irq(ssp->irq, drv_data);
 
 out_error_master_alloc:
-	spi_master_put(master);
 	pxa_ssp_free(ssp);
 	return status;
 }
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index 28987d9..b69d06f 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -952,7 +952,7 @@ static int rspi_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(struct rspi_data));
 	if (master == NULL) {
 		dev_err(&pdev->dev, "spi_alloc_master error.\n");
 		return -ENOMEM;
@@ -965,17 +965,14 @@ static int rspi_probe(struct platform_device *pdev)
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	rspi->addr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(rspi->addr)) {
-		ret = PTR_ERR(rspi->addr);
-		goto error1;
-	}
+	if (IS_ERR(rspi->addr))
+		return PTR_ERR(rspi->addr);
 
 	snprintf(clk_name, sizeof(clk_name), "%s%d", id_entry->name, pdev->id);
 	rspi->clk = devm_clk_get(&pdev->dev, clk_name);
 	if (IS_ERR(rspi->clk)) {
 		dev_err(&pdev->dev, "cannot get clock\n");
-		ret = PTR_ERR(rspi->clk);
-		goto error1;
+		return PTR_ERR(rspi->clk);
 	}
 	clk_enable(rspi->clk);
 
@@ -1023,8 +1020,6 @@ error3:
 	rspi_release_dma(rspi);
 error2:
 	clk_disable(rspi->clk);
-error1:
-	spi_master_put(master);
 
 	return ret;
 }
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index ae907dd..1772cba 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1279,8 +1279,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
 		return irq;
 	}
 
-	master = spi_alloc_master(&pdev->dev,
-				sizeof(struct s3c64xx_spi_driver_data));
+	master = devm_spi_alloc_master(&pdev->dev,
+				       sizeof(struct s3c64xx_spi_driver_data));
 	if (master == NULL) {
 		dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
 		return -ENOMEM;
@@ -1303,7 +1303,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
 		if (ret < 0) {
 			dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
 				ret);
-			goto err0;
+			return ret;
 		}
 		sdd->port_id = ret;
 	} else {
@@ -1349,29 +1349,24 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
 	master->auto_runtime_pm = true;
 
 	sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
-	if (IS_ERR(sdd->regs)) {
-		ret = PTR_ERR(sdd->regs);
-		goto err0;
-	}
+	if (IS_ERR(sdd->regs))
+		return PTR_ERR(sdd->regs);
 
 	if (sci->cfg_gpio && sci->cfg_gpio()) {
 		dev_err(&pdev->dev, "Unable to config gpio\n");
-		ret = -EBUSY;
-		goto err0;
+		return -EBUSY;
 	}
 
 	/* Setup clocks */
 	sdd->clk = devm_clk_get(&pdev->dev, "spi");
 	if (IS_ERR(sdd->clk)) {
 		dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
-		ret = PTR_ERR(sdd->clk);
-		goto err0;
+		return PTR_ERR(sdd->clk);
 	}
 
 	if (clk_prepare_enable(sdd->clk)) {
 		dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
-		ret = -EBUSY;
-		goto err0;
+		return -EBUSY;
 	}
 
 	sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
@@ -1428,8 +1423,6 @@ err3:
 	clk_disable_unprepare(sdd->src_clk);
 err2:
 	clk_disable_unprepare(sdd->clk);
-err0:
-	spi_master_put(master);
 
 	return ret;
 }
diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c
index 121c2e1..64760ae 100644
--- a/drivers/spi/spi-sc18is602.c
+++ b/drivers/spi/spi-sc18is602.c
@@ -267,7 +267,7 @@ static int sc18is602_probe(struct i2c_client *client,
 				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
 		return -EINVAL;
 
-	master = spi_alloc_master(dev, sizeof(struct sc18is602));
+	master = devm_spi_alloc_master(dev, sizeof(struct sc18is602));
 	if (!master)
 		return -ENOMEM;
 
@@ -310,15 +310,7 @@ static int sc18is602_probe(struct i2c_client *client,
 	master->transfer_one_message = sc18is602_transfer_one;
 	master->dev.of_node = np;
 
-	error = devm_spi_register_master(dev, master);
-	if (error)
-		goto error_reg;
-
-	return 0;
-
-error_reg:
-	spi_master_put(master);
-	return error;
+	return devm_spi_register_master(dev, master);
 }
 
 static const struct i2c_device_id sc18is602_id[] = {
diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c
index 82d2f92..fe090a3 100644
--- a/drivers/spi/spi-sh-hspi.c
+++ b/drivers/spi/spi-sh-hspi.c
@@ -268,7 +268,7 @@ static int hspi_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*hspi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*hspi));
 	if (!master) {
 		dev_err(&pdev->dev, "spi_alloc_master error.\n");
 		return -ENOMEM;
@@ -277,8 +277,7 @@ static int hspi_probe(struct platform_device *pdev)
 	clk = clk_get(NULL, "shyway_clk");
 	if (IS_ERR(clk)) {
 		dev_err(&pdev->dev, "shyway_clk is required\n");
-		ret = -EINVAL;
-		goto error0;
+		return -EINVAL;
 	}
 
 	hspi = spi_master_get_devdata(master);
@@ -316,8 +315,6 @@ static int hspi_probe(struct platform_device *pdev)
 
  error1:
 	clk_put(clk);
- error0:
-	spi_master_put(master);
 
 	return ret;
 }
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 47b93cc..72076f6 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1048,7 +1048,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
 	struct resource		*r;
 	int ret, spi_irq;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*tspi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*tspi));
 	if (!master) {
 		dev_err(&pdev->dev, "master allocation failed\n");
 		return -ENOMEM;
@@ -1073,10 +1073,8 @@ static int tegra_spi_probe(struct platform_device *pdev)
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	tspi->base = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(tspi->base)) {
-		ret = PTR_ERR(tspi->base);
-		goto exit_free_master;
-	}
+	if (IS_ERR(tspi->base))
+		return PTR_ERR(tspi->base);
 	tspi->phys = r->start;
 
 	spi_irq = platform_get_irq(pdev, 0);
@@ -1087,14 +1085,13 @@ static int tegra_spi_probe(struct platform_device *pdev)
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
 					tspi->irq);
-		goto exit_free_master;
+		return ret;
 	}
 
 	tspi->clk = devm_clk_get(&pdev->dev, "spi");
 	if (IS_ERR(tspi->clk)) {
 		dev_err(&pdev->dev, "can not get clock\n");
-		ret = PTR_ERR(tspi->clk);
-		goto exit_free_irq;
+		return PTR_ERR(tspi->clk);
 	}
 
 	tspi->max_buf_size = SPI_FIFO_DEPTH << 2;
@@ -1152,8 +1149,6 @@ exit_rx_dma_free:
 	tegra_spi_deinit_dma_param(tspi, true);
 exit_free_irq:
 	free_irq(spi_irq, tspi);
-exit_free_master:
-	spi_master_put(master);
 	return ret;
 }
 
diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c
index af78c17..1638036 100644
--- a/drivers/spi/spi-tegra20-sflash.c
+++ b/drivers/spi/spi-tegra20-sflash.c
@@ -458,7 +458,7 @@ static int tegra_sflash_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*tsd));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*tsd));
 	if (!master) {
 		dev_err(&pdev->dev, "master allocation failed\n");
 		return -ENOMEM;
@@ -482,10 +482,8 @@ static int tegra_sflash_probe(struct platform_device *pdev)
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	tsd->base = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(tsd->base)) {
-		ret = PTR_ERR(tsd->base);
-		goto exit_free_master;
-	}
+	if (IS_ERR(tsd->base))
+		return PTR_ERR(tsd->base);
 
 	tsd->irq = platform_get_irq(pdev, 0);
 	ret = request_irq(tsd->irq, tegra_sflash_isr, 0,
@@ -493,7 +491,7 @@ static int tegra_sflash_probe(struct platform_device *pdev)
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
 					tsd->irq);
-		goto exit_free_master;
+		return ret;
 	}
 
 	tsd->clk = devm_clk_get(&pdev->dev, NULL);
@@ -540,8 +538,6 @@ exit_pm_disable:
 		tegra_sflash_runtime_suspend(&pdev->dev);
 exit_free_irq:
 	free_irq(tsd->irq, tsd);
-exit_free_master:
-	spi_master_put(master);
 	return ret;
 }
 
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index 3ce1de8..65e3f0d 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -1045,7 +1045,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
 	}
 	cdata = match->data;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*tspi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*tspi));
 	if (!master) {
 		dev_err(&pdev->dev, "master allocation failed\n");
 		return -ENOMEM;
@@ -1073,15 +1073,12 @@ static int tegra_slink_probe(struct platform_device *pdev)
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
 		dev_err(&pdev->dev, "No IO memory resource\n");
-		ret = -ENODEV;
-		goto exit_free_master;
+		return -ENODEV;
 	}
 	tspi->phys = r->start;
 	tspi->base = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(tspi->base)) {
-		ret = PTR_ERR(tspi->base);
-		goto exit_free_master;
-	}
+	if (IS_ERR(tspi->base))
+		return PTR_ERR(tspi->base);
 
 	spi_irq = platform_get_irq(pdev, 0);
 	tspi->irq = spi_irq;
@@ -1091,7 +1088,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
 					tspi->irq);
-		goto exit_free_master;
+		return ret;
 	}
 
 	tspi->clk = devm_clk_get(&pdev->dev, NULL);
@@ -1158,8 +1155,6 @@ exit_rx_dma_free:
 	tegra_slink_deinit_dma_param(tspi, true);
 exit_free_irq:
 	free_irq(spi_irq, tspi);
-exit_free_master:
-	spi_master_put(master);
 	return ret;
 }
 
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index 3d09265..61807ef 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -423,7 +423,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	u32 max_freq;
 	int ret = 0, num_cs, irq;
 
-	master = spi_alloc_master(&pdev->dev, sizeof(*qspi));
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(*qspi));
 	if (!master)
 		return -ENOMEM;
 
@@ -484,26 +484,20 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	mutex_init(&qspi->list_lock);
 
 	qspi->base = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(qspi->base)) {
-		ret = PTR_ERR(qspi->base);
-		goto free_master;
-	}
+	if (IS_ERR(qspi->base))
+		return PTR_ERR(qspi->base);
 
 	if (res_ctrl) {
 		qspi->ctrl_mod = true;
 		qspi->ctrl_base = devm_ioremap_resource(&pdev->dev, res_ctrl);
-		if (IS_ERR(qspi->ctrl_base)) {
-			ret = PTR_ERR(qspi->ctrl_base);
-			goto free_master;
-		}
+		if (IS_ERR(qspi->ctrl_base))
+			return PTR_ERR(qspi->ctrl_base);
 	}
 
 	if (res_mmap) {
 		qspi->mmap_base = devm_ioremap_resource(&pdev->dev, res_mmap);
-		if (IS_ERR(qspi->mmap_base)) {
-			ret = PTR_ERR(qspi->mmap_base);
-			goto free_master;
-		}
+		if (IS_ERR(qspi->mmap_base))
+			return PTR_ERR(qspi->mmap_base);
 	}
 
 	ret = devm_request_irq(&pdev->dev, irq, ti_qspi_isr, 0,
@@ -511,7 +505,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
 				irq);
-		goto free_master;
+		return ret;
 	}
 
 	qspi->fclk = devm_clk_get(&pdev->dev, "fck");
@@ -529,15 +523,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 	if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
 		qspi->spi_max_frequency = max_freq;
 
-	ret = devm_spi_register_master(&pdev->dev, master);
-	if (ret)
-		goto free_master;
-
-	return 0;
-
-free_master:
-	spi_master_put(master);
-	return ret;
+	return devm_spi_register_master(&pdev->dev, master);
 }
 
 static int ti_qspi_remove(struct platform_device *pdev)
diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c
index 6191ced..76307b7 100644
--- a/drivers/spi/spi-txx9.c
+++ b/drivers/spi/spi-txx9.c
@@ -337,7 +337,7 @@ static int txx9spi_probe(struct platform_device *dev)
 	u32 mcr;
 	int irq;
 
-	master = spi_alloc_master(&dev->dev, sizeof(*c));
+	master = devm_spi_alloc_master(&dev->dev, sizeof(*c));
 	if (!master)
 		return ret;
 	c = spi_master_get_devdata(master);
@@ -416,7 +416,6 @@ exit:
 		destroy_workqueue(c->workqueue);
 	if (c->clk)
 		clk_disable(c->clk);
-	spi_master_put(master);
 	return ret;
 }
 
diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c
index 24c40b1..094119d 100644
--- a/drivers/spi/spi-xcomm.c
+++ b/drivers/spi/spi-xcomm.c
@@ -216,7 +216,7 @@ static int spi_xcomm_probe(struct i2c_client *i2c,
 	struct spi_master *master;
 	int ret;
 
-	master = spi_alloc_master(&i2c->dev, sizeof(*spi_xcomm));
+	master = devm_spi_alloc_master(&i2c->dev, sizeof(*spi_xcomm));
 	if (!master)
 		return -ENOMEM;
 
@@ -231,11 +231,7 @@ static int spi_xcomm_probe(struct i2c_client *i2c,
 	master->dev.of_node = i2c->dev.of_node;
 	i2c_set_clientdata(i2c, master);
 
-	ret = devm_spi_register_master(&i2c->dev, master);
-	if (ret < 0)
-		spi_master_put(master);
-
-	return ret;
+	return devm_spi_register_master(&i2c->dev, master);
 }
 
 static const struct i2c_device_id spi_xcomm_ids[] = {
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v2 2/3] ata: sata_mv: fix disk hotplug for Armada 370/XP SoCs
From: Simon Guinot @ 2014-01-31 10:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140126083210.2998ef12@skate>

On Sun, Jan 26, 2014 at 08:32:10AM +0100, Thomas Petazzoni wrote:
> Dear Simon Guinot,

Hi Thomas,

> 
> On Tue, 14 Jan 2014 15:50:06 +0100, Simon Guinot wrote:
> 
> > +			if (hpriv->hp_flags & MV_HP_FIX_LP_PHY_CTL) {
> > +				void __iomem *lp_phy_addr =
> > +					mv_ap_base(link->ap) + LP_PHY_CTL;
> > +				/*
> > +				 * Set PHY speed according to SControl speed.
> > +				 */
> > +				if ((val & 0xf0) == 0x10)
> > +					writelfl(0x7, lp_phy_addr);
> > +				else
> > +					writelfl(0x227, lp_phy_addr);
> > +			}
> 
> I think we could do a little bit better than these magical values.
> 
> The datasheet says:
> 
>  * bits 12:9, PIN_PHY_GEN_RX. Value 0x0 => 1.5 Gbps, value 0x1 => 3 Gbps
>  * bits 8:5, PIN_PHY_GEN_TX. Value 0x0 => 1.5 Gbps, value 0x1 => 3 Gbps
>  * bit 2, PIN_PU_TX. Value 0x0 => Power down, value 0x1 => Power up.
>  * bit 1, PIN_PU_RX. Value 0x0 => Power down, value 0x1 => Power up.
>  * bit 0, PIN_PU_PLL. Value 0x0 => Power down, value 0x1 => Power up.

I missed this section in the datasheet...

> 
> So maybe something like:
> 
> #define PIN_PHY_GEN_1_5		0
> #define PIN_PHY_GEN_3		1
> 
> #define PIN_PHY_GEN_RX(gen)	((gen) << 9)
> #define PIN_PHY_GEN_TX(gen)	((gen) << 5)
> #define PIN_PU_TX		BIT(2)
> #define PIN_PU_RX		BIT(1)
> #define PIN_PU_PLL		BIT(0)
> 
> 
> 		u32 sata_gen;
> 
> 		if ((val & 0xf0) == 0x10)
> 			sata_gen = PIN_PHY_GEN_1_5;
> 		else
> 			sata_gen = PIN_PHY_GEN_3;
> 
> 		writelfl(PIN_PHY_GEN_RX(sata_gen) |
> 			 PIN_PHY_GEN_TX(sata_gen) |
> 			 PIN_PU_TX | PIN_PU_RX | PIN_PU_PLL,
> 			 lp_phy_addr);

Yes, it is much more understandable.

> 
> 
> > +	/*
> > +	 * To allow disk hotplug on Armada 370/XP SoCs, the PHY speed must be
> > +	 * updated in the LP_PHY_CTL register.
> > +	 */
> > +	if (pdev->dev.of_node &&
> > +		of_device_is_compatible(pdev->dev.of_node,
> > +					"marvell,armada-370-xp-sata"))
> 
> Testing whether pdev->dev.of_node is not NULL does not seems to be
> useful. A quick read of of_device_is_compatible() and the function it's
> calling seem to indicate that of_device_is_compatible will return false
> if the passed struct device_node * is NULL.

I see.

It seems to me, you already have a patch ready to send. Isn't it ?
Or do you want me to do the update ?

Simon
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140131/1e4dc2f0/attachment.sig>

^ permalink raw reply

* [PATCH V2] arm64: add DSB after icache flush in __flush_icache_all()
From: Russell King - ARM Linux @ 2014-01-31 10:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140131001647.GA5525@mudshark.cambridge.arm.com>

On Fri, Jan 31, 2014 at 12:16:48AM +0000, Will Deacon wrote:
> Hi Nico,
> 
> On Thu, Jan 30, 2014 at 09:42:29PM +0000, Nicolas Pitre wrote:
> > On Thu, 30 Jan 2014, Will Deacon wrote:
> > > On Thu, Jan 30, 2014 at 06:04:43AM +0000, Vinayak Kale wrote:
> > > > Can you please elaborate whether you are referring to lack of memory
> > > > clobber or missing barriers?
> > > 
> > > The clobbers. For example:
> > > 
> > > arch/arm64/kvm/sys_regs.c:
> > > 
> > >         /* Make sure noone else changes CSSELR during this! */
> > >         local_irq_disable();
> > >         /* Put value into CSSELR */
> > >         asm volatile("msr csselr_el1, %x0" : : "r" (csselr));
> > >         isb();
> > >         /* Read result out of CCSIDR */
> > >         asm volatile("mrs %0, ccsidr_el1" : "=r" (ccsidr));
> > >         local_irq_enable();
> > > 
> > > Just about everything can be re-ordered in that block, because the asm
> > > volatile statements don't have "memory" clobbers.
> > 
> > I don't think they would be reordered at all with the 
> > volatile qualifiers.
> 
> Whilst that may be the case in current compilers (i.e. I've not actually
> seen the above sequence get re-ordered), the GCC documentation states that:
> 
>   Similarly, you can't expect a sequence of volatile asm instructions to remain
>   perfectly consecutive. If you want consecutive output, use a single asm. Also,
>   GCC performs some optimizations across a volatile asm instruction; GCC does not
>   `forget everything' when it encounters a volatile asm instruction the way some
>   other compilers do.
> 
> so I really think that the "memory" clobbers are needed to ensure strict
> ordering. This matches my understanding from discussions with the compiler
> engineers at ARM.

What it means is that the compiler may introduce additional instructions
between your consecutive asm() statements.  So there's no guarantee that
the ISB will immediately follow the MSR instruction - there may be other
instructions which the compiler may decide to schedule between the two.

For example, instructions to load the address of the variable(s) may be
inserted between the assembly specified in the asm() statements which
may involve loading from a literal pool.

-- 
FTTC broadband for 0.8mile line: 5.8Mbps down 500kbps up.  Estimation
in database were 13.1 to 19Mbit for a good line, about 7.5+ for a bad.
Estimate before purchase was "up to 13.2Mbit".

^ permalink raw reply

* [PATCH v2 2/3] ata: sata_mv: fix disk hotplug for Armada 370/XP SoCs
From: Thomas Petazzoni @ 2014-01-31 10:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140131104643.GC10250@kw.sim.vm.gnt>

Dear Simon Guinot,

On Fri, 31 Jan 2014 11:46:44 +0100, Simon Guinot wrote:

> > > +	/*
> > > +	 * To allow disk hotplug on Armada 370/XP SoCs, the PHY speed must be
> > > +	 * updated in the LP_PHY_CTL register.
> > > +	 */
> > > +	if (pdev->dev.of_node &&
> > > +		of_device_is_compatible(pdev->dev.of_node,
> > > +					"marvell,armada-370-xp-sata"))
> > 
> > Testing whether pdev->dev.of_node is not NULL does not seems to be
> > useful. A quick read of of_device_is_compatible() and the function it's
> > calling seem to indicate that of_device_is_compatible will return false
> > if the passed struct device_node * is NULL.
> 
> I see.
> 
> It seems to me, you already have a patch ready to send. Isn't it ?
> Or do you want me to do the update ?

I see that your patches have been merged, so I will send followup fixes.

Thanks!

Thomas
-- 
Thomas Petazzoni, CTO, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH v3 0/5] Add Allwinner A31 SPI controller support
From: Maxime Ripard @ 2014-01-31 10:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hi everyone,

This patchset brings support for the SPI controller found in the
Allwinner A31 SoC.

Even though the controller supports DMA, the driver only supports PIO
mode for now. This driver will be used to bring up and test DMA on the
SoC, so support for the DMA will come eventually.

It doesn't support transfer larger than the FIFO size (128 bytes) for
now, I expect it to be fixed in the future.

Thanks!
Maxime

Changes from v2:
  - Removed the select on runtime_pm
  - Fixed the clock error messages
  - Trigger the CS manually, and honour the enable bit in set_cs
  - Convert to devm_* functions
  - Remove useless clk_disable_unprepare in probe

Changes from v1:
  - Switched to using the transfer_one and set_cs callbacks
  - Switched to using runtime_pm
  - Report an error when we try to do a transfer larger than the FIFO
    size, instead of silently timeouting.
  - Added a Kconfig symbol
  - Move the clock ratio change at transfer time
  - Fixed the PLL6 cell size in the DTSI
  - A few fixes here and there: typos, etc.

Maxime Ripard (5):
  clk: sunxi: Add support for PLL6 on the A31
  ARM: sun6i: dt: Add PLL6 and SPI module clocks
  spi: sunxi: Add Allwinner A31 SPI controller driver
  ARM: sun6i: dt: Add SPI controllers to the A31 DTSI
  ARM: sunxi: Enable A31 SPI and SID in the defconfig

 Documentation/devicetree/bindings/clock/sunxi.txt  |   1 +
 .../devicetree/bindings/spi/spi-sun6i.txt          |  24 ++
 arch/arm/boot/dts/sun6i-a31.dtsi                   |  86 +++-
 arch/arm/configs/sunxi_defconfig                   |   3 +
 drivers/clk/sunxi/clk-sunxi.c                      |  45 ++
 drivers/spi/Kconfig                                |   6 +
 drivers/spi/Makefile                               |   1 +
 drivers/spi/spi-sun6i.c                            | 473 +++++++++++++++++++++
 8 files changed, 630 insertions(+), 9 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/spi-sun6i.txt
 create mode 100644 drivers/spi/spi-sun6i.c

-- 
1.8.4.2

^ permalink raw reply

* [PATCH v3 1/5] clk: sunxi: Add support for PLL6 on the A31
From: Maxime Ripard @ 2014-01-31 10:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391165752-1819-1-git-send-email-maxime.ripard@free-electrons.com>

The A31 has a slightly different PLL6 clock. Add support for this new clock in
our driver.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 Documentation/devicetree/bindings/clock/sunxi.txt |  1 +
 drivers/clk/sunxi/clk-sunxi.c                     | 45 +++++++++++++++++++++++
 2 files changed, 46 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index c2cb762..954845c 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -11,6 +11,7 @@ Required properties:
 	"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
 	"allwinner,sun4i-pll5-clk" - for the PLL5 clock
 	"allwinner,sun4i-pll6-clk" - for the PLL6 clock
+	"allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
 	"allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock
 	"allwinner,sun4i-axi-clk" - for the AXI clock
 	"allwinner,sun4i-axi-gates-clk" - for the AXI gates
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index 659e4ea..990ad5d 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -249,7 +249,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
 	*n = DIV_ROUND_UP(div, (*k+1));
 }
 
+/**
+ * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6
+ * PLL6 rate is calculated as follows
+ * rate = parent_rate * n * (k + 1) / 2
+ * parent_rate is always 24Mhz
+ */
+
+static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate,
+				       u8 *n, u8 *k, u8 *m, u8 *p)
+{
+	u8 div;
+
+	/*
+	 * We always have 24MHz / 2, so we can just say that our
+	 * parent clock is 12MHz.
+	 */
+	parent_rate = parent_rate / 2;
+
+	/* Normalize value to a parent_rate multiple (24M / 2) */
+	div = *freq / parent_rate;
+	*freq = parent_rate * div;
+
+	/* we were called to round the frequency, we can now return */
+	if (n == NULL)
+		return;
+
+	*k = div / 32;
+	if (*k > 3)
+		*k = 3;
 
+	*n = DIV_ROUND_UP(div, (*k+1));
+}
 
 /**
  * sun4i_get_apb1_factors() - calculates m, p factors for APB1
@@ -416,6 +447,13 @@ static struct clk_factors_config sun4i_pll5_config = {
 	.kwidth = 2,
 };
 
+static struct clk_factors_config sun6i_a31_pll6_config = {
+	.nshift	= 8,
+	.nwidth = 5,
+	.kshift = 4,
+	.kwidth = 2,
+};
+
 static struct clk_factors_config sun4i_apb1_config = {
 	.mshift = 0,
 	.mwidth = 5,
@@ -457,6 +495,12 @@ static const struct factors_data sun4i_pll5_data __initconst = {
 	.getter = sun4i_get_pll5_factors,
 };
 
+static const struct factors_data sun6i_a31_pll6_data __initconst = {
+	.enable = 31,
+	.table = &sun6i_a31_pll6_config,
+	.getter = sun6i_a31_get_pll6_factors,
+};
+
 static const struct factors_data sun4i_apb1_data __initconst = {
 	.table = &sun4i_apb1_config,
 	.getter = sun4i_get_apb1_factors,
@@ -972,6 +1016,7 @@ free_clkdata:
 static const struct of_device_id clk_factors_match[] __initconst = {
 	{.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,},
 	{.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,},
+	{.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,},
 	{.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,},
 	{.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,},
 	{.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,},
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v3 2/5] ARM: sun6i: dt: Add PLL6 and SPI module clocks
From: Maxime Ripard @ 2014-01-31 10:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391165752-1819-1-git-send-email-maxime.ripard@free-electrons.com>

The module clocks in the A31 are still compatible with the A10 one. Add the SPI
module clocks and the PLL6 in the device tree to allow their use by the SPI
controllers.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 46 ++++++++++++++++++++++++++++++++--------
 1 file changed, 37 insertions(+), 9 deletions(-)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 5256ad9..0eea325 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -73,16 +73,12 @@
 			clocks = <&osc24M>;
 		};
 
-		/*
-		 * This is a dummy clock, to be used as placeholder on
-		 * other mux clocks when a specific parent clock is not
-		 * yet implemented. It should be dropped when the driver
-		 * is complete.
-		 */
-		pll6: pll6 {
+		pll6: clk at 01c20028 {
 			#clock-cells = <0>;
-			compatible = "fixed-clock";
-			clock-frequency = <0>;
+			compatible = "allwinner,sun6i-a31-pll6-clk";
+			reg = <0x01c20028 0x4>;
+			clocks = <&osc24M>;
+			clock-output-names = "pll6";
 		};
 
 		cpu: cpu at 01c20050 {
@@ -182,6 +178,38 @@
 					"apb2_uart1", "apb2_uart2", "apb2_uart3",
 					"apb2_uart4", "apb2_uart5";
 		};
+
+		spi0_clk: clk at 01c200a0 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-mod0-clk";
+			reg = <0x01c200a0 0x4>;
+			clocks = <&osc24M>, <&pll6>;
+			clock-output-names = "spi0";
+		};
+
+		spi1_clk: clk at 01c200a4 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-mod0-clk";
+			reg = <0x01c200a4 0x4>;
+			clocks = <&osc24M>, <&pll6>;
+			clock-output-names = "spi1";
+		};
+
+		spi2_clk: clk at 01c200a8 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-mod0-clk";
+			reg = <0x01c200a8 0x4>;
+			clocks = <&osc24M>, <&pll6>;
+			clock-output-names = "spi2";
+		};
+
+		spi3_clk: clk at 01c200ac {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-mod0-clk";
+			reg = <0x01c200ac 0x4>;
+			clocks = <&osc24M>, <&pll6>;
+			clock-output-names = "spi3";
+		};
 	};
 
 	soc at 01c00000 {
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v3 3/5] spi: sunxi: Add Allwinner A31 SPI controller driver
From: Maxime Ripard @ 2014-01-31 10:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391165752-1819-1-git-send-email-maxime.ripard@free-electrons.com>

The Allwinner A31 has a new SPI controller IP compared to the older Allwinner
SoCs.

It supports DMA, but the driver only does PIO for now, and DMA will be
supported eventually.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 .../devicetree/bindings/spi/spi-sun6i.txt          |  24 ++
 drivers/spi/Kconfig                                |   6 +
 drivers/spi/Makefile                               |   1 +
 drivers/spi/spi-sun6i.c                            | 473 +++++++++++++++++++++
 4 files changed, 504 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spi/spi-sun6i.txt
 create mode 100644 drivers/spi/spi-sun6i.c

diff --git a/Documentation/devicetree/bindings/spi/spi-sun6i.txt b/Documentation/devicetree/bindings/spi/spi-sun6i.txt
new file mode 100644
index 0000000..21de73d
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-sun6i.txt
@@ -0,0 +1,24 @@
+Allwinner A31 SPI controller
+
+Required properties:
+- compatible: Should be "allwinner,sun6i-a31-spi".
+- reg: Should contain register location and length.
+- interrupts: Should contain interrupt.
+- clocks: phandle to the clocks feeding the SPI controller. Two are
+          needed:
+  - "ahb": the gated AHB parent clock
+  - "mod": the parent module clock
+- clock-names: Must contain the clock names described just above
+- resets: phandle to the reset controller asserting this device in
+          reset
+
+Example:
+
+spi1: spi at 01c69000 {
+	compatible = "allwinner,sun6i-a31-spi";
+	reg = <0x01c69000 0x1000>;
+	interrupts = <0 66 4>;
+	clocks = <&ahb1_gates 21>, <&spi1_clk>;
+	clock-names = "ahb", "mod";
+	resets = <&ahb1_rst 21>;
+};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 5072b71..24f3b85 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -446,6 +446,12 @@ config SPI_SIRF
 	help
 	  SPI driver for CSR SiRFprimaII SoCs
 
+config SPI_SUN6I
+	tristate "Allwinner A31 SPI controller"
+	depends on ARCH_SUNXI || COMPILE_TEST
+	help
+	  This enables using the SPI controller on the Allwinner A31 SoCs.
+
 config SPI_MXS
 	tristate "Freescale MXS SPI controller"
 	depends on ARCH_MXS
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 95af48d..13b6ccf 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_SPI_SH_HSPI)		+= spi-sh-hspi.o
 obj-$(CONFIG_SPI_SH_MSIOF)		+= spi-sh-msiof.o
 obj-$(CONFIG_SPI_SH_SCI)		+= spi-sh-sci.o
 obj-$(CONFIG_SPI_SIRF)		+= spi-sirf.o
+obj-$(CONFIG_SPI_SUN6I)			+= spi-sun6i.o
 obj-$(CONFIG_SPI_TEGRA114)		+= spi-tegra114.o
 obj-$(CONFIG_SPI_TEGRA20_SFLASH)	+= spi-tegra20-sflash.o
 obj-$(CONFIG_SPI_TEGRA20_SLINK)		+= spi-tegra20-slink.o
diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
new file mode 100644
index 0000000..1747892
--- /dev/null
+++ b/drivers/spi/spi-sun6i.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2012 - 2014 Allwinner Tech
+ * Pan Nan <pannan@allwinnertech.com>
+ *
+ * Copyright (C) 2014 Maxime Ripard
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/workqueue.h>
+
+#include <linux/spi/spi.h>
+
+#define SUN6I_FIFO_DEPTH		128
+
+#define SUN6I_GBL_CTL_REG		0x04
+#define SUN6I_GBL_CTL_BUS_ENABLE		BIT(0)
+#define SUN6I_GBL_CTL_MASTER			BIT(1)
+#define SUN6I_GBL_CTL_TP			BIT(7)
+#define SUN6I_GBL_CTL_RST			BIT(31)
+
+#define SUN6I_TFR_CTL_REG		0x08
+#define SUN6I_TFR_CTL_CPHA			BIT(0)
+#define SUN6I_TFR_CTL_CPOL			BIT(1)
+#define SUN6I_TFR_CTL_SPOL			BIT(2)
+#define SUN6I_TFR_CTL_CS_MASK			0x3
+#define SUN6I_TFR_CTL_CS(cs)			(((cs) & SUN6I_TFR_CTL_CS_MASK) << 4)
+#define SUN6I_TFR_CTL_CS_MANUAL			BIT(6)
+#define SUN6I_TFR_CTL_CS_LEVEL			BIT(7)
+#define SUN6I_TFR_CTL_DHB			BIT(8)
+#define SUN6I_TFR_CTL_FBS			BIT(12)
+#define SUN6I_TFR_CTL_XCH			BIT(31)
+
+#define SUN6I_INT_CTL_REG		0x10
+#define SUN6I_INT_CTL_RF_OVF			BIT(8)
+#define SUN6I_INT_CTL_TC			BIT(12)
+
+#define SUN6I_INT_STA_REG		0x14
+
+#define SUN6I_FIFO_CTL_REG		0x18
+#define SUN6I_FIFO_CTL_RF_RST			BIT(15)
+#define SUN6I_FIFO_CTL_TF_RST			BIT(31)
+
+#define SUN6I_FIFO_STA_REG		0x1c
+#define SUN6I_FIFO_STA_RF_CNT_MASK		0x7f
+#define SUN6I_FIFO_STA_RF_CNT_BITS		0
+#define SUN6I_FIFO_STA_TF_CNT_MASK		0x7f
+#define SUN6I_FIFO_STA_TF_CNT_BITS		16
+
+#define SUN6I_CLK_CTL_REG		0x24
+#define SUN6I_CLK_CTL_CDR2_MASK			0xff
+#define SUN6I_CLK_CTL_CDR2(div)			(((div) & SUN6I_CLK_CTL_CDR2_MASK) << 0)
+#define SUN6I_CLK_CTL_CDR1_MASK			0xf
+#define SUN6I_CLK_CTL_CDR1(div)			(((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8)
+#define SUN6I_CLK_CTL_DRS			BIT(12)
+
+#define SUN6I_BURST_CNT_REG		0x30
+#define SUN6I_BURST_CNT(cnt)			((cnt) & 0xffffff)
+
+#define SUN6I_XMIT_CNT_REG		0x34
+#define SUN6I_XMIT_CNT(cnt)			((cnt) & 0xffffff)
+
+#define SUN6I_BURST_CTL_CNT_REG		0x38
+#define SUN6I_BURST_CTL_CNT_STC(cnt)		((cnt) & 0xffffff)
+
+#define SUN6I_TXDATA_REG		0x200
+#define SUN6I_RXDATA_REG		0x300
+
+struct sun6i_spi {
+	struct spi_master	*master;
+	void __iomem		*base_addr;
+	struct clk		*hclk;
+	struct clk		*mclk;
+	struct reset_control	*rstc;
+
+	struct completion	done;
+
+	const u8		*tx_buf;
+	u8			*rx_buf;
+	int			len;
+};
+
+static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg)
+{
+	return readl(sspi->base_addr + reg);
+}
+
+static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value)
+{
+	writel(value, sspi->base_addr + reg);
+}
+
+static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
+{
+	u32 reg, cnt;
+	u8 byte;
+
+	/* See how much data is available */
+	reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
+	reg &= SUN6I_FIFO_STA_RF_CNT_MASK;
+	cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS;
+
+	if (len > cnt)
+		len = cnt;
+
+	while (len--) {
+		byte = readb(sspi->base_addr + SUN6I_RXDATA_REG);
+		if (sspi->rx_buf)
+			*sspi->rx_buf++ = byte;
+	}
+}
+
+static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len)
+{
+	u8 byte;
+
+	if (len > sspi->len)
+		len = sspi->len;
+
+	while (len--) {
+		byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
+		writeb(byte, sspi->base_addr + SUN6I_TXDATA_REG);
+		sspi->len--;
+	}
+}
+
+static void sun6i_spi_set_cs(struct spi_device *spi, bool enable)
+{
+	struct sun6i_spi *sspi = spi_master_get_devdata(spi->master);
+	u32 reg;
+
+	reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
+	reg &= ~SUN6I_TFR_CTL_CS_MASK;
+	reg |= SUN6I_TFR_CTL_CS(spi->chip_select);
+
+	if (enable)
+		reg |= SUN6I_TFR_CTL_CS_LEVEL;
+	else
+		reg &= ~SUN6I_TFR_CTL_CS_LEVEL;
+
+	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
+}
+
+
+static int sun6i_spi_transfer_one(struct spi_master *master,
+				  struct spi_device *spi,
+				  struct spi_transfer *tfr)
+{
+	struct sun6i_spi *sspi = spi_master_get_devdata(master);
+	unsigned int mclk_rate, div, timeout;
+	unsigned int tx_len = 0;
+	int ret = 0;
+	u32 reg;
+
+	/* We don't support transfer larger than the FIFO */
+	if (tfr->len > SUN6I_FIFO_DEPTH)
+		return -EINVAL;
+
+	reinit_completion(&sspi->done);
+	sspi->tx_buf = tfr->tx_buf;
+	sspi->rx_buf = tfr->rx_buf;
+	sspi->len = tfr->len;
+
+	/* Clear pending interrupts */
+	sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0);
+
+	/* Reset FIFO */
+	sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
+			SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST);
+
+	/*
+	 * Setup the transfer control register: Chip Select,
+	 * polarities, etc.
+	 */
+	reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
+
+	if (spi->mode & SPI_CPOL)
+		reg |= SUN6I_TFR_CTL_CPOL;
+	else
+		reg &= ~SUN6I_TFR_CTL_CPOL;
+
+	if (spi->mode & SPI_CPHA)
+		reg |= SUN6I_TFR_CTL_CPHA;
+	else
+		reg &= ~SUN6I_TFR_CTL_CPHA;
+
+	if (spi->mode & SPI_LSB_FIRST)
+		reg |= SUN6I_TFR_CTL_FBS;
+	else
+		reg &= ~SUN6I_TFR_CTL_FBS;
+
+	/*
+	 * If it's a TX only transfer, we don't want to fill the RX
+	 * FIFO with bogus data
+	 */
+	if (sspi->rx_buf)
+		reg &= ~SUN6I_TFR_CTL_DHB;
+	else
+		reg |= SUN6I_TFR_CTL_DHB;
+
+	/* We want to control the chip select manually */
+	reg |= SUN6I_TFR_CTL_CS_MANUAL;
+
+	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
+
+	/* Ensure that we have a parent clock fast enough */
+	mclk_rate = clk_get_rate(sspi->mclk);
+	if (mclk_rate < (2 * spi->max_speed_hz)) {
+		clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
+		mclk_rate = clk_get_rate(sspi->mclk);
+	}
+
+	/*
+	 * Setup clock divider.
+	 *
+	 * We have two choices there. Either we can use the clock
+	 * divide rate 1, which is calculated thanks to this formula:
+	 * SPI_CLK = MOD_CLK / (2 ^ cdr)
+	 * Or we can use CDR2, which is calculated with the formula:
+	 * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
+	 * Wether we use the former or the latter is set through the
+	 * DRS bit.
+	 *
+	 * First try CDR2, and if we can't reach the expected
+	 * frequency, fall back to CDR1.
+	 */
+	div = mclk_rate / (2 * spi->max_speed_hz);
+	if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
+		if (div > 0)
+			div--;
+
+		reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
+	} else {
+		div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
+		reg = SUN6I_CLK_CTL_CDR1(div);
+	}
+
+	sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
+
+	/* Setup the transfer now... */
+	if (sspi->tx_buf)
+		tx_len = tfr->len;
+
+	/* Setup the counters */
+	sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len));
+	sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len));
+	sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG,
+			SUN6I_BURST_CTL_CNT_STC(tx_len));
+
+	/* Fill the TX FIFO */
+	sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH);
+
+	/* Enable the interrupts */
+	sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC);
+
+	/* Start the transfer */
+	reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
+	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
+
+	timeout = wait_for_completion_timeout(&sspi->done,
+					      msecs_to_jiffies(1000));
+	if (!timeout) {
+		ret = -ETIMEDOUT;
+		goto out;
+	}
+
+	sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH);
+
+out:
+	sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
+
+	return ret;
+}
+
+static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
+{
+	struct sun6i_spi *sspi = dev_id;
+	u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG);
+
+	/* Transfer complete */
+	if (status & SUN6I_INT_CTL_TC) {
+		sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
+		complete(&sspi->done);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int sun6i_spi_runtime_resume(struct device *dev)
+{
+	struct spi_master *master = dev_get_drvdata(dev);
+	struct sun6i_spi *sspi = spi_master_get_devdata(master);
+	int ret;
+
+	ret = clk_prepare_enable(sspi->hclk);
+	if (ret) {
+		dev_err(dev, "Couldn't enable AHB clock\n");
+		goto out;
+	}
+
+	ret = clk_prepare_enable(sspi->mclk);
+	if (ret) {
+		dev_err(dev, "Couldn't enable module clock\n");
+		goto err;
+	}
+
+	ret = reset_control_deassert(sspi->rstc);
+	if (ret) {
+		dev_err(dev, "Couldn't deassert the device from reset\n");
+		goto err2;
+	}
+
+	sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG,
+			SUN6I_GBL_CTL_BUS_ENABLE | SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP);
+
+	return 0;
+
+err2:
+	clk_disable_unprepare(sspi->mclk);
+err:
+	clk_disable_unprepare(sspi->hclk);
+out:
+	return ret;
+}
+
+static int sun6i_spi_runtime_suspend(struct device *dev)
+{
+	struct spi_master *master = dev_get_drvdata(dev);
+	struct sun6i_spi *sspi = spi_master_get_devdata(master);
+
+	reset_control_assert(sspi->rstc);
+	clk_disable_unprepare(sspi->mclk);
+	clk_disable_unprepare(sspi->hclk);
+
+	return 0;
+}
+
+static int sun6i_spi_probe(struct platform_device *pdev)
+{
+	struct spi_master *master;
+	struct sun6i_spi *sspi;
+	struct resource	*res;
+	int ret = 0, irq;
+
+	master = devm_spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi));
+	if (!master) {
+		dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, master);
+	sspi = spi_master_get_devdata(master);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	sspi->base_addr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(sspi->base_addr))
+		return PTR_ERR(sspi->base_addr);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "No spi IRQ specified\n");
+		return -ENXIO;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler,
+			       0, "sun6i-spi", sspi);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot request IRQ\n");
+		return ret;
+	}
+
+	sspi->master = master;
+	master->bus_num	= -1;
+	master->set_cs = sun6i_spi_set_cs;
+	master->transfer_one = sun6i_spi_transfer_one;
+	master->num_chipselect = 4;
+	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
+	master->dev.of_node = pdev->dev.of_node;
+	master->auto_runtime_pm = true;
+
+	sspi->hclk = devm_clk_get(&pdev->dev, "ahb");
+	if (IS_ERR(sspi->hclk)) {
+		dev_err(&pdev->dev, "Unable to acquire AHB clock\n");
+		return PTR_ERR(sspi->hclk);
+	}
+
+	sspi->mclk = devm_clk_get(&pdev->dev, "mod");
+	if (IS_ERR(sspi->mclk)) {
+		dev_err(&pdev->dev, "Unable to acquire module clock\n");
+		return PTR_ERR(sspi->mclk);
+	}
+
+	init_completion(&sspi->done);
+
+	sspi->rstc = devm_reset_control_get(&pdev->dev, NULL);
+	if (IS_ERR(sspi->rstc)) {
+		dev_err(&pdev->dev, "Couldn't get reset controller\n");
+		return PTR_ERR(sspi->rstc);
+	}
+
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev)) {
+		ret = sun6i_spi_runtime_resume(&pdev->dev);
+		if (ret) {
+			dev_err(&pdev->dev, "Couldn't resume the device\n");
+			return ret;
+		}
+	}
+
+	ret = devm_spi_register_master(&pdev->dev, master);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot register SPI master\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	if (!pm_runtime_enabled(&pdev->dev))
+		sun6i_spi_runtime_suspend(&pdev->dev);
+
+	pm_runtime_disable(&pdev->dev);
+	return ret;
+}
+
+static int sun6i_spi_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id sun6i_spi_match[] = {
+	{ .compatible = "allwinner,sun6i-a31-spi", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sun6i_spi_match);
+
+static const struct dev_pm_ops sun6i_spi_pm_ops = {
+	.runtime_resume		= sun6i_spi_runtime_resume,
+	.runtime_suspend	= sun6i_spi_runtime_suspend,
+};
+
+static struct platform_driver sun6i_spi_driver = {
+	.probe	= sun6i_spi_probe,
+	.remove	= sun6i_spi_remove,
+	.driver	= {
+		.name		= "sun6i-spi",
+		.owner		= THIS_MODULE,
+		.of_match_table	= sun6i_spi_match,
+		.pm		= &sun6i_spi_pm_ops,
+	},
+};
+module_platform_driver(sun6i_spi_driver);
+
+MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A31 SPI controller driver");
+MODULE_LICENSE("GPL");
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v3 4/5] ARM: sun6i: dt: Add SPI controllers to the A31 DTSI
From: Maxime Ripard @ 2014-01-31 10:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391165752-1819-1-git-send-email-maxime.ripard@free-electrons.com>

The A31 has 4 SPI controllers. Add them in the DTSI.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 0eea325..57af66f 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -340,6 +340,46 @@
 			status = "disabled";
 		};
 
+		spi0: spi at 01c68000 {
+			compatible = "allwinner,sun6i-a31-spi";
+			reg = <0x01c68000 0x1000>;
+			interrupts = <0 65 4>;
+			clocks = <&ahb1_gates 20>, <&spi0_clk>;
+			clock-names = "ahb", "mod";
+			resets = <&ahb1_rst 20>;
+			status = "disabled";
+		};
+
+		spi1: spi at 01c69000 {
+			compatible = "allwinner,sun6i-a31-spi";
+			reg = <0x01c69000 0x1000>;
+			interrupts = <0 66 4>;
+			clocks = <&ahb1_gates 21>, <&spi1_clk>;
+			clock-names = "ahb", "mod";
+			resets = <&ahb1_rst 21>;
+			status = "disabled";
+		};
+
+		spi2: spi at 01c6a000 {
+			compatible = "allwinner,sun6i-a31-spi";
+			reg = <0x01c6a000 0x1000>;
+			interrupts = <0 67 4>;
+			clocks = <&ahb1_gates 22>, <&spi2_clk>;
+			clock-names = "ahb", "mod";
+			resets = <&ahb1_rst 22>;
+			status = "disabled";
+		};
+
+		spi3: spi at 01c6b000 {
+			compatible = "allwinner,sun6i-a31-spi";
+			reg = <0x01c6b000 0x1000>;
+			interrupts = <0 68 4>;
+			clocks = <&ahb1_gates 23>, <&spi3_clk>;
+			clock-names = "ahb", "mod";
+			resets = <&ahb1_rst 23>;
+			status = "disabled";
+		};
+
 		gic: interrupt-controller at 01c81000 {
 			compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic";
 			reg = <0x01c81000 0x1000>,
-- 
1.8.4.2

^ permalink raw reply related

* [PATCH v3 5/5] ARM: sunxi: Enable A31 SPI and SID in the defconfig
From: Maxime Ripard @ 2014-01-31 10:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391165752-1819-1-git-send-email-maxime.ripard@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/configs/sunxi_defconfig | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 3e2259b..b5df4a5 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -24,6 +24,7 @@ CONFIG_IP_PNP_BOOTP=y
 # CONFIG_WIRELESS is not set
 CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_EEPROM_SUNXI_SID=y
 CONFIG_NETDEVICES=y
 CONFIG_SUN4I_EMAC=y
 # CONFIG_NET_CADENCE is not set
@@ -48,6 +49,8 @@ CONFIG_I2C=y
 # CONFIG_I2C_COMPAT is not set
 CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_MV64XXX=y
+CONFIG_SPI=y
+CONFIG_SPI_SUN6I=y
 CONFIG_GPIO_SYSFS=y
 # CONFIG_HWMON is not set
 CONFIG_WATCHDOG=y
-- 
1.8.4.2

^ permalink raw reply related

* [RFC PATCH 0/2] dmaengine: at_hdmac: fix locking according to slave DMA requirements
From: Jouko Haapaluoma @ 2014-01-31 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1390832343.git.nicolas.ferre@atmel.com>

Hi

Thanks for the patches. We got the DMA working for now but the tasklet_disable() and tasklet_enable() had to be removed from atc_control().

If the device driver calls dmaengine_terminate_all() from the callback (like in our previous deadlock example), the tasklet_disable() will cause
another deadlock because the tasklet will then wait for itself to close.

The tasklet_disable() seems to be used to ensure that no tasklet is running when terminating the DMA transfers. This prevents the terminate_all from
happening in between the critical sections in the tasklet which are locked with atchan->lock. Maybe the tasklet needs to be implemented so that it can
detect and recover if the terminate_all was called in between the critical sections in the tasklet?

BR,
Jouko Haapaluoma

^ permalink raw reply

* [PATCH] pinctrl: sirf: correct the pin index of ac97_pins group
From: Linus Walleij @ 2014-01-31 11:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390802489-2535-1-git-send-email-21cnbao@gmail.com>

On Mon, Jan 27, 2014 at 7:01 AM, Barry Song <21cnbao@gmail.com> wrote:

> From: Qipan Li <Qipan.Li@csr.com>
>
> according to datasheet and ac97_muxmask assignment, ac97_pins should be
> corrected.
>
> Signed-off-by: Qipan Li <Qipan.Li@csr.com>
> Signed-off-by: Barry Song <Baohua.Song@csr.com>

Patch applied for fixes.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH v2] pwm: add CSR SiRFSoC PWM driver
From: Barry Song @ 2014-01-31 12:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201401302049.27521.arnd@arndb.de>

2014-01-31 Arnd Bergmann <arnd@arndb.de>:
> On Thursday 30 January 2014, Barry Song wrote:
>> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
>> index 7acab93..0a252f8 100644
>> --- a/drivers/pwm/Kconfig
>> +++ b/drivers/pwm/Kconfig
>> @@ -166,6 +166,15 @@ config PWM_SAMSUNG
>>         To compile this driver as a module, choose M here: the module
>>         will be called pwm-samsung.
>>
>> +config PWM_SIRF
>> +     tristate "SiRF PWM support"
>> +     depends on ARCH_SIRF
>> +     help
>
> please make this "depends on ARCH_SIRF || COMPILE_TEST" if you can, so we
> can build it on other platforms for test purposes. If you do this, you
> have to list the full set of dependencies, so probably another
> "depends on HAVE_CLK".

sounds good. it seems we can have this for other PWM drivers as well?

>
>> +
>> +#define to_sirf_chip(chip)   container_of(chip, struct sirf_pwm, chip)
>> +
>> +static unsigned int sirf_pwm_ns_to_cycles(struct pwm_chip *chip, unsigned int time_ns)
>> +{
>> +     u64 dividend;
>> +     unsigned int cycle;
>> +     /*
>> +      * on SiRFSoC, OSC input is const, we use it as the source to generate
>> +      * PWM wave
>> +      */
>> +#define SRC_OSC_RATE 26000000ULL
>> +     dividend = SRC_OSC_RATE * time_ns + NSEC_PER_SEC / 2;
>> +     do_div(dividend, NSEC_PER_SEC);
>> +
>> +     cycle = dividend & 0xFFFFFFFFUL;
>> +
>> +     return cycle > 1 ? cycle : 1;
>> +}
>> +
>
> Is SRC_OSC_RATE the rate of spwm->clk? If so, it would be nice to just call
> clk_get_rate() here, in case you ever have a chip with a different rate.
>

SRC_OSC_RATE is the fixed frequency of crystal oscillator, but
spwm->clk comes from the IO bus. the design is a little strange, pwm
channels don't use the clock of PWM controller to generate
period/duty, but use other sources.

> This is a very nice driver otherwise!
>
>         Arnd

-barry

^ permalink raw reply

* [PATCH] ARM: zynq: Reserve not DMAable space in front of the kernel
From: Michal Simek @ 2014-01-31 12:08 UTC (permalink / raw)
  To: linux-arm-kernel

Reserve space from 0x0 - __pa(swapper_pg_dir),
if kernel is loaded from 0, which is not DMAable.
It is causing problem with MMC driver and others
which want to add dma buffers to this space.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

Jason: I don't think it is worth to bring 0x8000 magic
value and count minimum from it and phys_addr of swapper_pg_dir.
Full 512k of memory shouldn't be used by DMA.

---
 arch/arm/mach-zynq/common.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 1db2a5ca..8c09a83 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -25,6 +25,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/of.h>
+#include <linux/memblock.h>
 #include <linux/irqchip.h>
 #include <linux/irqchip/arm-gic.h>

@@ -41,6 +42,18 @@

 void __iomem *zynq_scu_base;

+/**
+ * zynq_memory_init - Initialize special memory
+ *
+ * We need to stop things allocating the low memory as DMA can't work in
+ * the 1st 512K of memory.
+ */
+static void __init zynq_memory_init(void)
+{
+	if (!__pa(PAGE_OFFSET))
+		memblock_reserve(__pa(PAGE_OFFSET), __pa(swapper_pg_dir));
+}
+
 static struct platform_device zynq_cpuidle_device = {
 	.name = "cpuidle-zynq",
 };
@@ -117,5 +130,6 @@ DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
 	.init_machine	= zynq_init_machine,
 	.init_time	= zynq_timer_init,
 	.dt_compat	= zynq_dt_match,
+	.reserve	= zynq_memory_init,
 	.restart	= zynq_system_reset,
 MACHINE_END
--
1.8.2.3

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140131/7214a18f/attachment.sig>

^ permalink raw reply related

* [PATCH 0/3] spi: core: Introduce devm_spi_alloc_master
From: Mark Brown @ 2014-01-31 12:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391163792-21819-1-git-send-email-maxime.ripard@free-electrons.com>

On Fri, Jan 31, 2014 at 11:23:09AM +0100, Maxime Ripard wrote:

> This patchset introduces a devm_spi_alloc_master to the spi core. While most of
> the drivers have a spi_master_put call in the probe, a lot of them using the
> devm_spi_register_master function are missing it in the remove function,
> leading to leaked resources.

This seems confusing - the idea here is that if we've handed the device
off to the managed function then the managed function deals with
destroying it.  Note that spi_alloc_master() says that the put is only
required after errors adding the device (which would be the expected
behaviour if you look at other APIs).  Looking at the code I think there
is an issue here but I'm not at all clear that this is the best fix.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140131/b7e655ca/attachment.sig>

^ permalink raw reply

* [PATCH 1/4] ARM: STi: add stid127 soc support
From: srinivas kandagatla @ 2014-01-31 12:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201401301939.08302.arnd@arndb.de>

Hi Arnd,
On 30/01/14 18:39, Arnd Bergmann wrote:
> Actually reading the code in this file shows that the L2 cache
> initialization is the only nonstandard thing in there. We should
> really find a way to get rid of the entire function.
I think this will get rid of lot of code left in board-dt.

> 
> Sorry if I missed the initial review, but can you explain
> why this is needed to start with?
On ST SoCs the default value for L2 AUX_CTRL register is 0x0, so we  set
the way-size explicit here.

Thanks,
srini

^ 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