LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 10/18] powerpc: Support endian agnostic MMIO
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh; +Cc: paulus, Ian Munsie
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch maps the MMIO functions for 32bit PowerPC to their
appropriate instructions depending on CPU endianness.

The macros used to create the corresponding inline functions are also
renamed by this patch. Previously they had BE or LE in their names which
was misleading - they had nothing to do with endianness, but actually
created different instruction forms so their new names reflect the
instruction form they are creating (D-Form and X-Form).

Little endian 64bit PowerPC is not supported, so the lack of mappings
(and corresponding breakage) for that case is intentional to bring the
attention of anyone doing a 64bit little endian port. 64bit big endian
is unaffected.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/include/asm/io.h |   51 ++++++++++++++++++++++++++--------------
 1 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h
index 001f2f1..fd8e922 100644
--- a/arch/powerpc/include/asm/io.h
+++ b/arch/powerpc/include/asm/io.h
@@ -97,7 +97,7 @@ extern resource_size_t isa_mem_base;
 
 /* gcc 4.0 and older doesn't have 'Z' constraint */
 #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ == 0)
-#define DEF_MMIO_IN_LE(name, size, insn)				\
+#define DEF_MMIO_IN_X(name, size, insn)				\
 static inline u##size name(const volatile u##size __iomem *addr)	\
 {									\
 	u##size ret;							\
@@ -106,7 +106,7 @@ static inline u##size name(const volatile u##size __iomem *addr)	\
 	return ret;							\
 }
 
-#define DEF_MMIO_OUT_LE(name, size, insn) 				\
+#define DEF_MMIO_OUT_X(name, size, insn) 				\
 static inline void name(volatile u##size __iomem *addr, u##size val)	\
 {									\
 	__asm__ __volatile__("sync;"#insn" %1,0,%2"			\
@@ -114,7 +114,7 @@ static inline void name(volatile u##size __iomem *addr, u##size val)	\
 	IO_SET_SYNC_FLAG();						\
 }
 #else /* newer gcc */
-#define DEF_MMIO_IN_LE(name, size, insn)				\
+#define DEF_MMIO_IN_X(name, size, insn)				\
 static inline u##size name(const volatile u##size __iomem *addr)	\
 {									\
 	u##size ret;							\
@@ -123,7 +123,7 @@ static inline u##size name(const volatile u##size __iomem *addr)	\
 	return ret;							\
 }
 
-#define DEF_MMIO_OUT_LE(name, size, insn) 				\
+#define DEF_MMIO_OUT_X(name, size, insn) 				\
 static inline void name(volatile u##size __iomem *addr, u##size val)	\
 {									\
 	__asm__ __volatile__("sync;"#insn" %1,%y0"			\
@@ -132,7 +132,7 @@ static inline void name(volatile u##size __iomem *addr, u##size val)	\
 }
 #endif
 
-#define DEF_MMIO_IN_BE(name, size, insn)				\
+#define DEF_MMIO_IN_D(name, size, insn)				\
 static inline u##size name(const volatile u##size __iomem *addr)	\
 {									\
 	u##size ret;							\
@@ -141,7 +141,7 @@ static inline u##size name(const volatile u##size __iomem *addr)	\
 	return ret;							\
 }
 
-#define DEF_MMIO_OUT_BE(name, size, insn)				\
+#define DEF_MMIO_OUT_D(name, size, insn)				\
 static inline void name(volatile u##size __iomem *addr, u##size val)	\
 {									\
 	__asm__ __volatile__("sync;"#insn"%U0%X0 %1,%0"			\
@@ -149,22 +149,37 @@ static inline void name(volatile u##size __iomem *addr, u##size val)	\
 	IO_SET_SYNC_FLAG();						\
 }
 
+DEF_MMIO_IN_D(in_8,     8, lbz);
+DEF_MMIO_OUT_D(out_8,   8, stb);
 
-DEF_MMIO_IN_BE(in_8,     8, lbz);
-DEF_MMIO_IN_BE(in_be16, 16, lhz);
-DEF_MMIO_IN_BE(in_be32, 32, lwz);
-DEF_MMIO_IN_LE(in_le16, 16, lhbrx);
-DEF_MMIO_IN_LE(in_le32, 32, lwbrx);
+#ifdef __BIG_ENDIAN__
+DEF_MMIO_IN_D(in_be16, 16, lhz);
+DEF_MMIO_IN_D(in_be32, 32, lwz);
+DEF_MMIO_IN_X(in_le16, 16, lhbrx);
+DEF_MMIO_IN_X(in_le32, 32, lwbrx);
 
-DEF_MMIO_OUT_BE(out_8,     8, stb);
-DEF_MMIO_OUT_BE(out_be16, 16, sth);
-DEF_MMIO_OUT_BE(out_be32, 32, stw);
-DEF_MMIO_OUT_LE(out_le16, 16, sthbrx);
-DEF_MMIO_OUT_LE(out_le32, 32, stwbrx);
+DEF_MMIO_OUT_D(out_be16, 16, sth);
+DEF_MMIO_OUT_D(out_be32, 32, stw);
+DEF_MMIO_OUT_X(out_le16, 16, sthbrx);
+DEF_MMIO_OUT_X(out_le32, 32, stwbrx);
+#else
+DEF_MMIO_IN_X(in_be16, 16, lhbrx);
+DEF_MMIO_IN_X(in_be32, 32, lwbrx);
+DEF_MMIO_IN_D(in_le16, 16, lhz);
+DEF_MMIO_IN_D(in_le32, 32, lwz);
+
+DEF_MMIO_OUT_X(out_be16, 16, sthbrx);
+DEF_MMIO_OUT_X(out_be32, 32, stwbrx);
+DEF_MMIO_OUT_D(out_le16, 16, sth);
+DEF_MMIO_OUT_D(out_le32, 32, stw);
+#endif /* __BIG_ENDIAN */
 
 #ifdef __powerpc64__
-DEF_MMIO_OUT_BE(out_be64, 64, std);
-DEF_MMIO_IN_BE(in_be64, 64, ld);
+
+#ifndef __LITTLE_ENDIAN__
+DEF_MMIO_OUT_D(out_be64, 64, std);
+DEF_MMIO_IN_D(in_be64, 64, ld);
+#endif
 
 /* There is no asm instructions for 64 bits reverse loads and stores */
 static inline u64 in_le64(const volatile u64 __iomem *addr)
-- 
1.7.1

^ permalink raw reply related

* [PATCH 11/18] powerpc: Make assembly endian agnostic when accessing 64bit values
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: paulus, Ian Munsie, Matthew McClintock, Torez Smith
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

The 32bit PowerPC ABI states that when passing arguments and return
values via registers a value of type long long is stored in pairs of
registers as follows:

The lower addressed word is stored in the next available odd numbered
register and the higher addressed value is stored in register+1.

i.e. the values will either be stored in the next available of:
r3/r4, r5/r6, r7/r8 or r9/r10

Since the lower addressed value must be in the lower register number we
have an endianness issue and need to treat this specially in any
assembly that is passed or returns a 64bit value.

This patch introduces some aliases in ppc_asm.h which will select the
appropriate register from the pair depending on the CPU endianness.
There are in the form of r34l for the low word from the r3/r4 pair and
r34h for the high word and so on for the remaining register pairs.

It also introduces p64l and p64h which can be used to select the
appropriate offset whenever loading a 32bit word while referring to the
address of a 64bit value. For instance if r3 contains the address of a
64bit value the following assembly would load the high word into r5 and
the low word into r6 regardless of endianness:
	lwz	r5,p64h(r3)
	lwz	r6,p64l(r3)

Finally, the patch also alters the functions in misc_32.S that take
64bit arguments to use these new accessors to work on the little endian
PowerPC architecture:

mulhdu, __div64_32, __ashrdi3, __ashldi3, __lshrdi3 and __ucmpdi2

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/include/asm/ppc_asm.h |   24 ++++++++++++
 arch/powerpc/kernel/misc_32.S      |   72 ++++++++++++++++++------------------
 arch/powerpc/lib/div64.S           |    8 ++--
 3 files changed, 64 insertions(+), 40 deletions(-)

diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h
index 9821006..6929483 100644
--- a/arch/powerpc/include/asm/ppc_asm.h
+++ b/arch/powerpc/include/asm/ppc_asm.h
@@ -510,6 +510,30 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
 #define	r30	30
 #define	r31	31
 
+/* Endian agnostic accessors for 64 bit values passed and returned in GPRs */
+#ifdef __BIG_ENDIAN__
+#define r34l	r4
+#define r34h	r3
+#define r56l	r6
+#define r56h	r5
+#define r78l	r8
+#define r78h	r7
+
+/* Endian agnostic accessors for pointer offsets to 64 bit values */
+#define p64l	4
+#define p64h	0
+#else
+#define r34l	r3
+#define r34h	r4
+#define r56l	r5
+#define r56h	r6
+#define r78l	r7
+#define r78h	r8
+
+#define p64l	0
+#define p64h	4
+#endif
+
 
 /* Floating Point Registers (FPRs) */
 
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index a7a570d..6c40079 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -60,27 +60,27 @@ _GLOBAL(call_handle_irq)
  * This returns the high 64 bits of the product of two 64-bit numbers.
  */
 _GLOBAL(mulhdu)
-	cmpwi	r6,0
-	cmpwi	cr1,r3,0
-	mr	r10,r4
-	mulhwu	r4,r4,r5
+	cmpwi	r56l,0
+	cmpwi	cr1,r34h,0
+	mr	r10,r34l
+	mulhwu	r34l,r34l,r56h
 	beq	1f
-	mulhwu	r0,r10,r6
-	mullw	r7,r10,r5
+	mulhwu	r0,r10,r56l
+	mullw	r7,r10,r56h
 	addc	r7,r0,r7
-	addze	r4,r4
+	addze	r34l,r34l
 1:	beqlr	cr1		/* all done if high part of A is 0 */
-	mr	r10,r3
-	mullw	r9,r3,r5
-	mulhwu	r3,r3,r5
+	mr	r10,r34h
+	mullw	r9,r34h,r56h
+	mulhwu	r34h,r34h,r56h
 	beq	2f
-	mullw	r0,r10,r6
-	mulhwu	r8,r10,r6
+	mullw	r0,r10,r56l
+	mulhwu	r8,r10,r56l
 	addc	r7,r0,r7
-	adde	r4,r4,r8
-	addze	r3,r3
-2:	addc	r4,r4,r9
-	addze	r3,r3
+	adde	r34l,r34l,r8
+	addze	r34h,r34h
+2:	addc	r34l,r34l,r9
+	addze	r34h,r34h
 	blr
 
 /*
@@ -606,37 +606,37 @@ _GLOBAL(atomic_set_mask)
  */
 _GLOBAL(__ashrdi3)
 	subfic	r6,r5,32
-	srw	r4,r4,r5	# LSW = count > 31 ? 0 : LSW >> count
+	srw	r34l,r34l,r5	# LSW = count > 31 ? 0 : LSW >> count
 	addi	r7,r5,32	# could be xori, or addi with -32
-	slw	r6,r3,r6	# t1 = count > 31 ? 0 : MSW << (32-count)
+	slw	r6,r34h,r6	# t1 = count > 31 ? 0 : MSW << (32-count)
 	rlwinm	r8,r7,0,32	# t3 = (count < 32) ? 32 : 0
-	sraw	r7,r3,r7	# t2 = MSW >> (count-32)
-	or	r4,r4,r6	# LSW |= t1
+	sraw	r7,r34h,r7	# t2 = MSW >> (count-32)
+	or	r34l,r34l,r6	# LSW |= t1
 	slw	r7,r7,r8	# t2 = (count < 32) ? 0 : t2
-	sraw	r3,r3,r5	# MSW = MSW >> count
-	or	r4,r4,r7	# LSW |= t2
+	sraw	r34h,r34h,r5	# MSW = MSW >> count
+	or	r34l,r34l,r7	# LSW |= t2
 	blr
 
 _GLOBAL(__ashldi3)
 	subfic	r6,r5,32
-	slw	r3,r3,r5	# MSW = count > 31 ? 0 : MSW << count
+	slw	r34h,r34h,r5	# MSW = count > 31 ? 0 : MSW << count
 	addi	r7,r5,32	# could be xori, or addi with -32
-	srw	r6,r4,r6	# t1 = count > 31 ? 0 : LSW >> (32-count)
-	slw	r7,r4,r7	# t2 = count < 32 ? 0 : LSW << (count-32)
-	or	r3,r3,r6	# MSW |= t1
-	slw	r4,r4,r5	# LSW = LSW << count
-	or	r3,r3,r7	# MSW |= t2
+	srw	r6,r34l,r6	# t1 = count > 31 ? 0 : LSW >> (32-count)
+	slw	r7,r34l,r7	# t2 = count < 32 ? 0 : LSW << (count-32)
+	or	r34h,r34h,r6	# MSW |= t1
+	slw	r34l,r34l,r5	# LSW = LSW << count
+	or	r34h,r34h,r7	# MSW |= t2
 	blr
 
 _GLOBAL(__lshrdi3)
 	subfic	r6,r5,32
-	srw	r4,r4,r5	# LSW = count > 31 ? 0 : LSW >> count
+	srw	r34l,r34l,r5	# LSW = count > 31 ? 0 : LSW >> count
 	addi	r7,r5,32	# could be xori, or addi with -32
-	slw	r6,r3,r6	# t1 = count > 31 ? 0 : MSW << (32-count)
-	srw	r7,r3,r7	# t2 = count < 32 ? 0 : MSW >> (count-32)
-	or	r4,r4,r6	# LSW |= t1
-	srw	r3,r3,r5	# MSW = MSW >> count
-	or	r4,r4,r7	# LSW |= t2
+	slw	r6,r34h,r6	# t1 = count > 31 ? 0 : MSW << (32-count)
+	srw	r7,r34h,r7	# t2 = count < 32 ? 0 : MSW >> (count-32)
+	or	r34l,r34l,r6	# LSW |= t1
+	srw	r34h,r34h,r5	# MSW = MSW >> count
+	or	r34l,r34l,r7	# LSW |= t2
 	blr
 
 /*
@@ -644,10 +644,10 @@ _GLOBAL(__lshrdi3)
  * Returns 0 if a < b, 1 if a == b, 2 if a > b.
  */
 _GLOBAL(__ucmpdi2)
-	cmplw	r3,r5
+	cmplw	r34h,r56h
 	li	r3,1
 	bne	1f
-	cmplw	r4,r6
+	cmplw	r34l,r56l
 	beqlr
 1:	li	r3,0
 	bltlr
diff --git a/arch/powerpc/lib/div64.S b/arch/powerpc/lib/div64.S
index 83d9832..12f2da4 100644
--- a/arch/powerpc/lib/div64.S
+++ b/arch/powerpc/lib/div64.S
@@ -17,8 +17,8 @@
 #include <asm/processor.h>
 
 _GLOBAL(__div64_32)
-	lwz	r5,0(r3)	# get the dividend into r5/r6
-	lwz	r6,4(r3)
+	lwz	r5,p64h(r3)	# get the dividend into r5/r6
+	lwz	r6,p64l(r3)
 	cmplw	r5,r4
 	li	r7,0
 	li	r8,0
@@ -53,7 +53,7 @@ _GLOBAL(__div64_32)
 	mullw	r10,r0,r4	# and get the remainder
 	add	r8,r8,r0
 	subf	r6,r10,r6
-4:	stw	r7,0(r3)	# return the quotient in *r3
-	stw	r8,4(r3)
+4:	stw	r7,p64h(r3)	# return the quotient in *r3
+	stw	r8,p64l(r3)
 	mr	r3,r6		# return the remainder in r3
 	blr
-- 
1.7.1

^ permalink raw reply related

* [PATCH 12/18] powerpc 44x: Handle TLB miss regardless of endianness
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Dave Kleikamp, paulus, Ian Munsie, Torez Smith
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

On the 44x we use 64bit page table entries, but the CPU is only 32bit.
When a PTE is loaded during a TLB miss each half is loaded into
different registers, so we need to reverse the offsets if the CPU is
running in little endian mode.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/kernel/head_44x.S |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index 6198733..d4c144f 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -258,8 +258,8 @@ interrupt_base:
 
 	/* Compute pte address */
 	rlwimi  r12, r10, PPC44x_PTE_ADD_SHIFT, PPC44x_PTE_ADD_MASK_BIT, 28
-	lwz	r11, 0(r12)		/* Get high word of pte entry */
-	lwz	r12, 4(r12)		/* Get low word of pte entry */
+	lwz	r11, p64h(r12)		/* Get high word of pte entry */
+	lwz	r12, p64l(r12)		/* Get low word of pte entry */
 
 	lis	r10,tlb_44x_index@ha
 
@@ -354,8 +354,8 @@ tlb_44x_patch_hwater_D:
 
 	/* Compute pte address */
 	rlwimi	r12, r10, PPC44x_PTE_ADD_SHIFT, PPC44x_PTE_ADD_MASK_BIT, 28
-	lwz	r11, 0(r12)		/* Get high word of pte entry */
-	lwz	r12, 4(r12)		/* Get low word of pte entry */
+	lwz	r11, p64h(r12)		/* Get high word of pte entry */
+	lwz	r12, p64l(r12)		/* Get low word of pte entry */
 
 	lis	r10,tlb_44x_index@ha
 
-- 
1.7.1

^ permalink raw reply related

* [PATCH 16/18] powerpc: Fix endianness issues in alignment handler
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Andreas Schwab, Michael Neuling, paulus, Ian Munsie,
	Anton Blanchard
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch reverses the order of the high and low bits in the alignment
handler on little endian, which should be enough to fix any alignment
exceptions.

Please note that this patch is largely untested.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/kernel/align.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index 8184ee9..fc357c6 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -712,12 +712,22 @@ int fix_alignment(struct pt_regs *regs)
 		double dd;
 		unsigned char v[8];
 		struct {
+#ifdef __LITTLE_ENDIAN__
+			int	 low32;
+			unsigned hi32;
+#else
 			unsigned hi32;
 			int	 low32;
+#endif
 		} x32;
 		struct {
+#ifdef __LITTLE_ENDIAN__
+			short	      low16;
+			unsigned char hi48[6];
+#else
 			unsigned char hi48[6];
 			short	      low16;
+#endif
 		} x16;
 	} data;
 
-- 
1.7.1

^ permalink raw reply related

* [PATCH 15/18] mtd: Fix endianness issues from device tree
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Jason Gunthorpe, Artem Bityutskiy, H Hartley Sweeten,
	Julia Lawall, Ian Munsie, linux-mtd, Sean MacLennan, Tejun Heo,
	paulus, Steve Deiters, Anatolij Gustschin, David Woodhouse,
	David S. Miller
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

This patch adds the appropriate conversions to correct the endianness
issues in the MTD driver whenever it accesses the device tree (which is
always big endian).

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 drivers/mtd/maps/physmap_of.c  |   14 +++++++-------
 drivers/mtd/nand/fsl_upm.c     |    8 ++++----
 drivers/mtd/nand/mpc5121_nfc.c |    4 ++--
 drivers/mtd/nand/ndfc.c        |    8 ++++----
 drivers/mtd/ofpart.c           |    6 +++---
 5 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index fe63f6b..ec4011c 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -50,7 +50,7 @@ static int parse_obsolete_partitions(struct platform_device *dev,
 {
 	int i, plen, nr_parts;
 	const struct {
-		u32 offset, len;
+		__be32 offset, len;
 	} *part;
 	const char *names;
 
@@ -69,9 +69,9 @@ static int parse_obsolete_partitions(struct platform_device *dev,
 	names = of_get_property(dp, "partition-names", &plen);
 
 	for (i = 0; i < nr_parts; i++) {
-		info->parts[i].offset = part->offset;
-		info->parts[i].size   = part->len & ~1;
-		if (part->len & 1) /* bit 0 set signifies read only partition */
+		info->parts[i].offset = be32_to_cpu(part->offset);
+		info->parts[i].size   = be32_to_cpu(part->len) & ~1;
+		if (be32_to_cpu(part->len) & 1) /* bit 0 set signifies read only partition */
 			info->parts[i].mask_flags = MTD_WRITEABLE;
 
 		if (names && (plen > 0)) {
@@ -226,11 +226,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
 	struct resource res;
 	struct of_flash *info;
 	const char *probe_type = match->data;
-	const u32 *width;
+	const __be32 *width;
 	int err;
 	int i;
 	int count;
-	const u32 *p;
+	const __be32 *p;
 	int reg_tuple_size;
 	struct mtd_info **mtd_list = NULL;
 	resource_size_t res_size;
@@ -294,7 +294,7 @@ static int __devinit of_flash_probe(struct platform_device *dev,
 		info->list[i].map.name = dev_name(&dev->dev);
 		info->list[i].map.phys = res.start;
 		info->list[i].map.size = res_size;
-		info->list[i].map.bankwidth = *width;
+		info->list[i].map.bankwidth = be32_to_cpup(width);
 
 		err = -ENOMEM;
 		info->list[i].map.virt = ioremap(info->list[i].map.phys,
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index 4eff8b2..8da9014 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -222,7 +222,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
 {
 	struct fsl_upm_nand *fun;
 	struct resource io_res;
-	const uint32_t *prop;
+	const __be32 *prop;
 	int rnb_gpio;
 	int ret;
 	int size;
@@ -270,7 +270,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
 			goto err1;
 		}
 		for (i = 0; i < fun->mchip_count; i++)
-			fun->mchip_offsets[i] = prop[i];
+			fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
 	} else {
 		fun->mchip_count = 1;
 	}
@@ -295,13 +295,13 @@ static int __devinit fun_probe(struct platform_device *ofdev,
 
 	prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL);
 	if (prop)
-		fun->chip_delay = *prop;
+		fun->chip_delay = be32_to_cpup(prop);
 	else
 		fun->chip_delay = 50;
 
 	prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size);
 	if (prop && size == sizeof(uint32_t))
-		fun->wait_flags = *prop;
+		fun->wait_flags = be32_to_cpup(prop);
 	else
 		fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
 				  FSL_UPM_WAIT_WRITE_BYTE;
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index df0c1da..aeb27a1 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -660,7 +660,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
 #endif
 	struct nand_chip *chip;
 	unsigned long regs_paddr, regs_size;
-	const uint *chips_no;
+	const __be32 *chips_no;
 	int resettime = 0;
 	int retval = 0;
 	int rev, len;
@@ -803,7 +803,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
 	}
 
 	/* Detect NAND chips */
-	if (nand_scan(mtd, *chips_no)) {
+	if (nand_scan(mtd, be32_to_cpup(chips_no))) {
 		dev_err(dev, "NAND Flash not found !\n");
 		devm_free_irq(dev, prv->irq, mtd);
 		retval = -ENXIO;
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 510554e..c9ae0a5 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -229,7 +229,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
 				const struct of_device_id *match)
 {
 	struct ndfc_controller *ndfc = &ndfc_ctrl;
-	const u32 *reg;
+	const __be32 *reg;
 	u32 ccr;
 	int err, len;
 
@@ -244,7 +244,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
 		dev_err(&ofdev->dev, "unable read reg property (%d)\n", len);
 		return -ENOENT;
 	}
-	ndfc->chip_select = reg[0];
+	ndfc->chip_select = be32_to_cpu(reg[0]);
 
 	ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0);
 	if (!ndfc->ndfcbase) {
@@ -257,7 +257,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
 	/* It is ok if ccr does not exist - just default to 0 */
 	reg = of_get_property(ofdev->dev.of_node, "ccr", NULL);
 	if (reg)
-		ccr |= *reg;
+		ccr |= be32_to_cpup(reg);
 
 	out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
 
@@ -265,7 +265,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
 	reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL);
 	if (reg) {
 		int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
-		out_be32(ndfc->ndfcbase + offset, *reg);
+		out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg));
 	}
 
 	err = ndfc_chip_init(ndfc, ofdev->dev.of_node);
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index 8bf7dc6..a996718 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -44,7 +44,7 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
 	pp = NULL;
 	i = 0;
 	while ((pp = of_get_next_child(node, pp))) {
-		const u32 *reg;
+		const __be32 *reg;
 		int len;
 
 		reg = of_get_property(pp, "reg", &len);
@@ -53,8 +53,8 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
 			continue;
 		}
 
-		(*pparts)[i].offset = reg[0];
-		(*pparts)[i].size = reg[1];
+		(*pparts)[i].offset = be32_to_cpu(reg[0]);
+		(*pparts)[i].size = be32_to_cpu(reg[1]);
 
 		partname = of_get_property(pp, "label", &len);
 		if (!partname)
-- 
1.7.1

^ permalink raw reply related

* [PATCH 18/18] powerpc: Fix jiffies variable on little endian
From: Ian Munsie @ 2010-10-01  7:06 UTC (permalink / raw)
  To: linux-kernel, linuxppc-dev, benh
  Cc: Michal Marek, Denys Vlasenko, Rusty Russell, Tim Abbott, paulus,
	Ian Munsie, Sean MacLennan
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

From: Ian Munsie <imunsie@au1.ibm.com>

The vmlinux linker script sets the jiffies variable to the low word of
the jiffies_64 variable. This patch corrects which word is used on
little endian.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
---
 arch/powerpc/kernel/vmlinux.lds.S |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index 8a0deef..7a9010f 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -34,8 +34,12 @@ OUTPUT_ARCH(powerpc:common64)
 jiffies = jiffies_64;
 #else
 OUTPUT_ARCH(powerpc:common)
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+jiffies = jiffies_64;
+#else
 jiffies = jiffies_64 + 4;
 #endif
+#endif
 SECTIONS
 {
 	. = 0;
-- 
1.7.1

^ permalink raw reply related

* Re: [PATCH 02/18] powerpc: Add CROSSBE_COMPILE to build big endian boot wrapper
From: Geert Uytterhoeven @ 2010-10-01  7:13 UTC (permalink / raw)
  To: Ian Munsie
  Cc: Michal Marek, Sam Ravnborg, Albert Herranz, linux-kernel, paulus,
	Andrew Morton, linuxppc-dev
In-Reply-To: <1285916771-18033-3-git-send-email-imunsie@au1.ibm.com>

On Fri, Oct 1, 2010 at 09:05, Ian Munsie <imunsie@au1.ibm.com> wrote:
> From: Ian Munsie <imunsie@au1.ibm.com>
>
> Since the boot wrapper must be built by a big endian 32bit toolchain
> regardless of what the rest of the kernel is using introduce a new
> parameter to specify that toolchain - CROSSBE_COMPILE.
>
> We already have CROSS32_COMPILE which is already used for the boot
> wrapper, but it is also used to build the 32bit vdso which should be
> build in the same endianness as the rest of the kernel, so it is
> necessary to be able to specify the toolchain to build the boot wrapper
> separately from that used to build the vdso and again separately from
> that used to build the main kernel.
>
> CROSSBE_COMPILE should be pointed to a toolchain capable of targeting
> 32bit big endian powerpc, either specifically targetted at 32bit or
> bi-arch 64 and 32bit. If CROSSBE_COMPILE is not specified it will fall
> back to CROSS32_COMPILE to maintain compatibility with big endian
> targets.

Shouldn't the help text added by [PATCH 01/18]:

+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -329,3 +329,19 @@ config CHECK_CACHE_COHERENCY
       bool

 endmenu
+
+config ARCH_SUPPORTS_LITTLE_ENDIAN
+       bool
+
+config CPU_LITTLE_ENDIAN
+       bool "Build little endian kernel"
+       depends on ARCH_SUPPORTS_LITTLE_ENDIAN && EXPERIMENTAL
+       default n
+       help
+         This option selects whether a big endian or little endian kernel =
will
+         be built.
+
+         Note that if building a little endian kernel, CROSS_COMPILE must
+         point to a toolchain capable of targetting little endian powerpc,
+         while the toolchain specified by CROSS32_COMPILE must be capable =
of
+         targetting *BIG* endian PowerPC.

be updated here as well?

Gr{oetje,eeting}s,

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org

In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH 02/18] powerpc: Add CROSSBE_COMPILE to build big endian boot wrapper
From: Ian Munsie @ 2010-10-01  7:19 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Michal Marek, Sam Ravnborg, Albert Herranz, linux-kernel, paulus,
	Andrew Morton, linuxppc-dev
In-Reply-To: <AANLkTi=VmSXN6XeD__nkQLJAYXzxOh6WEtW6-jjJcC4Q@mail.gmail.com>

Excerpts from Geert Uytterhoeven's message of Fri Oct 01 17:13:19 +1000 2010:
> Shouldn't the help text added by [PATCH 01/18]:
....
> be updated here as well?

Yep, you're right. Thanks for pointing that out, I'll fix it for the next
revision.

Cheers,
-Ian

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Kumar Gala @ 2010-10-01  9:02 UTC (permalink / raw)
  To: Ian Munsie; +Cc: linuxppc-dev, linux-kernel, paulus
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>


On Oct 1, 2010, at 2:05 AM, Ian Munsie wrote:

> Some PowerPC processors can be run in either big or little endian =
modes, some
> others can map selected pages of memory as little endian, which allows =
the same
> thing. Until now we have only supported the default big endian mode in =
Linux.
> This patch set introduces little endian support for the 44x family of =
PowerPC
> processors.

=46rom a community aspect is anyone actually going to use this?  Is this =
going to be the equivalent of voyager on x86?  I've got nothing against =
some of the endian clean ups this introduces.  However the changes to =
misc_32.S are a bit ugly from a readability point of view.  Just seems =
like this is likely to bit-rot pretty quickly.

- k

^ permalink raw reply

* Re: [PATCH 01/18] powerpc: Add ability to build little endian kernels
From: Kumar Gala @ 2010-10-01  9:18 UTC (permalink / raw)
  To: Ian Munsie
  Cc: Michal Marek, Albert Herranz, linuxppc-dev, linux-kernel, paulus,
	Andreas Schwab, Andrew Morton, Sam Ravnborg, Torez Smith
In-Reply-To: <1285916771-18033-2-git-send-email-imunsie@au1.ibm.com>


On Oct 1, 2010, at 2:05 AM, Ian Munsie wrote:

>=20
> diff --git a/arch/powerpc/platforms/Kconfig.cputype =
b/arch/powerpc/platforms/Kconfig.cputype
> index d361f81..074ff12 100644
> --- a/arch/powerpc/platforms/Kconfig.cputype
> +++ b/arch/powerpc/platforms/Kconfig.cputype
> @@ -329,3 +329,19 @@ config CHECK_CACHE_COHERENCY
> 	bool
>=20
> endmenu
> +
> +config ARCH_SUPPORTS_LITTLE_ENDIAN
> +	bool
> +
> +config CPU_LITTLE_ENDIAN
> +	bool "Build little endian kernel"
> +	depends on ARCH_SUPPORTS_LITTLE_ENDIAN && EXPERIMENTAL
> +	default n
> +	help
> +	  This option selects whether a big endian or little endian =
kernel will
> +	  be built.
> +
> +	  Note that if building a little endian kernel, CROSS_COMPILE =
must
> +	  point to a toolchain capable of targetting little endian =
powerpc,
> +	  while the toolchain specified by CROSS32_COMPILE must be =
capable of
> +	  targetting *BIG* endian PowerPC.
> --=20
> 1.7.1

Shouldn't we have something that limits to the sub-arch'es that actually =
support it?  I doubt I'm ever going to make FSL-Book-e support LE.

- k=

^ permalink raw reply

* Re: [PATCH v3 3/7] eSPI: add eSPI controller support
From: Anton Vorontsov @ 2010-10-01 11:22 UTC (permalink / raw)
  To: Mingkai Hu; +Cc: linuxppc-dev, kumar.gala, linux-mtd, spi-devel-general
In-Reply-To: <1285833646-12006-4-git-send-email-Mingkai.hu@freescale.com>

Hello Mingkai,

There are mostly cosmetic comments down below.

On Thu, Sep 30, 2010 at 04:00:42PM +0800, Mingkai Hu wrote:
[...]
> +/*
> + * Freescale eSPI controller driver.
> + *
> + * Copyright 2010 Freescale Semiconductor, Inc.
> + *
> + * 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/module.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/spi/spi.h>
> +#include <linux/platform_device.h>
> +#include <linux/fsl_devices.h>
> +#include <linux/mm.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_spi.h>
> +#include <sysdev/fsl_soc.h>

Please move the sysdev/ include after linux/.
Will make it a bit prettier. :-)

> +#include <linux/interrupt.h>
> +#include <linux/err.h>
> +
> +#include "spi_fsl_lib.h"
[...]
> +static void fsl_espi_change_mode(struct spi_device *spi)
> +{
> +	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
> +	struct spi_mpc8xxx_cs *cs = spi->controller_state;
> +	struct fsl_espi_reg *reg_base = (struct fsl_espi_reg *)mspi->reg_base;

No need for the type cast. The same for the rest of the code.

> +	__be32 __iomem *mode;
> +	__be32 __iomem *espi_mode = NULL;
> +	u32 tmp;
> +	unsigned long flags;
> +
> +	espi_mode = &reg_base->mode;
> +	mode = &reg_base->csmode[spi->chip_select];

Could save a few lines by turning this into initializers.

> +
> +	/* Turn off IRQs locally to minimize time that SPI is disabled. */
> +	local_irq_save(flags);
> +
> +	/* Turn off SPI unit prior changing mode */
> +	tmp = mpc8xxx_spi_read_reg(espi_mode);
> +	mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE);
> +	mpc8xxx_spi_write_reg(mode, cs->hw_mode);
> +	mpc8xxx_spi_write_reg(espi_mode, tmp);
> +
> +	local_irq_restore(flags);
> +}
> +
> +static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi)
> +{
> +	u32 data;
> +	u16 data_h, data_l;

u16 data_h;
u16 data_l;

> +

No need for this empty line.

> +	const u32 *tx = mpc8xxx_spi->tx;

<- Instead, add an empty line here.

> +	if (!tx)
> +		return 0;
> +
> +	data = *tx++ << mpc8xxx_spi->tx_shift;
> +	data_l = data & 0xffff;
> +	data_h = (data >> 16) & 0xffff;
> +	swab16s(&data_l);
> +	swab16s(&data_h);
> +	data = data_h | data_l;
> +
> +	mpc8xxx_spi->tx = tx;
> +	return data;
> +}
> +
> +static int fsl_espi_setup_transfer(struct spi_device *spi,
> +					struct spi_transfer *t)
> +{
> +	struct mpc8xxx_spi *mpc8xxx_spi;
> +	int bits_per_word = 0;
> +	u8 pm;
> +	u32 hz = 0;
> +	struct spi_mpc8xxx_cs	*cs = spi->controller_state;

Stray tab.

> +
> +	mpc8xxx_spi = spi_master_get_devdata(spi->master);

Could move this to the initializer.

> +
> +	if (t) {
> +		bits_per_word = t->bits_per_word;
> +		hz = t->speed_hz;
> +	}
> +
> +	/* spi_transfer level calls that work per-word */
> +	if (!bits_per_word)
> +		bits_per_word = spi->bits_per_word;
> +
> +	/* Make sure its a bit width we support [4..16] */
> +	if ((bits_per_word < 4) || (bits_per_word > 16))
> +		return -EINVAL;
> +
> +	if (!hz)
> +		hz = spi->max_speed_hz;
> +
> +	cs->rx_shift = 0;
> +	cs->tx_shift = 0;
> +	cs->get_rx = mpc8xxx_spi_rx_buf_u32;
> +	cs->get_tx = mpc8xxx_spi_tx_buf_u32;
> +	if (bits_per_word <= 8) {
> +		cs->rx_shift = 8 - bits_per_word;
> +	} else if (bits_per_word <= 16) {
> +		cs->rx_shift = 16 - bits_per_word;
> +		if (spi->mode & SPI_LSB_FIRST)
> +			cs->get_tx = fsl_espi_tx_buf_lsb;
> +	} else
> +		return -EINVAL;

} else {
}

> +
> +	mpc8xxx_spi->rx_shift = cs->rx_shift;
> +	mpc8xxx_spi->tx_shift = cs->tx_shift;
> +	mpc8xxx_spi->get_rx = cs->get_rx;
> +	mpc8xxx_spi->get_tx = cs->get_tx;
> +
> +	bits_per_word = bits_per_word - 1;
> +
> +	/* mask out bits we are going to set */
> +	cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16
> +				  | CSMODE_PM(0xF));

No need to break this statement.

> +
> +	cs->hw_mode |= CSMODE_LEN(bits_per_word);
> +
> +	if ((mpc8xxx_spi->spibrg / hz) > 64) {
> +		cs->hw_mode |= CSMODE_DIV16;
> +		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
> +
> +		WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
> +			  "Will use %d Hz instead.\n", dev_name(&spi->dev),
> +			  hz, mpc8xxx_spi->spibrg / 1024);
> +		if (pm > 16)
> +			pm = 16;
> +	} else {
> +		pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
> +	}
> +	if (pm)
> +		pm--;
> +
> +	cs->hw_mode |= CSMODE_PM(pm);
> +
> +	fsl_espi_change_mode(spi);
> +	return 0;
> +}
> +
> +int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t,
> +		unsigned int len)

Does this need to be global?

> +{
> +	u32 word;
> +	struct fsl_espi_reg *reg_base = (struct fsl_espi_reg *)mspi->reg_base;
> +
> +	mspi->count = len;
> +
> +	/* enable rx ints */
> +	mpc8xxx_spi_write_reg(&reg_base->mask, SPIM_NE);
> +
> +	/* transmit word */
> +	word = mspi->get_tx(mspi);
> +	mpc8xxx_spi_write_reg(&reg_base->transmit, word);
> +
> +	return 0;
> +}
> +
> +static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t,
> +			    bool is_dma_mapped)

No need for the is_dma_mapped argument.

> +{
> +	struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
> +	struct fsl_espi_reg *reg_base;
> +	unsigned int len = t->len;
> +	u8 bits_per_word;
> +	int ret;
> +
> +	reg_base = (struct fsl_espi_reg *)mpc8xxx_spi->reg_base;

Better write this as an initializer, no need for the cast.

> +
> +	bits_per_word = spi->bits_per_word;
> +	if (t->bits_per_word)
> +		bits_per_word = t->bits_per_word;
[...]
> +static int fsl_espi_setup(struct spi_device *spi)
> +{
> +	struct mpc8xxx_spi *mpc8xxx_spi;
> +	struct fsl_espi_reg *reg_base;
> +	int retval;
> +	u32 hw_mode;
> +	u32 loop_mode;
> +	struct spi_mpc8xxx_cs	*cs = spi->controller_state;

Stray tab.

> +
> +	if (!spi->max_speed_hz)
> +		return -EINVAL;
> +
> +	if (!cs) {
> +		cs = kzalloc(sizeof *cs, GFP_KERNEL);
> +		if (!cs)
> +			return -ENOMEM;
> +		spi->controller_state = cs;
> +	}
[...]
> +		rx_data = mpc8xxx_spi_read_reg(&reg_base->receive);
> +
> +		if (mspi->rx)
> +			mspi->get_rx(rx_data, mspi);
> +	}
> +
> +	if ((events & SPIE_NF) == 0)

if (!(events & bit)) is a bit more more natural. Also, the if
statement here needs braces.

> +		/* spin until TX is done */
> +		while (((events = mpc8xxx_spi_read_reg(&reg_base->event))
> +					& SPIE_NF) == 0)
> +			cpu_relax();

This is dangerous. There's a handy spin_event_timeout()
in asm/delay.h.

> +
> +	/* Clear the events */
> +	mpc8xxx_spi_write_reg(&reg_base->event, events);
> +
> +	mspi->count -= 1;
> +	if (mspi->count) {
> +		u32 word = mspi->get_tx(mspi);
> +
> +		mpc8xxx_spi_write_reg(&reg_base->transmit, word);
> +	} else {
> +		complete(&mspi->done);
> +	}
> +}
> +
> +static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
> +{
> +	struct mpc8xxx_spi *mspi = context_data;
> +	struct fsl_espi_reg *reg_base = (struct fsl_espi_reg *)mspi->reg_base;
> +	irqreturn_t ret = IRQ_NONE;
> +	u32 events;
> +
> +	/* Get interrupt events(tx/rx) */
> +	events = mpc8xxx_spi_read_reg(&reg_base->event);
> +	if (events)
> +		ret = IRQ_HANDLED;
> +
> +	dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);

dev_vdbg()

> +
> +	fsl_espi_cpu_irq(mspi, events);
> +
> +	return ret;
> +}
> +
> +static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
> +{
> +	iounmap(mspi->reg_base);
> +}
> +
> +static struct spi_master * __devinit fsl_espi_probe(struct device *dev,
> +		struct resource *mem, unsigned int irq)
> +{
> +	struct fsl_spi_platform_data *pdata = dev->platform_data;
> +	struct spi_master *master;
> +	struct mpc8xxx_spi *mpc8xxx_spi;
> +	struct fsl_espi_reg *reg_base;
> +	u32 regval;
> +	int i, ret = 0;
> +
> +	master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
> +	if (master == NULL) {

Sometimes you check for !allocated, and sometimes allocated == NULL.
Be consistent. (And !allocated is more natural.)

> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	dev_set_drvdata(dev, master);
> +
> +	ret = mpc8xxx_spi_probe(dev, mem, irq);
> +	if (ret)
> +		goto err_probe;
> +
> +	master->setup = fsl_espi_setup;
> +
> +	mpc8xxx_spi = spi_master_get_devdata(master);
> +	mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg;
> +	mpc8xxx_spi->spi_remove = fsl_espi_remove;
> +
> +	mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
> +	if (mpc8xxx_spi->reg_base == NULL) {

Ditto.

> +		ret = -ENOMEM;
> +		goto err_probe;
> +	}
> +
> +	reg_base = (struct fsl_espi_reg *)mpc8xxx_spi->reg_base;
> +
> +	/* Register for SPI Interrupt */
> +	ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq,
> +			  0, "fsl_espi", mpc8xxx_spi);
> +
> +	if (ret != 0)

Every time someone writes 'if (rc != 0)', a kitty dies.
Simple 'if (rc)' saves kittens.

> +		goto free_irq;
> +
> +	if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
> +		mpc8xxx_spi->rx_shift = 16;
> +		mpc8xxx_spi->tx_shift = 24;
> +	}
[...]
> +
> +static int __devexit of_fsl_espi_remove(struct platform_device *dev)
> +{
> +	int ret;
> +
> +	ret = mpc8xxx_spi_remove(&dev->dev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;

Just 'return mpc8xxx_spi_remove(&dev->dev);' is sufficient.

Also, I think there's no need for this wrapper nowadays (but
splitting OF and real probe() stuff is still appropriate).

> +}
> +
> +static const struct of_device_id of_fsl_espi_match[] = {
> +	{ .compatible = "fsl,mpc8536-espi" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, of_fsl_espi_match);
> +
> +static struct of_platform_driver fsl_espi_driver = {
> +	.driver = {
> +		.name = "fsl_espi",
> +		.owner = THIS_MODULE,
> +		.of_match_table = of_fsl_espi_match,
> +	},
> +	.probe		= of_fsl_espi_probe,
> +	.remove		= __devexit_p(of_fsl_espi_remove),
> +};
> +
> +static int __init fsl_espi_init(void)
> +{
> +	return of_register_platform_driver(&fsl_espi_driver);
> +}
> +module_init(fsl_espi_init);
> +
> +static void __exit fsl_espi_exit(void)
> +{
> +	of_unregister_platform_driver(&fsl_espi_driver);
> +}
> +module_exit(fsl_espi_exit);
> +
> +MODULE_AUTHOR("Mingkai Hu");
> +MODULE_DESCRIPTION("Enhanced Freescale SPI Driver");

This sounds like that this is an enhanced version of the
Freescale SPI driver, which it is not. ;-)

> +MODULE_LICENSE("GPL");
> diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h
> index 6ae8949..9c81498 100644
> --- a/drivers/spi/spi_fsl_lib.h
> +++ b/drivers/spi/spi_fsl_lib.h
> @@ -26,6 +26,7 @@ struct mpc8xxx_spi {
>  	/* rx & tx bufs from the spi_transfer */
>  	const void *tx;
>  	void *rx;
> +	int len;

I'd place the #ifdef CONFIG_SPI_ESPI, for documentation purposes.

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH v3 2/7] spi/mpc8xxx: refactor the common code for SPI/eSPI controller
From: Anton Vorontsov @ 2010-10-01 11:22 UTC (permalink / raw)
  To: Mingkai Hu; +Cc: linuxppc-dev, kumar.gala, linux-mtd, spi-devel-general
In-Reply-To: <1285833646-12006-3-git-send-email-Mingkai.hu@freescale.com>

On Thu, Sep 30, 2010 at 04:00:41PM +0800, Mingkai Hu wrote:
[...]
> -static void mpc8xxx_spi_change_mode(struct spi_device *spi)
> +static void fsl_spi_change_mode(struct spi_device *spi)
>  {
>  	struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
>  	struct spi_mpc8xxx_cs *cs = spi->controller_state;
> -	__be32 __iomem *mode = &mspi->base->mode;
> +	struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;

No need for these type casts (the same is for the whole patch).

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH 05/18] powerpc: Wire up 44x little endian boot for remaining 44x targets
From: Josh Boyer @ 2010-10-01 11:27 UTC (permalink / raw)
  To: Ian Munsie; +Cc: linuxppc-dev, linux-kernel, paulus
In-Reply-To: <1285916771-18033-6-git-send-email-imunsie@au1.ibm.com>

On Fri, Oct 1, 2010 at 3:05 AM, Ian Munsie <imunsie@au1.ibm.com> wrote:
> From: Ian Munsie <imunsie@au1.ibm.com>
>
> I haven't tested booting a little endian kernel on any of these targets,
> but they all claim to be 44x so my little endian trampoline should work
> on all of them, so wire it up on:
>
> bamboo
> katmai
> kilauea
> rainer
> sam440ep
> sequoia
> warp
> yosemite
> ebony
>
> Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>

I see no reason to do this at all.  If you haven't tested them and
there is no demand, there's no reason to wire them up.  Some might
actively want to disallow LE mode anyway, like the Warp or Sam440EP.

josh

^ permalink raw reply

* Re: [PATCH 01/18] powerpc: Add ability to build little endian kernels
From: Josh Boyer @ 2010-10-01 11:28 UTC (permalink / raw)
  To: Kumar Gala
  Cc: Michal Marek, Sam Ravnborg, Albert Herranz, linux-kernel, paulus,
	Ian Munsie, Andreas Schwab, Andrew Morton, linuxppc-dev,
	Torez Smith
In-Reply-To: <F7E7DAE6-A132-458D-ABC3-4CC92A4C7263@kernel.crashing.org>

On Fri, Oct 1, 2010 at 5:18 AM, Kumar Gala <galak@kernel.crashing.org> wrot=
e:
>
> On Oct 1, 2010, at 2:05 AM, Ian Munsie wrote:
>
>>
>> diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platf=
orms/Kconfig.cputype
>> index d361f81..074ff12 100644
>> --- a/arch/powerpc/platforms/Kconfig.cputype
>> +++ b/arch/powerpc/platforms/Kconfig.cputype
>> @@ -329,3 +329,19 @@ config CHECK_CACHE_COHERENCY
>> =A0 =A0 =A0 bool
>>
>> endmenu
>> +
>> +config ARCH_SUPPORTS_LITTLE_ENDIAN
>> + =A0 =A0 bool
>> +
>> +config CPU_LITTLE_ENDIAN
>> + =A0 =A0 bool "Build little endian kernel"
>> + =A0 =A0 depends on ARCH_SUPPORTS_LITTLE_ENDIAN && EXPERIMENTAL
>> + =A0 =A0 default n
>> + =A0 =A0 help
>> + =A0 =A0 =A0 This option selects whether a big endian or little endian =
kernel will
>> + =A0 =A0 =A0 be built.
>> +
>> + =A0 =A0 =A0 Note that if building a little endian kernel, CROSS_COMPIL=
E must
>> + =A0 =A0 =A0 point to a toolchain capable of targetting little endian p=
owerpc,
>> + =A0 =A0 =A0 while the toolchain specified by CROSS32_COMPILE must be c=
apable of
>> + =A0 =A0 =A0 targetting *BIG* endian PowerPC.
>> --
>> 1.7.1
>
> Shouldn't we have something that limits to the sub-arch'es that actually =
support it? =A0I doubt I'm ever going to make FSL-Book-e support LE.

Yes, it should.

josh

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Josh Boyer @ 2010-10-01 11:30 UTC (permalink / raw)
  To: Kumar Gala; +Cc: paulus, linuxppc-dev, Ian Munsie, linux-kernel
In-Reply-To: <2C5357FA-F87F-457E-B5C1-0DCC5A842DE7@kernel.crashing.org>

On Fri, Oct 1, 2010 at 5:02 AM, Kumar Gala <galak@kernel.crashing.org> wrot=
e:
>
> On Oct 1, 2010, at 2:05 AM, Ian Munsie wrote:
>
>> Some PowerPC processors can be run in either big or little endian modes,=
 some
>> others can map selected pages of memory as little endian, which allows t=
he same
>> thing. Until now we have only supported the default big endian mode in L=
inux.
>> This patch set introduces little endian support for the 44x family of Po=
werPC
>> processors.
>
> From a community aspect is anyone actually going to use this? =A0Is this =
going to be the equivalent of voyager on x86? =A0I've got nothing against s=
ome of the endian clean ups this introduces. =A0However the changes to misc=
_32.S are a bit ugly from a readability point of view. =A0Just seems like t=
his is likely to bit-rot pretty quickly.

I'm with Kumar on this one.  Why would we want to support this?  I
can't say I would be very willing to help anyone run in LE mode, let
alone have it randomly selectable.

josh

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Josh Boyer @ 2010-10-01 11:36 UTC (permalink / raw)
  To: Ian Munsie; +Cc: linuxppc-dev, linux-kernel, paulus
In-Reply-To: <1285916771-18033-1-git-send-email-imunsie@au1.ibm.com>

On Fri, Oct 1, 2010 at 3:05 AM, Ian Munsie <imunsie@au1.ibm.com> wrote:
> This patch set in combination with a patched GCC, binutils, uClibc and
> buildroot has allowed for a full proof of concept little endian environme=
nt on
> a 440 Taishan board, which was able to successfully run busybox, OpenSSH =
and a
> handful of other userspace programs without problems.

Aside from my general "uh, why?" stance, I'm very very hesitant to
integrate anything in the kernel that doesn'.t have released patches
on the toolchain side.

Also, which uClibc?  The old and crusty uClibc that uses the horrible
linuxthreads, or the somewhat less crusty that just switched to NPTL
(which hasn't been verified on normal PowerPC that I recall).  Why not
use glibc...

> This is not yet complete support for little endian PowerPC, some outstand=
ing
> issues that I am aware of are:
> =A0* We only support 32bit PowerPC for now (and indeed, only 44x)
> =A0* The vdso has not been fixed to be endian agnostic - any userspace pr=
ogram
> =A0 accessing it will get an unexpected result.
> =A0* I have not touched PCI at all
> =A0* Remaining device tree accesses still need to be examined to ensure t=
hey are
> =A0 correctly handling the endianess of the device tree.
> =A0* Any other driver that uses the device tree is likely be broken for t=
he same reason.
> =A0* I've included a patch for the alignment handler, however it is as ye=
t
> =A0 completely untested due to a property of the hardware I've been using=
 for
> =A0 testing.

I'm not meeting to detract here, but the Kconfig should be dependent
on && BROKEN until the above is fixed.

josh

^ permalink raw reply

* Re: [PATCH 01/18] powerpc: Add ability to build little endian kernels
From: Josh Boyer @ 2010-10-01 11:40 UTC (permalink / raw)
  To: Ian Munsie
  Cc: Michal Marek, Sam Ravnborg, Albert Herranz, linux-kernel, paulus,
	Andreas Schwab, Andrew Morton, linuxppc-dev, Torez Smith
In-Reply-To: <1285916771-18033-2-git-send-email-imunsie@au1.ibm.com>

On Fri, Oct 1, 2010 at 3:05 AM, Ian Munsie <imunsie@au1.ibm.com> wrote:
> diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platfo=
rms/Kconfig.cputype
> index d361f81..074ff12 100644
> --- a/arch/powerpc/platforms/Kconfig.cputype
> +++ b/arch/powerpc/platforms/Kconfig.cputype
> @@ -329,3 +329,19 @@ config CHECK_CACHE_COHERENCY
> =A0 =A0 =A0 =A0bool
>
> =A0endmenu
> +
> +config ARCH_SUPPORTS_LITTLE_ENDIAN
> + =A0 =A0 =A0 bool
> +
> +config CPU_LITTLE_ENDIAN
> + =A0 =A0 =A0 bool "Build little endian kernel"
> + =A0 =A0 =A0 depends on ARCH_SUPPORTS_LITTLE_ENDIAN && EXPERIMENTAL
> + =A0 =A0 =A0 default n
> + =A0 =A0 =A0 help
> + =A0 =A0 =A0 =A0 This option selects whether a big endian or little endi=
an kernel will
> + =A0 =A0 =A0 =A0 be built.
> +
> + =A0 =A0 =A0 =A0 Note that if building a little endian kernel, CROSS_COM=
PILE must
> + =A0 =A0 =A0 =A0 point to a toolchain capable of targetting little endia=
n powerpc,
> + =A0 =A0 =A0 =A0 while the toolchain specified by CROSS32_COMPILE must b=
e capable of
> + =A0 =A0 =A0 =A0 targetting *BIG* endian PowerPC.

Have you tested this support with a userspace containing floating
point instructions?  I wonder if CONFIG_MATH_EMULATION is going to
need work at all, and if the boards with an actual FPU (440EP, 440EPx,
460EX, etc) would have issues.

josh

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Gary Thomas @ 2010-10-01 11:55 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev, paulus, linux-kernel, Ian Munsie
In-Reply-To: <AANLkTinSDMvBb7rnnfwYMkEVOgDUqRetZg86rh-UmSAg@mail.gmail.com>

On 10/01/2010 05:30 AM, Josh Boyer wrote:
> On Fri, Oct 1, 2010 at 5:02 AM, Kumar Gala<galak@kernel.crashing.org>  wrote:
>>
>> On Oct 1, 2010, at 2:05 AM, Ian Munsie wrote:
>>
>>> Some PowerPC processors can be run in either big or little endian modes, some
>>> others can map selected pages of memory as little endian, which allows the same
>>> thing. Until now we have only supported the default big endian mode in Linux.
>>> This patch set introduces little endian support for the 44x family of PowerPC
>>> processors.
>>
>>  From a community aspect is anyone actually going to use this?  Is this going to be the equivalent of voyager on x86?  I've got nothing against some of the endian clean ups this introduces.  However the changes to misc_32.S are a bit ugly from a readability point of view.  Just seems like this is likely to bit-rot pretty quickly.
>
> I'm with Kumar on this one.  Why would we want to support this?  I
> can't say I would be very willing to help anyone run in LE mode, let
> alone have it randomly selectable.

Indeed, I thought we had killed that Windows-NT dog ~15 years ago :-)

-- 
------------------------------------------------------------
Gary Thomas                 |  Consulting for the
MLB Associates              |    Embedded world
------------------------------------------------------------

^ permalink raw reply

* Re: [PATCH 01/18] powerpc: Add ability to build little endian kernels
From: Benjamin Herrenschmidt @ 2010-10-01 12:09 UTC (permalink / raw)
  To: Josh Boyer
  Cc: Michal Marek, linuxppc-dev, Albert Herranz, linux-kernel, paulus,
	Ian Munsie, Andreas Schwab, Andrew Morton, Sam Ravnborg,
	Torez Smith
In-Reply-To: <AANLkTimGtB_KoOgwk3Ujn2UcNzs9qHxzeZcb1ShLgcXh@mail.gmail.com>

On Fri, 2010-10-01 at 07:28 -0400, Josh Boyer wrote:
> > Shouldn't we have something that limits to the sub-arch'es that
> actually support it?  I doubt I'm ever going to make FSL-Book-e
> support LE.
> 
> Yes, it should. 

Sure, that's only WIP patches :-)

Tho FSL BookE would be relatively easy...

Cheers,
Ben.

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Benjamin Herrenschmidt @ 2010-10-01 12:14 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev, paulus, linux-kernel, Ian Munsie
In-Reply-To: <AANLkTinSDMvBb7rnnfwYMkEVOgDUqRetZg86rh-UmSAg@mail.gmail.com>

On Fri, 2010-10-01 at 07:30 -0400, Josh Boyer wrote:
> 
> > From a community aspect is anyone actually going to use this?  Is
> this going to be the equivalent of voyager on x86?  I've got nothing
> against some of the endian clean ups this introduces.  However the
> changes to misc_32.S are a bit ugly from a readability point of view.
>  Just seems like this is likely to bit-rot pretty quickly.
> 
> I'm with Kumar on this one.  Why would we want to support this?  I
> can't say I would be very willing to help anyone run in LE mode, let
> alone have it randomly selectable. 

There's some good reasons on the field ... sadly.

At this stage this is mostly an experiment, which went pretty well in
the sense that it's actually quite easy and a lot of the "fixes" are
actually reasonable cleanups to carry.

Now, the main reasons in practice are anything touching graphics.

There's quite a few IP cores out there for SoCs that don't have HW
swappers, and -tons- of more or less ugly code that can't deal with non
native pixel ordering (hell, even Xorg isn't good at it, we really only
support cards that have HW swappers today).

There's an even bigger pile of application code that deals with graphics
without any regard for endianness and is essentially unfixable.

So it becomes a matter of potential customers that will take it if it
does LE and won't if it doesn't ...

Now, I don't have a problem supporting that as the maintainer, as I
said, from a kernel standpoint, it's all quite easy to deal with. Some
of the most gory aspects in misc_32.S could probably be done in a way
that is slightly more readable, but the approach is actually good, I
think, to have macros to represent the high/low parts of register pairs.

So at this stage, I'd say, let's not dismiss it just because we all come
from a long education of hating LE for the sake of it :-)

It makes -some- sense, even if it's not necessarily on the markets
targeted by FSL today for example. At least from the kernel POV, it
doesn't seem to me to be a significant support burden at all.

Cheers,
Ben.

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Benjamin Herrenschmidt @ 2010-10-01 12:15 UTC (permalink / raw)
  To: Gary Thomas; +Cc: paulus, linuxppc-dev, linux-kernel, Ian Munsie
In-Reply-To: <4CA5CC3C.6020006@mlbassoc.com>

On Fri, 2010-10-01 at 05:55 -0600, Gary Thomas wrote:
> On 10/01/2010 05:30 AM, Josh Boyer wrote:
> > On Fri, Oct 1, 2010 at 5:02 AM, Kumar Gala<galak@kernel.crashing.org>  wrote:
> >>
> >> On Oct 1, 2010, at 2:05 AM, Ian Munsie wrote:
> >>
> >>> Some PowerPC processors can be run in either big or little endian modes, some
> >>> others can map selected pages of memory as little endian, which allows the same
> >>> thing. Until now we have only supported the default big endian mode in Linux.
> >>> This patch set introduces little endian support for the 44x family of PowerPC
> >>> processors.
> >>
> >>  From a community aspect is anyone actually going to use this?  Is this going to be the equivalent of voyager on x86?  I've got nothing against some of the endian clean ups this introduces.  However the changes to misc_32.S are a bit ugly from a readability point of view.  Just seems like this is likely to bit-rot pretty quickly.
> >
> > I'm with Kumar on this one.  Why would we want to support this?  I
> > can't say I would be very willing to help anyone run in LE mode, let
> > alone have it randomly selectable.
> 
> Indeed, I thought we had killed that Windows-NT dog ~15 years ago :-)

Actually this has more to do with having to deal with code written for
ARM LE :-)

Cheers,
Ben.

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Benjamin Herrenschmidt @ 2010-10-01 12:21 UTC (permalink / raw)
  To: Josh Boyer; +Cc: paulus, linuxppc-dev, Ian Munsie, linux-kernel
In-Reply-To: <AANLkTikHcoccu-WGtSOSdkwU6Tw_An_VFkUcEoY==8=c@mail.gmail.com>

On Fri, 2010-10-01 at 07:36 -0400, Josh Boyer wrote:
> On Fri, Oct 1, 2010 at 3:05 AM, Ian Munsie <imunsie@au1.ibm.com> wrote:
> > This patch set in combination with a patched GCC, binutils, uClibc and
> > buildroot has allowed for a full proof of concept little endian environment on
> > a 440 Taishan board, which was able to successfully run busybox, OpenSSH and a
> > handful of other userspace programs without problems.
> 
> Aside from my general "uh, why?" stance, I'm very very hesitant to
> integrate anything in the kernel that doesn'.t have released patches
> on the toolchain side.

We aren't yet talking about merging that as-is, though I beleive at
least -some- of the patches have merit on their own, such as the proper
accessors for device-tree properties. At the very least, it would make
it less painful for archs like ARM to borrow code in that area and will
make it cleaner for sparse when we generalize endian annotations.

The toolchain work was done as a quick & dirty experiment. Whether some
"proper" work there will happen remains to be decided.

> Also, which uClibc?  The old and crusty uClibc that uses the horrible
> linuxthreads, or the somewhat less crusty that just switched to NPTL
> (which hasn't been verified on normal PowerPC that I recall).  Why not
> use glibc...

Because this was a proof of concept and as such, it was easier to deal
with uclibc initially to get busybox going :-)

> > This is not yet complete support for little endian PowerPC, some outstanding
> > issues that I am aware of are:
> >  * We only support 32bit PowerPC for now (and indeed, only 44x)
> >  * The vdso has not been fixed to be endian agnostic - any userspace program
> >   accessing it will get an unexpected result.
> >  * I have not touched PCI at all
> >  * Remaining device tree accesses still need to be examined to ensure they are
> >   correctly handling the endianess of the device tree.
> >  * Any other driver that uses the device tree is likely be broken for the same reason.
> >  * I've included a patch for the alignment handler, however it is as yet
> >   completely untested due to a property of the hardware I've been using for
> >   testing.
> 
> I'm not meeting to detract here, but the Kconfig should be dependent
> on && BROKEN until the above is fixed.

Right.

I think Ian wasn't clear enough on the fact that those patches aren't
meant to be merged in the next merge window :-) I told him to shoot them
to the list for review, comments and discussions, but if we decide to
move along with integrating that, there's definitely more work to do.

Cheers,
Ben.

^ permalink raw reply

* Re: [PATCH 01/18] powerpc: Add ability to build little endian kernels
From: Benjamin Herrenschmidt @ 2010-10-01 12:22 UTC (permalink / raw)
  To: Josh Boyer
  Cc: Michal Marek, Sam Ravnborg, Albert Herranz, linux-kernel, paulus,
	Ian Munsie, Andreas Schwab, Andrew Morton, linuxppc-dev,
	Torez Smith
In-Reply-To: <AANLkTimdV2FTEhcKXcM_+k23PBFTR5KvyjqEGxzzX72Q@mail.gmail.com>

On Fri, 2010-10-01 at 07:40 -0400, Josh Boyer wrote:
> Have you tested this support with a userspace containing floating
> point instructions?  I wonder if CONFIG_MATH_EMULATION is going to
> need work at all, and if the boards with an actual FPU (440EP, 440EPx,
> 460EX, etc) would have issues. 

That's one of the things on the TODO list. We've tested that on a 44x
with no FPU so far and made sure we built without math emu.

Cheers,
Ben.

^ permalink raw reply

* Re: Introduce support for little endian PowerPC
From: Gary Thomas @ 2010-10-01 12:37 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: paulus, linuxppc-dev, linux-kernel, Ian Munsie
In-Reply-To: <1285935329.2463.79.camel@pasglop>

On 10/01/2010 06:15 AM, Benjamin Herrenschmidt wrote:
> On Fri, 2010-10-01 at 05:55 -0600, Gary Thomas wrote:
>> On 10/01/2010 05:30 AM, Josh Boyer wrote:
>>> On Fri, Oct 1, 2010 at 5:02 AM, Kumar Gala<galak@kernel.crashing.org>   wrote:
>>>>
>>>> On Oct 1, 2010, at 2:05 AM, Ian Munsie wrote:
>>>>
>>>>> Some PowerPC processors can be run in either big or little endian modes, some
>>>>> others can map selected pages of memory as little endian, which allows the same
>>>>> thing. Until now we have only supported the default big endian mode in Linux.
>>>>> This patch set introduces little endian support for the 44x family of PowerPC
>>>>> processors.
>>>>
>>>>    From a community aspect is anyone actually going to use this?  Is this going to be the equivalent of voyager on x86?  I've got nothing against some of the endian clean ups this introduces.  However the changes to misc_32.S are a bit ugly from a readability point of view.  Just seems like this is likely to bit-rot pretty quickly.
>>>
>>> I'm with Kumar on this one.  Why would we want to support this?  I
>>> can't say I would be very willing to help anyone run in LE mode, let
>>> alone have it randomly selectable.
>>
>> Indeed, I thought we had killed that Windows-NT dog ~15 years ago :-)
>
> Actually this has more to do with having to deal with code written for
> ARM LE :-)

The comment was mostly aimed as a remnder of the main reason this was considered
a long time ago.

I understand that the world has moved on, and sadly the vast majority
of hardware is now little endian (although it still baffles me why anyone
would think that way...)

-- 
------------------------------------------------------------
Gary Thomas                 |  Consulting for the
MLB Associates              |    Embedded world
------------------------------------------------------------

^ permalink raw reply

* RE: [RFC] irq: Migrate powerpc virq subsystem into generic code
From: Lorenzo Pieralisi @ 2010-10-01 12:44 UTC (permalink / raw)
  To: 'Grant Likely', benh, devicetree-discuss, linuxppc-dev
In-Reply-To: <20100922203117.10426.69278.stgit@angua>

Hi Grant, Ben, all

> -----Original Message-----
> From: devicetree-discuss-
> bounces+lorenzo.pieralisi=arm.com@lists.ozlabs.org [mailto:devicetree-
> discuss-bounces+lorenzo.pieralisi=arm.com@lists.ozlabs.org] On Behalf
> Of Grant Likely
> Sent: 22 September 2010 21:33
> To: benh@kernel.crashing.org; devicetree-discuss@lists.ozlabs.org;
> linuxppc-dev@lists.ozlabs.org
> Subject: [RFC] irq: Migrate powerpc virq subsystem into generic code
> 
> Being able to dynamically manage linux irq ranges is useful.  Migrate
> the powerpc virq code into common code so that other architectures can
> use it.
> 
> This patch also removes the unused irq_early_init() references.
> 
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> Only compile tested; but I wanted to get this out for comments.  I
> think this is the right set of routines to generalize for virq on
> other architectures.
> 
> g.

I have a question on the PowerPC IRQ layer and how to use it
for device drivers and device tree in general.

A device such as eth smsc911x (example) requires the platform_data 
pointer to specify irq sense/level to programme the chip accordingly. 
As agreed with Grant these pieces of information should be retrieved 
from the device tree, from the interrupt-specifier.

Now: the device driver should be interrupt-controller agnostic, so
I cannot just retrieve the interrupts property at probe and decode it
in order to get the interrupt sense/level bits (the driver has no clue 
about the interrupt-controller irq flags encoding).
I need to associate a irq_host to the interrupt controller node, with
a proper xlate function to correctly decode the interrupt-specifier 
and set the irq type accordingly (irq_create_of_mapping(),
for now 1:1 on ARM, so useless from this standpoint).

At platform device init time, of_irq_to_resource() is called to parse
and map irqs; if we code the irq_host correctly for the ARM GIC for
instance the xlate function gets called and irq type set accordingly
(and maybe the function could set platform_device IRQ resource 
flags as well ?)

At driver dt probe, from the hwirq number defined in "interrupts"
the driver retrieves the virq, hence sense/level flags and use them,
or just use the platform_device IRQ resource flags if set properly
by the OF layer. 

Correct ?

If yes I will have a stab at it on a ARM platform with complex
IRQ routing.

Thank you very much.

Cheers,
Lorenzo

> 
>  arch/microblaze/kernel/setup.c |    2
>  arch/powerpc/Kconfig           |    3
>  arch/powerpc/include/asm/irq.h |  270 ----------------
>  arch/powerpc/kernel/irq.c      |  659 --------------------------------
> ------
>  include/linux/virq.h           |  302 ++++++++++++++++++
>  kernel/irq/Makefile            |    1
>  kernel/irq/virq.c              |  687
> ++++++++++++++++++++++++++++++++++++++++
>  7 files changed, 995 insertions(+), 929 deletions(-)
>  create mode 100644 include/linux/virq.h
>  create mode 100644 kernel/irq/virq.c
> 
> diff --git a/arch/microblaze/kernel/setup.c
> b/arch/microblaze/kernel/setup.c
> index f5f7688..39cf20d 100644
> --- a/arch/microblaze/kernel/setup.c
> +++ b/arch/microblaze/kernel/setup.c
> @@ -51,8 +51,6 @@ void __init setup_arch(char **cmdline_p)
> 
>  	unflatten_device_tree();
> 
> -	/* NOTE I think that this function is not necessary to call */
> -	/* irq_early_init(); */
>  	setup_cpuinfo();
> 
>  	microblaze_cache_init();
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 631e5a0..cc06e59 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -146,6 +146,9 @@ config EARLY_PRINTK
>  	bool
>  	default y
> 
> +config VIRQ
> +	def_bool y
> +
>  config COMPAT
>  	bool
>  	default y if PPC64
> diff --git a/arch/powerpc/include/asm/irq.h
> b/arch/powerpc/include/asm/irq.h
> index 67ab5fb..6dea0cb 100644
> --- a/arch/powerpc/include/asm/irq.h
> +++ b/arch/powerpc/include/asm/irq.h
> @@ -17,10 +17,6 @@
>  #include <asm/atomic.h>
> 
> 
> -/* Define a way to iterate across irqs. */
> -#define for_each_irq(i) \
> -	for ((i) = 0; (i) < NR_IRQS; ++(i))
> -
>  extern atomic_t ppc_n_lost_interrupts;
> 
>  /* This number is used when no interrupt has been assigned */
> @@ -41,270 +37,6 @@ extern atomic_t ppc_n_lost_interrupts;
>  /* Same thing, used by the generic IRQ code */
>  #define NR_IRQS_LEGACY		NUM_ISA_INTERRUPTS
> 
> -/* This type is the placeholder for a hardware interrupt number. It
> has to
> - * be big enough to enclose whatever representation is used by a given
> - * platform.
> - */
> -typedef unsigned long irq_hw_number_t;
> -
> -/* Interrupt controller "host" data structure. This could be defined
> as a
> - * irq domain controller. That is, it handles the mapping between
> hardware
> - * and virtual interrupt numbers for a given interrupt domain. The
> host
> - * structure is generally created by the PIC code for a given PIC
> instance
> - * (though a host can cover more than one PIC if they have a flat
> number
> - * model). It's the host callbacks that are responsible for setting
> the
> - * irq_chip on a given irq_desc after it's been mapped.
> - *
> - * The host code and data structures are fairly agnostic to the fact
> that
> - * we use an open firmware device-tree. We do have references to
> struct
> - * device_node in two places: in irq_find_host() to find the host
> matching
> - * a given interrupt controller node, and of course as an argument to
> its
> - * counterpart host->ops->match() callback. However, those are treated
> as
> - * generic pointers by the core and the fact that it's actually a
> device-node
> - * pointer is purely a convention between callers and implementation.
> This
> - * code could thus be used on other architectures by replacing those
> two
> - * by some sort of arch-specific void * "token" used to identify
> interrupt
> - * controllers.
> - */
> -struct irq_host;
> -struct radix_tree_root;
> -
> -/* Functions below are provided by the host and called whenever a new
> mapping
> - * is created or an old mapping is disposed. The host can then proceed
> to
> - * whatever internal data structures management is required. It also
> needs
> - * to setup the irq_desc when returning from map().
> - */
> -struct irq_host_ops {
> -	/* Match an interrupt controller device node to a host, returns
> -	 * 1 on a match
> -	 */
> -	int (*match)(struct irq_host *h, struct device_node *node);
> -
> -	/* Create or update a mapping between a virtual irq number and a
> hw
> -	 * irq number. This is called only once for a given mapping.
> -	 */
> -	int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t
> hw);
> -
> -	/* Dispose of such a mapping */
> -	void (*unmap)(struct irq_host *h, unsigned int virq);
> -
> -	/* Update of such a mapping  */
> -	void (*remap)(struct irq_host *h, unsigned int virq,
> irq_hw_number_t hw);
> -
> -	/* Translate device-tree interrupt specifier from raw format
> coming
> -	 * from the firmware to a irq_hw_number_t (interrupt line number)
> and
> -	 * type (sense) that can be passed to set_irq_type(). In the
> absence
> -	 * of this callback, irq_create_of_mapping() and
> irq_of_parse_and_map()
> -	 * will return the hw number in the first cell and IRQ_TYPE_NONE
> for
> -	 * the type (which amount to keeping whatever default value the
> -	 * interrupt controller has for that line)
> -	 */
> -	int (*xlate)(struct irq_host *h, struct device_node *ctrler,
> -		     const u32 *intspec, unsigned int intsize,
> -		     irq_hw_number_t *out_hwirq, unsigned int *out_type);
> -};
> -
> -struct irq_host {
> -	struct list_head	link;
> -
> -	/* type of reverse mapping technique */
> -	unsigned int		revmap_type;
> -#define IRQ_HOST_MAP_LEGACY     0 /* legacy 8259, gets irqs 1..15 */
> -#define IRQ_HOST_MAP_NOMAP	1 /* no fast reverse mapping */
> -#define IRQ_HOST_MAP_LINEAR	2 /* linear map of interrupts */
> -#define IRQ_HOST_MAP_TREE	3 /* radix tree */
> -	union {
> -		struct {
> -			unsigned int size;
> -			unsigned int *revmap;
> -		} linear;
> -		struct radix_tree_root tree;
> -	} revmap_data;
> -	struct irq_host_ops	*ops;
> -	void			*host_data;
> -	irq_hw_number_t		inval_irq;
> -
> -	/* Optional device node pointer */
> -	struct device_node	*of_node;
> -};
> -
> -/* The main irq map itself is an array of NR_IRQ entries containing
> the
> - * associate host and irq number. An entry with a host of NULL is
> free.
> - * An entry can be allocated if it's free, the allocator always then
> sets
> - * hwirq first to the host's invalid irq number and then fills ops.
> - */
> -struct irq_map_entry {
> -	irq_hw_number_t	hwirq;
> -	struct irq_host	*host;
> -};
> -
> -extern struct irq_map_entry irq_map[NR_IRQS];
> -
> -extern irq_hw_number_t virq_to_hw(unsigned int virq);
> -
> -/**
> - * irq_alloc_host - Allocate a new irq_host data structure
> - * @of_node: optional device-tree node of the interrupt controller
> - * @revmap_type: type of reverse mapping to use
> - * @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map
> - * @ops: map/unmap host callbacks
> - * @inval_irq: provide a hw number in that host space that is always
> invalid
> - *
> - * Allocates and initialize and irq_host structure. Note that in the
> case of
> - * IRQ_HOST_MAP_LEGACY, the map() callback will be called before this
> returns
> - * for all legacy interrupts except 0 (which is always the invalid irq
> for
> - * a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is
> allocated by
> - * this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be
> allocated
> - * later during boot automatically (the reverse mapping will use the
> slow path
> - * until that happens).
> - */
> -extern struct irq_host *irq_alloc_host(struct device_node *of_node,
> -				       unsigned int revmap_type,
> -				       unsigned int revmap_arg,
> -				       struct irq_host_ops *ops,
> -				       irq_hw_number_t inval_irq);
> -
> -
> -/**
> - * irq_find_host - Locates a host for a given device node
> - * @node: device-tree node of the interrupt controller
> - */
> -extern struct irq_host *irq_find_host(struct device_node *node);
> -
> -
> -/**
> - * irq_set_default_host - Set a "default" host
> - * @host: default host pointer
> - *
> - * For convenience, it's possible to set a "default" host that will be
> used
> - * whenever NULL is passed to irq_create_mapping(). It makes life
> easier for
> - * platforms that want to manipulate a few hard coded interrupt
> numbers that
> - * aren't properly represented in the device-tree.
> - */
> -extern void irq_set_default_host(struct irq_host *host);
> -
> -
> -/**
> - * irq_set_virq_count - Set the maximum number of virt irqs
> - * @count: number of linux virtual irqs, capped with NR_IRQS
> - *
> - * This is mainly for use by platforms like iSeries who want to
> program
> - * the virtual irq number in the controller to avoid the reverse
> mapping
> - */
> -extern void irq_set_virq_count(unsigned int count);
> -
> -
> -/**
> - * irq_create_mapping - Map a hardware interrupt into linux virq space
> - * @host: host owning this hardware interrupt or NULL for default host
> - * @hwirq: hardware irq number in that host space
> - *
> - * Only one mapping per hardware interrupt is permitted. Returns a
> linux
> - * virq number.
> - * If the sense/trigger is to be specified, set_irq_type() should be
> called
> - * on the number returned from that call.
> - */
> -extern unsigned int irq_create_mapping(struct irq_host *host,
> -				       irq_hw_number_t hwirq);
> -
> -
> -/**
> - * irq_dispose_mapping - Unmap an interrupt
> - * @virq: linux virq number of the interrupt to unmap
> - */
> -extern void irq_dispose_mapping(unsigned int virq);
> -
> -/**
> - * irq_find_mapping - Find a linux virq from an hw irq number.
> - * @host: host owning this hardware interrupt
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is a slow path, for use by generic code. It's expected that an
> - * irq controller implementation directly calls the appropriate low
> level
> - * mapping function.
> - */
> -extern unsigned int irq_find_mapping(struct irq_host *host,
> -				     irq_hw_number_t hwirq);
> -
> -/**
> - * irq_create_direct_mapping - Allocate a virq for direct mapping
> - * @host: host to allocate the virq for or NULL for default host
> - *
> - * This routine is used for irq controllers which can choose the
> hardware
> - * interrupt numbers they generate. In such a case it's simplest to
> use
> - * the linux virq as the hardware interrupt number.
> - */
> -extern unsigned int irq_create_direct_mapping(struct irq_host *host);
> -
> -/**
> - * irq_radix_revmap_insert - Insert a hw irq to linux virq number
> mapping.
> - * @host: host owning this hardware interrupt
> - * @virq: linux irq number
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is for use by irq controllers that use a radix tree reverse
> - * mapping for fast lookup.
> - */
> -extern void irq_radix_revmap_insert(struct irq_host *host, unsigned
> int virq,
> -				    irq_hw_number_t hwirq);
> -
> -/**
> - * irq_radix_revmap_lookup - Find a linux virq from a hw irq number.
> - * @host: host owning this hardware interrupt
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is a fast path, for use by irq controller code that uses radix
> tree
> - * revmaps
> - */
> -extern unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> -					    irq_hw_number_t hwirq);
> -
> -/**
> - * irq_linear_revmap - Find a linux virq from a hw irq number.
> - * @host: host owning this hardware interrupt
> - * @hwirq: hardware irq number in that host space
> - *
> - * This is a fast path, for use by irq controller code that uses
> linear
> - * revmaps. It does fallback to the slow path if the revmap doesn't
> exist
> - * yet and will create the revmap entry with appropriate locking
> - */
> -
> -extern unsigned int irq_linear_revmap(struct irq_host *host,
> -				      irq_hw_number_t hwirq);
> -
> -
> -
> -/**
> - * irq_alloc_virt - Allocate virtual irq numbers
> - * @host: host owning these new virtual irqs
> - * @count: number of consecutive numbers to allocate
> - * @hint: pass a hint number, the allocator will try to use a 1:1
> mapping
> - *
> - * This is a low level function that is used internally by
> irq_create_mapping()
> - * and that can be used by some irq controllers implementations for
> things
> - * like allocating ranges of numbers for MSIs. The revmaps are left
> untouched.
> - */
> -extern unsigned int irq_alloc_virt(struct irq_host *host,
> -				   unsigned int count,
> -				   unsigned int hint);
> -
> -/**
> - * irq_free_virt - Free virtual irq numbers
> - * @virq: virtual irq number of the first interrupt to free
> - * @count: number of interrupts to free
> - *
> - * This function is the opposite of irq_alloc_virt. It will not clear
> reverse
> - * maps, this should be done previously by unmap'ing the interrupt. In
> fact,
> - * all interrupts covered by the range being freed should have been
> unmapped
> - * prior to calling this.
> - */
> -extern void irq_free_virt(unsigned int virq, unsigned int count);
> -
> -/**
> - * irq_early_init - Init irq remapping subsystem
> - */
> -extern void irq_early_init(void);
> -
>  static __inline__ int irq_canonicalize(int irq)
>  {
>  	return irq;
> @@ -342,5 +74,7 @@ extern int call_handle_irq(int irq, void *p1,
>  			   struct thread_info *tp, void *func);
>  extern void do_IRQ(struct pt_regs *regs);
> 
> +#include <linux/virq.h>
> +
>  #endif /* _ASM_IRQ_H */
>  #endif /* __KERNEL__ */
> diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
> index 4a65386..86d8e42 100644
> --- a/arch/powerpc/kernel/irq.c
> +++ b/arch/powerpc/kernel/irq.c
> @@ -523,553 +523,6 @@ void do_softirq(void)
>  }
> 
> 
> -/*
> - * IRQ controller and virtual interrupts
> - */
> -
> -static LIST_HEAD(irq_hosts);
> -static DEFINE_RAW_SPINLOCK(irq_big_lock);
> -static unsigned int revmap_trees_allocated;
> -static DEFINE_MUTEX(revmap_trees_mutex);
> -struct irq_map_entry irq_map[NR_IRQS];
> -static unsigned int irq_virq_count = NR_IRQS;
> -static struct irq_host *irq_default_host;
> -
> -irq_hw_number_t virq_to_hw(unsigned int virq)
> -{
> -	return irq_map[virq].hwirq;
> -}
> -EXPORT_SYMBOL_GPL(virq_to_hw);
> -
> -static int default_irq_host_match(struct irq_host *h, struct
> device_node *np)
> -{
> -	return h->of_node != NULL && h->of_node == np;
> -}
> -
> -struct irq_host *irq_alloc_host(struct device_node *of_node,
> -				unsigned int revmap_type,
> -				unsigned int revmap_arg,
> -				struct irq_host_ops *ops,
> -				irq_hw_number_t inval_irq)
> -{
> -	struct irq_host *host;
> -	unsigned int size = sizeof(struct irq_host);
> -	unsigned int i;
> -	unsigned int *rmap;
> -	unsigned long flags;
> -
> -	/* Allocate structure and revmap table if using linear mapping */
> -	if (revmap_type == IRQ_HOST_MAP_LINEAR)
> -		size += revmap_arg * sizeof(unsigned int);
> -	host = zalloc_maybe_bootmem(size, GFP_KERNEL);
> -	if (host == NULL)
> -		return NULL;
> -
> -	/* Fill structure */
> -	host->revmap_type = revmap_type;
> -	host->inval_irq = inval_irq;
> -	host->ops = ops;
> -	host->of_node = of_node_get(of_node);
> -
> -	if (host->ops->match == NULL)
> -		host->ops->match = default_irq_host_match;
> -
> -	raw_spin_lock_irqsave(&irq_big_lock, flags);
> -
> -	/* If it's a legacy controller, check for duplicates and
> -	 * mark it as allocated (we use irq 0 host pointer for that
> -	 */
> -	if (revmap_type == IRQ_HOST_MAP_LEGACY) {
> -		if (irq_map[0].host != NULL) {
> -			raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -			/* If we are early boot, we can't free the
structure,
> -			 * too bad...
> -			 * this will be fixed once slab is made available
> early
> -			 * instead of the current cruft
> -			 */
> -			if (mem_init_done)
> -				kfree(host);
> -			return NULL;
> -		}
> -		irq_map[0].host = host;
> -	}
> -
> -	list_add(&host->link, &irq_hosts);
> -	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -
> -	/* Additional setups per revmap type */
> -	switch(revmap_type) {
> -	case IRQ_HOST_MAP_LEGACY:
> -		/* 0 is always the invalid number for legacy */
> -		host->inval_irq = 0;
> -		/* setup us as the host for all legacy interrupts */
> -		for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
> -			irq_map[i].hwirq = i;
> -			smp_wmb();
> -			irq_map[i].host = host;
> -			smp_wmb();
> -
> -			/* Clear norequest flags */
> -			irq_to_desc(i)->status &= ~IRQ_NOREQUEST;
> -
> -			/* Legacy flags are left to default at this point,
> -			 * one can then use irq_create_mapping() to
> -			 * explicitly change them
> -			 */
> -			ops->map(host, i, i);
> -		}
> -		break;
> -	case IRQ_HOST_MAP_LINEAR:
> -		rmap = (unsigned int *)(host + 1);
> -		for (i = 0; i < revmap_arg; i++)
> -			rmap[i] = NO_IRQ;
> -		host->revmap_data.linear.size = revmap_arg;
> -		smp_wmb();
> -		host->revmap_data.linear.revmap = rmap;
> -		break;
> -	default:
> -		break;
> -	}
> -
> -	pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type,
> host);
> -
> -	return host;
> -}
> -
> -struct irq_host *irq_find_host(struct device_node *node)
> -{
> -	struct irq_host *h, *found = NULL;
> -	unsigned long flags;
> -
> -	/* We might want to match the legacy controller last since
> -	 * it might potentially be set to match all interrupts in
> -	 * the absence of a device node. This isn't a problem so far
> -	 * yet though...
> -	 */
> -	raw_spin_lock_irqsave(&irq_big_lock, flags);
> -	list_for_each_entry(h, &irq_hosts, link)
> -		if (h->ops->match(h, node)) {
> -			found = h;
> -			break;
> -		}
> -	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -	return found;
> -}
> -EXPORT_SYMBOL_GPL(irq_find_host);
> -
> -void irq_set_default_host(struct irq_host *host)
> -{
> -	pr_debug("irq: Default host set to @0x%p\n", host);
> -
> -	irq_default_host = host;
> -}
> -
> -void irq_set_virq_count(unsigned int count)
> -{
> -	pr_debug("irq: Trying to set virq count to %d\n", count);
> -
> -	BUG_ON(count < NUM_ISA_INTERRUPTS);
> -	if (count < NR_IRQS)
> -		irq_virq_count = count;
> -}
> -
> -static int irq_setup_virq(struct irq_host *host, unsigned int virq,
> -			    irq_hw_number_t hwirq)
> -{
> -	struct irq_desc *desc;
> -
> -	desc = irq_to_desc_alloc_node(virq, 0);
> -	if (!desc) {
> -		pr_debug("irq: -> allocating desc failed\n");
> -		goto error;
> -	}
> -
> -	/* Clear IRQ_NOREQUEST flag */
> -	desc->status &= ~IRQ_NOREQUEST;
> -
> -	/* map it */
> -	smp_wmb();
> -	irq_map[virq].hwirq = hwirq;
> -	smp_mb();
> -
> -	if (host->ops->map(host, virq, hwirq)) {
> -		pr_debug("irq: -> mapping failed, freeing\n");
> -		goto error;
> -	}
> -
> -	return 0;
> -
> -error:
> -	irq_free_virt(virq, 1);
> -	return -1;
> -}
> -
> -unsigned int irq_create_direct_mapping(struct irq_host *host)
> -{
> -	unsigned int virq;
> -
> -	if (host == NULL)
> -		host = irq_default_host;
> -
> -	BUG_ON(host == NULL);
> -	WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP);
> -
> -	virq = irq_alloc_virt(host, 1, 0);
> -	if (virq == NO_IRQ) {
> -		pr_debug("irq: create_direct virq allocation failed\n");
> -		return NO_IRQ;
> -	}
> -
> -	pr_debug("irq: create_direct obtained virq %d\n", virq);
> -
> -	if (irq_setup_virq(host, virq, virq))
> -		return NO_IRQ;
> -
> -	return virq;
> -}
> -
> -unsigned int irq_create_mapping(struct irq_host *host,
> -				irq_hw_number_t hwirq)
> -{
> -	unsigned int virq, hint;
> -
> -	pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
> -
> -	/* Look for default host if nececssary */
> -	if (host == NULL)
> -		host = irq_default_host;
> -	if (host == NULL) {
> -		printk(KERN_WARNING "irq_create_mapping called for"
> -		       " NULL host, hwirq=%lx\n", hwirq);
> -		WARN_ON(1);
> -		return NO_IRQ;
> -	}
> -	pr_debug("irq: -> using host @%p\n", host);
> -
> -	/* Check if mapping already exist, if it does, call
> -	 * host->ops->map() to update the flags
> -	 */
> -	virq = irq_find_mapping(host, hwirq);
> -	if (virq != NO_IRQ) {
> -		if (host->ops->remap)
> -			host->ops->remap(host, virq, hwirq);
> -		pr_debug("irq: -> existing mapping on virq %d\n", virq);
> -		return virq;
> -	}
> -
> -	/* Get a virtual interrupt number */
> -	if (host->revmap_type == IRQ_HOST_MAP_LEGACY) {
> -		/* Handle legacy */
> -		virq = (unsigned int)hwirq;
> -		if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
> -			return NO_IRQ;
> -		return virq;
> -	} else {
> -		/* Allocate a virtual interrupt number */
> -		hint = hwirq % irq_virq_count;
> -		virq = irq_alloc_virt(host, 1, hint);
> -		if (virq == NO_IRQ) {
> -			pr_debug("irq: -> virq allocation failed\n");
> -			return NO_IRQ;
> -		}
> -	}
> -
> -	if (irq_setup_virq(host, virq, hwirq))
> -		return NO_IRQ;
> -
> -	printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq
> %u\n",
> -		hwirq, host->of_node ? host->of_node->full_name : "null",
> virq);
> -
> -	return virq;
> -}
> -EXPORT_SYMBOL_GPL(irq_create_mapping);
> -
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> -				   const u32 *intspec, unsigned int intsize)
> -{
> -	struct irq_host *host;
> -	irq_hw_number_t hwirq;
> -	unsigned int type = IRQ_TYPE_NONE;
> -	unsigned int virq;
> -
> -	if (controller == NULL)
> -		host = irq_default_host;
> -	else
> -		host = irq_find_host(controller);
> -	if (host == NULL) {
> -		printk(KERN_WARNING "irq: no irq host found for %s !\n",
> -		       controller->full_name);
> -		return NO_IRQ;
> -	}
> -
> -	/* If host has no translation, then we assume interrupt line */
> -	if (host->ops->xlate == NULL)
> -		hwirq = intspec[0];
> -	else {
> -		if (host->ops->xlate(host, controller, intspec, intsize,
> -				     &hwirq, &type))
> -			return NO_IRQ;
> -	}
> -
> -	/* Create mapping */
> -	virq = irq_create_mapping(host, hwirq);
> -	if (virq == NO_IRQ)
> -		return virq;
> -
> -	/* Set type if specified and different than the current one */
> -	if (type != IRQ_TYPE_NONE &&
> -	    type != (irq_to_desc(virq)->status & IRQF_TRIGGER_MASK))
> -		set_irq_type(virq, type);
> -	return virq;
> -}
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> -
> -void irq_dispose_mapping(unsigned int virq)
> -{
> -	struct irq_host *host;
> -	irq_hw_number_t hwirq;
> -
> -	if (virq == NO_IRQ)
> -		return;
> -
> -	host = irq_map[virq].host;
> -	WARN_ON (host == NULL);
> -	if (host == NULL)
> -		return;
> -
> -	/* Never unmap legacy interrupts */
> -	if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> -		return;
> -
> -	/* remove chip and handler */
> -	set_irq_chip_and_handler(virq, NULL, NULL);
> -
> -	/* Make sure it's completed */
> -	synchronize_irq(virq);
> -
> -	/* Tell the PIC about it */
> -	if (host->ops->unmap)
> -		host->ops->unmap(host, virq);
> -	smp_mb();
> -
> -	/* Clear reverse map */
> -	hwirq = irq_map[virq].hwirq;
> -	switch(host->revmap_type) {
> -	case IRQ_HOST_MAP_LINEAR:
> -		if (hwirq < host->revmap_data.linear.size)
> -			host->revmap_data.linear.revmap[hwirq] = NO_IRQ;
> -		break;
> -	case IRQ_HOST_MAP_TREE:
> -		/*
> -		 * Check if radix tree allocated yet, if not then nothing
> to
> -		 * remove.
> -		 */
> -		smp_rmb();
> -		if (revmap_trees_allocated < 1)
> -			break;
> -		mutex_lock(&revmap_trees_mutex);
> -		radix_tree_delete(&host->revmap_data.tree, hwirq);
> -		mutex_unlock(&revmap_trees_mutex);
> -		break;
> -	}
> -
> -	/* Destroy map */
> -	smp_mb();
> -	irq_map[virq].hwirq = host->inval_irq;
> -
> -	/* Set some flags */
> -	irq_to_desc(virq)->status |= IRQ_NOREQUEST;
> -
> -	/* Free it */
> -	irq_free_virt(virq, 1);
> -}
> -EXPORT_SYMBOL_GPL(irq_dispose_mapping);
> -
> -unsigned int irq_find_mapping(struct irq_host *host,
> -			      irq_hw_number_t hwirq)
> -{
> -	unsigned int i;
> -	unsigned int hint = hwirq % irq_virq_count;
> -
> -	/* Look for default host if nececssary */
> -	if (host == NULL)
> -		host = irq_default_host;
> -	if (host == NULL)
> -		return NO_IRQ;
> -
> -	/* legacy -> bail early */
> -	if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> -		return hwirq;
> -
> -	/* Slow path does a linear search of the map */
> -	if (hint < NUM_ISA_INTERRUPTS)
> -		hint = NUM_ISA_INTERRUPTS;
> -	i = hint;
> -	do  {
> -		if (irq_map[i].host == host &&
> -		    irq_map[i].hwirq == hwirq)
> -			return i;
> -		i++;
> -		if (i >= irq_virq_count)
> -			i = NUM_ISA_INTERRUPTS;
> -	} while(i != hint);
> -	return NO_IRQ;
> -}
> -EXPORT_SYMBOL_GPL(irq_find_mapping);
> -
> -
> -unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> -				     irq_hw_number_t hwirq)
> -{
> -	struct irq_map_entry *ptr;
> -	unsigned int virq;
> -
> -	WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> -
> -	/*
> -	 * Check if the radix tree exists and has bee initialized.
> -	 * If not, we fallback to slow mode
> -	 */
> -	if (revmap_trees_allocated < 2)
> -		return irq_find_mapping(host, hwirq);
> -
> -	/* Now try to resolve */
> -	/*
> -	 * No rcu_read_lock(ing) needed, the ptr returned can't go under
> us
> -	 * as it's referencing an entry in the static irq_map table.
> -	 */
> -	ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
> -
> -	/*
> -	 * If found in radix tree, then fine.
> -	 * Else fallback to linear lookup - this should not happen in
> practice
> -	 * as it means that we failed to insert the node in the radix
> tree.
> -	 */
> -	if (ptr)
> -		virq = ptr - irq_map;
> -	else
> -		virq = irq_find_mapping(host, hwirq);
> -
> -	return virq;
> -}
> -
> -void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
> -			     irq_hw_number_t hwirq)
> -{
> -
> -	WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> -
> -	/*
> -	 * Check if the radix tree exists yet.
> -	 * If not, then the irq will be inserted into the tree when it
> gets
> -	 * initialized.
> -	 */
> -	smp_rmb();
> -	if (revmap_trees_allocated < 1)
> -		return;
> -
> -	if (virq != NO_IRQ) {
> -		mutex_lock(&revmap_trees_mutex);
> -		radix_tree_insert(&host->revmap_data.tree, hwirq,
> -				  &irq_map[virq]);
> -		mutex_unlock(&revmap_trees_mutex);
> -	}
> -}
> -
> -unsigned int irq_linear_revmap(struct irq_host *host,
> -			       irq_hw_number_t hwirq)
> -{
> -	unsigned int *revmap;
> -
> -	WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR);
> -
> -	/* Check revmap bounds */
> -	if (unlikely(hwirq >= host->revmap_data.linear.size))
> -		return irq_find_mapping(host, hwirq);
> -
> -	/* Check if revmap was allocated */
> -	revmap = host->revmap_data.linear.revmap;
> -	if (unlikely(revmap == NULL))
> -		return irq_find_mapping(host, hwirq);
> -
> -	/* Fill up revmap with slow path if no mapping found */
> -	if (unlikely(revmap[hwirq] == NO_IRQ))
> -		revmap[hwirq] = irq_find_mapping(host, hwirq);
> -
> -	return revmap[hwirq];
> -}
> -
> -unsigned int irq_alloc_virt(struct irq_host *host,
> -			    unsigned int count,
> -			    unsigned int hint)
> -{
> -	unsigned long flags;
> -	unsigned int i, j, found = NO_IRQ;
> -
> -	if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
> -		return NO_IRQ;
> -
> -	raw_spin_lock_irqsave(&irq_big_lock, flags);
> -
> -	/* Use hint for 1 interrupt if any */
> -	if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
> -	    hint < irq_virq_count && irq_map[hint].host == NULL) {
> -		found = hint;
> -		goto hint_found;
> -	}
> -
> -	/* Look for count consecutive numbers in the allocatable
> -	 * (non-legacy) space
> -	 */
> -	for (i = NUM_ISA_INTERRUPTS, j = 0; i < irq_virq_count; i++) {
> -		if (irq_map[i].host != NULL)
> -			j = 0;
> -		else
> -			j++;
> -
> -		if (j == count) {
> -			found = i - count + 1;
> -			break;
> -		}
> -	}
> -	if (found == NO_IRQ) {
> -		raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -		return NO_IRQ;
> -	}
> - hint_found:
> -	for (i = found; i < (found + count); i++) {
> -		irq_map[i].hwirq = host->inval_irq;
> -		smp_wmb();
> -		irq_map[i].host = host;
> -	}
> -	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -	return found;
> -}
> -
> -void irq_free_virt(unsigned int virq, unsigned int count)
> -{
> -	unsigned long flags;
> -	unsigned int i;
> -
> -	WARN_ON (virq < NUM_ISA_INTERRUPTS);
> -	WARN_ON (count == 0 || (virq + count) > irq_virq_count);
> -
> -	raw_spin_lock_irqsave(&irq_big_lock, flags);
> -	for (i = virq; i < (virq + count); i++) {
> -		struct irq_host *host;
> -
> -		if (i < NUM_ISA_INTERRUPTS ||
> -		    (virq + count) > irq_virq_count)
> -			continue;
> -
> -		host = irq_map[i].host;
> -		irq_map[i].hwirq = host->inval_irq;
> -		smp_wmb();
> -		irq_map[i].host = NULL;
> -	}
> -	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -}
> -
>  int arch_early_irq_init(void)
>  {
>  	struct irq_desc *desc;
> @@ -1090,118 +543,6 @@ int arch_init_chip_data(struct irq_desc *desc,
> int node)
>  	return 0;
>  }
> 
> -/* We need to create the radix trees late */
> -static int irq_late_init(void)
> -{
> -	struct irq_host *h;
> -	unsigned int i;
> -
> -	/*
> -	 * No mutual exclusion with respect to accessors of the tree is
> needed
> -	 * here as the synchronization is done via the state variable
> -	 * revmap_trees_allocated.
> -	 */
> -	list_for_each_entry(h, &irq_hosts, link) {
> -		if (h->revmap_type == IRQ_HOST_MAP_TREE)
> -			INIT_RADIX_TREE(&h->revmap_data.tree, GFP_KERNEL);
> -	}
> -
> -	/*
> -	 * Make sure the radix trees inits are visible before setting
> -	 * the flag
> -	 */
> -	smp_wmb();
> -	revmap_trees_allocated = 1;
> -
> -	/*
> -	 * Insert the reverse mapping for those interrupts already
> present
> -	 * in irq_map[].
> -	 */
> -	mutex_lock(&revmap_trees_mutex);
> -	for (i = 0; i < irq_virq_count; i++) {
> -		if (irq_map[i].host &&
> -		    (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE))
> -
radix_tree_insert(&irq_map[i].host->revmap_data.tree,
> -					  irq_map[i].hwirq, &irq_map[i]);
> -	}
> -	mutex_unlock(&revmap_trees_mutex);
> -
> -	/*
> -	 * Make sure the radix trees insertions are visible before
> setting
> -	 * the flag
> -	 */
> -	smp_wmb();
> -	revmap_trees_allocated = 2;
> -
> -	return 0;
> -}
> -arch_initcall(irq_late_init);
> -
> -#ifdef CONFIG_VIRQ_DEBUG
> -static int virq_debug_show(struct seq_file *m, void *private)
> -{
> -	unsigned long flags;
> -	struct irq_desc *desc;
> -	const char *p;
> -	char none[] = "none";
> -	int i;
> -
> -	seq_printf(m, "%-5s  %-7s  %-15s  %s\n", "virq", "hwirq",
> -		      "chip name", "host name");
> -
> -	for (i = 1; i < nr_irqs; i++) {
> -		desc = irq_to_desc(i);
> -		if (!desc)
> -			continue;
> -
> -		raw_spin_lock_irqsave(&desc->lock, flags);
> -
> -		if (desc->action && desc->action->handler) {
> -			seq_printf(m, "%5d  ", i);
> -			seq_printf(m, "0x%05lx  ", virq_to_hw(i));
> -
> -			if (desc->chip && desc->chip->name)
> -				p = desc->chip->name;
> -			else
> -				p = none;
> -			seq_printf(m, "%-15s  ", p);
> -
> -			if (irq_map[i].host && irq_map[i].host->of_node)
> -				p = irq_map[i].host->of_node->full_name;
> -			else
> -				p = none;
> -			seq_printf(m, "%s\n", p);
> -		}
> -
> -		raw_spin_unlock_irqrestore(&desc->lock, flags);
> -	}
> -
> -	return 0;
> -}
> -
> -static int virq_debug_open(struct inode *inode, struct file *file)
> -{
> -	return single_open(file, virq_debug_show, inode->i_private);
> -}
> -
> -static const struct file_operations virq_debug_fops = {
> -	.open = virq_debug_open,
> -	.read = seq_read,
> -	.llseek = seq_lseek,
> -	.release = single_release,
> -};
> -
> -static int __init irq_debugfs_init(void)
> -{
> -	if (debugfs_create_file("virq_mapping", S_IRUGO,
> powerpc_debugfs_root,
> -				 NULL, &virq_debug_fops) == NULL)
> -		return -ENOMEM;
> -
> -	return 0;
> -}
> -__initcall(irq_debugfs_init);
> -#endif /* CONFIG_VIRQ_DEBUG */
> -
>  #ifdef CONFIG_PPC64
>  static int __init setup_noirqdistrib(char *str)
>  {
> diff --git a/include/linux/virq.h b/include/linux/virq.h
> new file mode 100644
> index 0000000..06035ef
> --- /dev/null
> +++ b/include/linux/virq.h
> @@ -0,0 +1,302 @@
> +/*
> + * Virtual IRQ infrastructure
> + *
> + * Virtual IRQs provides support for dynamically allocating ranges of
> IRQ
> + * numbers for use by interrupt controllers.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + */
> +
> +
> +#ifdef __KERNEL__
> +#ifndef _LINUX_VIRQ_H
> +#define _LINUX_VIRQ_H
> +
> +#include <asm/irq.h>
> +
> +#ifdef CONFIG_VIRQ
> +
> +/* Define a way to iterate across irqs. */
> +#define for_each_irq(i) \
> +	for ((i) = 0; (i) < NR_IRQS; ++(i))
> +
> +/* This type is the placeholder for a hardware interrupt number. It
> has to
> + * be big enough to enclose whatever representation is used by a given
> + * platform.
> + */
> +typedef unsigned long irq_hw_number_t;
> +
> +/* Interrupt controller "host" data structure. This could be defined
> as a
> + * irq domain controller. That is, it handles the mapping between
> hardware
> + * and virtual interrupt numbers for a given interrupt domain. The
> host
> + * structure is generally created by the PIC code for a given PIC
> instance
> + * (though a host can cover more than one PIC if they have a flat
> number
> + * model). It's the host callbacks that are responsible for setting
> the
> + * irq_chip on a given irq_desc after it's been mapped.
> + *
> + * The host code and data structures are fairly agnostic to the fact
> that
> + * we use an open firmware device-tree. We do have references to
> struct
> + * device_node in two places: in irq_find_host() to find the host
> matching
> + * a given interrupt controller node, and of course as an argument to
> its
> + * counterpart host->ops->match() callback. However, those are treated
> as
> + * generic pointers by the core and the fact that it's actually a
> device-node
> + * pointer is purely a convention between callers and implementation.
> This
> + * code could thus be used on other architectures by replacing those
> two
> + * by some sort of arch-specific void * "token" used to identify
> interrupt
> + * controllers.
> + */
> +struct irq_host;
> +struct radix_tree_root;
> +struct device_node;
> +
> +/**
> + * struct irq_host_ops - operations for managing per-domain hw irq
> numbers
> + *
> + * Functions below are provided by the host and called whenever a new
> mapping
> + * is created or an old mapping is disposed. The host can then proceed
> to
> + * whatever internal data structures management is required. It also
> needs
> + * to setup the irq_desc when returning from map().
> + */
> +struct irq_host_ops {
> +	/* Match an interrupt controller device node to a host, returns
> +	 * 1 on a match
> +	 */
> +	int (*match)(struct irq_host *h, struct device_node *node);
> +
> +	/* Create or update a mapping between a virtual irq number and a
> hw
> +	 * irq number. This is called only once for a given mapping.
> +	 */
> +	int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t
> hw);
> +
> +	/* Dispose of such a mapping */
> +	void (*unmap)(struct irq_host *h, unsigned int virq);
> +
> +	/* Update of such a mapping  */
> +	void (*remap)(struct irq_host *h, unsigned int virq,
> irq_hw_number_t hw);
> +
> +	/* Translate device-tree interrupt specifier from raw format
> coming
> +	 * from the firmware to a irq_hw_number_t (interrupt line number)
> and
> +	 * type (sense) that can be passed to set_irq_type(). In the
> absence
> +	 * of this callback, irq_create_of_mapping() and
> irq_of_parse_and_map()
> +	 * will return the hw number in the first cell and IRQ_TYPE_NONE
> for
> +	 * the type (which amount to keeping whatever default value the
> +	 * interrupt controller has for that line)
> +	 */
> +	int (*xlate)(struct irq_host *h, struct device_node *ctrler,
> +		     const u32 *intspec, unsigned int intsize,
> +		     irq_hw_number_t *out_hwirq, unsigned int *out_type);
> +};
> +
> +/**
> + * struct irq_host - a single irq domain. maps hw irq numbers to Linux
> irq.
> + * @link: entry in global irq_host list
> + * @revmap_type: Method of reverse mapping hwirq to Linux irq number
> + * @revmap_data: reverse map data
> + * @ops: irq domain operations (documented above)
> + * @host_data: irq controller driver data; core does not touch this
> pointer
> + * @inval_irq: hw irq number used for unassigned virqs
> + * @of_node: Optional pointer to the irq controllers device tree node.
> + *
> + * One irq_host is allocated for each range (domain) of Linux irq
> numbers
> + * allocated.  Typically, one irq_host is allocated per controller,
> but it
> + * is perfectly valid to manage multiple controllers with a single
> irq_host
> + * instance if need be.
> + */
> +struct irq_host {
> +	struct list_head	link;
> +
> +	/* type of reverse mapping technique */
> +	unsigned int		revmap_type;
> +#define IRQ_HOST_MAP_LEGACY     0 /* legacy 8259, gets irqs 1..15 */
> +#define IRQ_HOST_MAP_NOMAP	1 /* no fast reverse mapping */
> +#define IRQ_HOST_MAP_LINEAR	2 /* linear map of interrupts */
> +#define IRQ_HOST_MAP_TREE	3 /* radix tree */
> +	union {
> +		struct {
> +			unsigned int size;
> +			unsigned int *revmap;
> +		} linear;
> +		struct radix_tree_root tree;
> +	} revmap_data;
> +	struct irq_host_ops	*ops;
> +	void			*host_data;
> +	irq_hw_number_t		inval_irq;
> +
> +	/* Optional device node pointer */
> +	struct device_node	*of_node;
> +};
> +
> +/**
> + * irq_alloc_host() - Allocate a new irq_host data structure
> + * @of_node: optional device-tree node of the interrupt controller
> + * @revmap_type: type of reverse mapping to use
> + * @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map
> + * @ops: map/unmap host callbacks
> + * @inval_irq: provide a hw number in that host space that is always
> invalid
> + *
> + * Allocates and initialize and irq_host structure. Note that in the
> case of
> + * IRQ_HOST_MAP_LEGACY, the map() callback will be called before this
> returns
> + * for all legacy interrupts except 0 (which is always the invalid irq
> for
> + * a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is
> allocated by
> + * this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be
> allocated
> + * later during boot automatically (the reverse mapping will use the
> slow path
> + * until that happens).
> + */
> +extern struct irq_host *irq_alloc_host(struct device_node *of_node,
> +				       unsigned int revmap_type,
> +				       unsigned int revmap_arg,
> +				       struct irq_host_ops *ops,
> +				       irq_hw_number_t inval_irq);
> +
> +/* The main irq map itself is an array of NR_IRQ entries containing
> the
> + * associate host and irq number. An entry with a host of NULL is
> free.
> + * An entry can be allocated if it's free, the allocator always then
> sets
> + * hwirq first to the host's invalid irq number and then fills ops.
> + */
> +struct irq_map_entry {
> +	irq_hw_number_t	hwirq;
> +	struct irq_host	*host;
> +};
> +extern struct irq_map_entry irq_map[NR_IRQS];
> +
> +extern irq_hw_number_t virq_to_hw(unsigned int virq);
> +
> +/**
> + * irq_find_host - Locates a host for a given device node
> + * @node: device-tree node of the interrupt controller
> + */
> +extern struct irq_host *irq_find_host(struct device_node *node);
> +
> +/**
> + * irq_set_default_host - Set a "default" host
> + * @host: default host pointer
> + *
> + * For convenience, it's possible to set a "default" host that will be
> used
> + * whenever NULL is passed to irq_create_mapping(). It makes life
> easier for
> + * platforms that want to manipulate a few hard coded interrupt
> numbers that
> + * aren't properly represented in the device-tree.
> + */
> +extern void irq_set_default_host(struct irq_host *host);
> +
> +/**
> + * irq_set_virq_count - Set the maximum number of virt irqs
> + * @count: number of linux virtual irqs, capped with NR_IRQS
> + *
> + * This is mainly for use by platforms like iSeries who want to
> program
> + * the virtual irq number in the controller to avoid the reverse
> mapping
> + */
> +extern void irq_set_virq_count(unsigned int count);
> +
> +/**
> + * irq_create_mapping - Map a hardware interrupt into linux virq space
> + * @host: host owning this hardware interrupt or NULL for default host
> + * @hwirq: hardware irq number in that host space
> + *
> + * Only one mapping per hardware interrupt is permitted. Returns a
> linux
> + * virq number.
> + * If the sense/trigger is to be specified, set_irq_type() should be
> called
> + * on the number returned from that call.
> + */
> +extern unsigned int irq_create_mapping(struct irq_host *host,
> +				       irq_hw_number_t hwirq);
> +
> +/**
> + * irq_dispose_mapping - Unmap an interrupt
> + * @virq: linux virq number of the interrupt to unmap
> + */
> +extern void irq_dispose_mapping(unsigned int virq);
> +
> +/**
> + * irq_find_mapping - Find a linux virq from an hw irq number.
> + * @host: host owning this hardware interrupt
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is a slow path, for use by generic code. It's expected that an
> + * irq controller implementation directly calls the appropriate low
> level
> + * mapping function.
> + */
> +extern unsigned int irq_find_mapping(struct irq_host *host,
> +				     irq_hw_number_t hwirq);
> +
> +/**
> + * irq_create_direct_mapping - Allocate a virq for direct mapping
> + * @host: host to allocate the virq for or NULL for default host
> + *
> + * This routine is used for irq controllers which can choose the
> hardware
> + * interrupt numbers they generate. In such a case it's simplest to
> use
> + * the linux virq as the hardware interrupt number.
> + */
> +extern unsigned int irq_create_direct_mapping(struct irq_host *host);
> +
> +/**
> + * irq_radix_revmap_insert - Insert a hw irq to linux virq number
> mapping.
> + * @host: host owning this hardware interrupt
> + * @virq: linux irq number
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is for use by irq controllers that use a radix tree reverse
> + * mapping for fast lookup.
> + */
> +extern void irq_radix_revmap_insert(struct irq_host *host, unsigned
> int virq,
> +				    irq_hw_number_t hwirq);
> +
> +/**
> + * irq_radix_revmap_lookup - Find a linux virq from a hw irq number.
> + * @host: host owning this hardware interrupt
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is a fast path, for use by irq controller code that uses radix
> tree
> + * revmaps
> + */
> +extern unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> +					    irq_hw_number_t hwirq);
> +
> +/**
> + * irq_linear_revmap - Find a linux virq from a hw irq number.
> + * @host: host owning this hardware interrupt
> + * @hwirq: hardware irq number in that host space
> + *
> + * This is a fast path, for use by irq controller code that uses
> linear
> + * revmaps. It does fallback to the slow path if the revmap doesn't
> exist
> + * yet and will create the revmap entry with appropriate locking
> + */
> +
> +extern unsigned int irq_linear_revmap(struct irq_host *host,
> +				      irq_hw_number_t hwirq);
> +
> +
> +
> +/**
> + * irq_alloc_virt - Allocate virtual irq numbers
> + * @host: host owning these new virtual irqs
> + * @count: number of consecutive numbers to allocate
> + * @hint: pass a hint number, the allocator will try to use a 1:1
> mapping
> + *
> + * This is a low level function that is used internally by
> irq_create_mapping()
> + * and that can be used by some irq controllers implementations for
> things
> + * like allocating ranges of numbers for MSIs. The revmaps are left
> untouched.
> + */
> +extern unsigned int irq_alloc_virt(struct irq_host *host,
> +				   unsigned int count,
> +				   unsigned int hint);
> +
> +/**
> + * irq_free_virt - Free virtual irq numbers
> + * @virq: virtual irq number of the first interrupt to free
> + * @count: number of interrupts to free
> + *
> + * This function is the opposite of irq_alloc_virt. It will not clear
> reverse
> + * maps, this should be done previously by unmap'ing the interrupt. In
> fact,
> + * all interrupts covered by the range being freed should have been
> unmapped
> + * prior to calling this.
> + */
> +extern void irq_free_virt(unsigned int virq, unsigned int count);
> +
> +
> +#endif /* CONFIG_VIRQ */
> +
> +#endif /* _LINUX_VIRQ_H */
> +#endif /* __KERNEL__ */
> +
> diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
> index 7d04780..f5207dc 100644
> --- a/kernel/irq/Makefile
> +++ b/kernel/irq/Makefile
> @@ -1,5 +1,6 @@
> 
>  obj-y := handle.o manage.o spurious.o resend.o chip.o devres.o
> +obj-$(CONFIG_VIRQ) += virq.o
>  obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
>  obj-$(CONFIG_PROC_FS) += proc.o
>  obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
> diff --git a/kernel/irq/virq.c b/kernel/irq/virq.c
> new file mode 100644
> index 0000000..b3c0db3
> --- /dev/null
> +++ b/kernel/irq/virq.c
> @@ -0,0 +1,687 @@
> +/*
> + * Mapping support from per-controller hw irq numbers to linux irqs
> + *
> + *  Derived from arch/i386/kernel/irq.c
> + *    Copyright (C) 1992 Linus Torvalds
> + *  Adapted from arch/i386 by Gary Thomas
> + *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
> + *  Updated and modified by Cort Dougan <cort@fsmlabs.com>
> + *    Copyright (C) 1996-2001 Cort Dougan
> + *  Adapted for Power Macintosh by Paul Mackerras
> + *    Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
> + *  Generalized for virtual irq mapping on all platformes by Grant
> Likely
> + *    Copyright (C) 2010 Secret Lab Technologies Ltd.
> + *
> + * 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/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/radix-tree.h>
> +#include <linux/virq.h>
> +#include <linux/of_irq.h>
> +
> +/*
> + * IRQ controller and virtual interrupts
> + */
> +static LIST_HEAD(irq_hosts);
> +static DEFINE_RAW_SPINLOCK(irq_big_lock);
> +static unsigned int revmap_trees_allocated;
> +static DEFINE_MUTEX(revmap_trees_mutex);
> +struct irq_map_entry irq_map[NR_IRQS];
> +static unsigned int irq_virq_count = NR_IRQS;
> +static struct irq_host *irq_default_host;
> +
> +irq_hw_number_t virq_to_hw(unsigned int virq)
> +{
> +	return irq_map[virq].hwirq;
> +}
> +EXPORT_SYMBOL_GPL(virq_to_hw);
> +
> +static int default_irq_host_match(struct irq_host *h, struct
> device_node *np)
> +{
> +	return h->of_node != NULL && h->of_node == np;
> +}
> +
> +struct irq_host *irq_alloc_host(struct device_node *of_node,
> +				unsigned int revmap_type,
> +				unsigned int revmap_arg,
> +				struct irq_host_ops *ops,
> +				irq_hw_number_t inval_irq)
> +{
> +	struct irq_host *host;
> +	unsigned int size = sizeof(struct irq_host);
> +	unsigned int i;
> +	unsigned int *rmap;
> +	unsigned long flags;
> +
> +	/* Allocate structure and revmap table if using linear mapping */
> +	if (revmap_type == IRQ_HOST_MAP_LINEAR)
> +		size += revmap_arg * sizeof(unsigned int);
> +	host = zalloc_maybe_bootmem(size, GFP_KERNEL);
> +	if (host == NULL)
> +		return NULL;
> +
> +	/* Fill structure */
> +	host->revmap_type = revmap_type;
> +	host->inval_irq = inval_irq;
> +	host->ops = ops;
> +	host->of_node = of_node_get(of_node);
> +
> +	if (host->ops->match == NULL)
> +		host->ops->match = default_irq_host_match;
> +
> +	raw_spin_lock_irqsave(&irq_big_lock, flags);
> +
> +	/* If it's a legacy controller, check for duplicates and
> +	 * mark it as allocated (we use irq 0 host pointer for that
> +	 */
> +	if (revmap_type == IRQ_HOST_MAP_LEGACY) {
> +		if (irq_map[0].host != NULL) {
> +			raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +			/* If we are early boot, we can't free the
structure,
> +			 * too bad...
> +			 * this will be fixed once slab is made available
> early
> +			 * instead of the current cruft
> +			 */
> +			if (mem_init_done)
> +				kfree(host);
> +			return NULL;
> +		}
> +		irq_map[0].host = host;
> +	}
> +
> +	list_add(&host->link, &irq_hosts);
> +	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +
> +	/* Additional setups per revmap type */
> +	switch(revmap_type) {
> +	case IRQ_HOST_MAP_LEGACY:
> +		/* 0 is always the invalid number for legacy */
> +		host->inval_irq = 0;
> +		/* setup us as the host for all legacy interrupts */
> +		for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
> +			irq_map[i].hwirq = i;
> +			smp_wmb();
> +			irq_map[i].host = host;
> +			smp_wmb();
> +
> +			/* Clear norequest flags */
> +			irq_to_desc(i)->status &= ~IRQ_NOREQUEST;
> +
> +			/* Legacy flags are left to default at this point,
> +			 * one can then use irq_create_mapping() to
> +			 * explicitly change them
> +			 */
> +			ops->map(host, i, i);
> +		}
> +		break;
> +	case IRQ_HOST_MAP_LINEAR:
> +		rmap = (unsigned int *)(host + 1);
> +		for (i = 0; i < revmap_arg; i++)
> +			rmap[i] = NO_IRQ;
> +		host->revmap_data.linear.size = revmap_arg;
> +		smp_wmb();
> +		host->revmap_data.linear.revmap = rmap;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type,
> host);
> +
> +	return host;
> +}
> +
> +struct irq_host *irq_find_host(struct device_node *node)
> +{
> +	struct irq_host *h, *found = NULL;
> +	unsigned long flags;
> +
> +	/* We might want to match the legacy controller last since
> +	 * it might potentially be set to match all interrupts in
> +	 * the absence of a device node. This isn't a problem so far
> +	 * yet though...
> +	 */
> +	raw_spin_lock_irqsave(&irq_big_lock, flags);
> +	list_for_each_entry(h, &irq_hosts, link)
> +		if (h->ops->match(h, node)) {
> +			found = h;
> +			break;
> +		}
> +	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +	return found;
> +}
> +EXPORT_SYMBOL_GPL(irq_find_host);
> +
> +void irq_set_default_host(struct irq_host *host)
> +{
> +	pr_debug("irq: Default host set to @0x%p\n", host);
> +
> +	irq_default_host = host;
> +}
> +
> +void irq_set_virq_count(unsigned int count)
> +{
> +	pr_debug("irq: Trying to set virq count to %d\n", count);
> +
> +	BUG_ON(count < NUM_ISA_INTERRUPTS);
> +	if (count < NR_IRQS)
> +		irq_virq_count = count;
> +}
> +
> +static int irq_setup_virq(struct irq_host *host, unsigned int virq,
> +			    irq_hw_number_t hwirq)
> +{
> +	struct irq_desc *desc;
> +
> +	desc = irq_to_desc_alloc_node(virq, 0);
> +	if (!desc) {
> +		pr_debug("irq: -> allocating desc failed\n");
> +		goto error;
> +	}
> +
> +	/* Clear IRQ_NOREQUEST flag */
> +	desc->status &= ~IRQ_NOREQUEST;
> +
> +	/* map it */
> +	smp_wmb();
> +	irq_map[virq].hwirq = hwirq;
> +	smp_mb();
> +
> +	if (host->ops->map(host, virq, hwirq)) {
> +		pr_debug("irq: -> mapping failed, freeing\n");
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	irq_free_virt(virq, 1);
> +	return -1;
> +}
> +
> +unsigned int irq_create_direct_mapping(struct irq_host *host)
> +{
> +	unsigned int virq;
> +
> +	if (host == NULL)
> +		host = irq_default_host;
> +
> +	BUG_ON(host == NULL);
> +	WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP);
> +
> +	virq = irq_alloc_virt(host, 1, 0);
> +	if (virq == NO_IRQ) {
> +		pr_debug("irq: create_direct virq allocation failed\n");
> +		return NO_IRQ;
> +	}
> +
> +	pr_debug("irq: create_direct obtained virq %d\n", virq);
> +
> +	if (irq_setup_virq(host, virq, virq))
> +		return NO_IRQ;
> +
> +	return virq;
> +}
> +
> +unsigned int irq_create_mapping(struct irq_host *host,
> +				irq_hw_number_t hwirq)
> +{
> +	unsigned int virq, hint;
> +
> +	pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
> +
> +	/* Look for default host if nececssary */
> +	if (host == NULL)
> +		host = irq_default_host;
> +	if (host == NULL) {
> +		printk(KERN_WARNING "irq_create_mapping called for"
> +		       " NULL host, hwirq=%lx\n", hwirq);
> +		WARN_ON(1);
> +		return NO_IRQ;
> +	}
> +	pr_debug("irq: -> using host @%p\n", host);
> +
> +	/* Check if mapping already exist, if it does, call
> +	 * host->ops->map() to update the flags
> +	 */
> +	virq = irq_find_mapping(host, hwirq);
> +	if (virq != NO_IRQ) {
> +		if (host->ops->remap)
> +			host->ops->remap(host, virq, hwirq);
> +		pr_debug("irq: -> existing mapping on virq %d\n", virq);
> +		return virq;
> +	}
> +
> +	/* Get a virtual interrupt number */
> +	if (host->revmap_type == IRQ_HOST_MAP_LEGACY) {
> +		/* Handle legacy */
> +		virq = (unsigned int)hwirq;
> +		if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
> +			return NO_IRQ;
> +		return virq;
> +	} else {
> +		/* Allocate a virtual interrupt number */
> +		hint = hwirq % irq_virq_count;
> +		virq = irq_alloc_virt(host, 1, hint);
> +		if (virq == NO_IRQ) {
> +			pr_debug("irq: -> virq allocation failed\n");
> +			return NO_IRQ;
> +		}
> +	}
> +
> +	if (irq_setup_virq(host, virq, hwirq))
> +		return NO_IRQ;
> +
> +	printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq
> %u\n",
> +		hwirq, host->of_node ? host->of_node->full_name : "null",
> virq);
> +
> +	return virq;
> +}
> +EXPORT_SYMBOL_GPL(irq_create_mapping);
> +
> +unsigned int irq_create_of_mapping(struct device_node *controller,
> +				   const u32 *intspec, unsigned int intsize)
> +{
> +	struct irq_host *host;
> +	irq_hw_number_t hwirq;
> +	unsigned int type = IRQ_TYPE_NONE;
> +	unsigned int virq;
> +
> +	if (controller == NULL)
> +		host = irq_default_host;
> +	else
> +		host = irq_find_host(controller);
> +	if (host == NULL) {
> +		printk(KERN_WARNING "irq: no irq host found for %s !\n",
> +		       controller->full_name);
> +		return NO_IRQ;
> +	}
> +
> +	/* If host has no translation, then we assume interrupt line */
> +	if (host->ops->xlate == NULL)
> +		hwirq = intspec[0];
> +	else {
> +		if (host->ops->xlate(host, controller, intspec, intsize,
> +				     &hwirq, &type))
> +			return NO_IRQ;
> +	}
> +
> +	/* Create mapping */
> +	virq = irq_create_mapping(host, hwirq);
> +	if (virq == NO_IRQ)
> +		return virq;
> +
> +	/* Set type if specified and different than the current one */
> +	if (type != IRQ_TYPE_NONE &&
> +	    type != (irq_to_desc(virq)->status & IRQF_TRIGGER_MASK))
> +		set_irq_type(virq, type);
> +	return virq;
> +}
> +EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> +
> +void irq_dispose_mapping(unsigned int virq)
> +{
> +	struct irq_host *host;
> +	irq_hw_number_t hwirq;
> +
> +	if (virq == NO_IRQ)
> +		return;
> +
> +	host = irq_map[virq].host;
> +	WARN_ON (host == NULL);
> +	if (host == NULL)
> +		return;
> +
> +	/* Never unmap legacy interrupts */
> +	if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> +		return;
> +
> +	/* remove chip and handler */
> +	set_irq_chip_and_handler(virq, NULL, NULL);
> +
> +	/* Make sure it's completed */
> +	synchronize_irq(virq);
> +
> +	/* Tell the PIC about it */
> +	if (host->ops->unmap)
> +		host->ops->unmap(host, virq);
> +	smp_mb();
> +
> +	/* Clear reverse map */
> +	hwirq = irq_map[virq].hwirq;
> +	switch(host->revmap_type) {
> +	case IRQ_HOST_MAP_LINEAR:
> +		if (hwirq < host->revmap_data.linear.size)
> +			host->revmap_data.linear.revmap[hwirq] = NO_IRQ;
> +		break;
> +	case IRQ_HOST_MAP_TREE:
> +		/*
> +		 * Check if radix tree allocated yet, if not then nothing
> to
> +		 * remove.
> +		 */
> +		smp_rmb();
> +		if (revmap_trees_allocated < 1)
> +			break;
> +		mutex_lock(&revmap_trees_mutex);
> +		radix_tree_delete(&host->revmap_data.tree, hwirq);
> +		mutex_unlock(&revmap_trees_mutex);
> +		break;
> +	}
> +
> +	/* Destroy map */
> +	smp_mb();
> +	irq_map[virq].hwirq = host->inval_irq;
> +
> +	/* Set some flags */
> +	irq_to_desc(virq)->status |= IRQ_NOREQUEST;
> +
> +	/* Free it */
> +	irq_free_virt(virq, 1);
> +}
> +EXPORT_SYMBOL_GPL(irq_dispose_mapping);
> +
> +unsigned int irq_find_mapping(struct irq_host *host,
> +			      irq_hw_number_t hwirq)
> +{
> +	unsigned int i;
> +	unsigned int hint = hwirq % irq_virq_count;
> +
> +	/* Look for default host if nececssary */
> +	if (host == NULL)
> +		host = irq_default_host;
> +	if (host == NULL)
> +		return NO_IRQ;
> +
> +	/* legacy -> bail early */
> +	if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
> +		return hwirq;
> +
> +	/* Slow path does a linear search of the map */
> +	if (hint < NUM_ISA_INTERRUPTS)
> +		hint = NUM_ISA_INTERRUPTS;
> +	i = hint;
> +	do  {
> +		if (irq_map[i].host == host &&
> +		    irq_map[i].hwirq == hwirq)
> +			return i;
> +		i++;
> +		if (i >= irq_virq_count)
> +			i = NUM_ISA_INTERRUPTS;
> +	} while(i != hint);
> +	return NO_IRQ;
> +}
> +EXPORT_SYMBOL_GPL(irq_find_mapping);
> +
> +
> +unsigned int irq_radix_revmap_lookup(struct irq_host *host,
> +				     irq_hw_number_t hwirq)
> +{
> +	struct irq_map_entry *ptr;
> +	unsigned int virq;
> +
> +	WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> +
> +	/*
> +	 * Check if the radix tree exists and has bee initialized.
> +	 * If not, we fallback to slow mode
> +	 */
> +	if (revmap_trees_allocated < 2)
> +		return irq_find_mapping(host, hwirq);
> +
> +	/* Now try to resolve */
> +	/*
> +	 * No rcu_read_lock(ing) needed, the ptr returned can't go under
> us
> +	 * as it's referencing an entry in the static irq_map table.
> +	 */
> +	ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
> +
> +	/*
> +	 * If found in radix tree, then fine.
> +	 * Else fallback to linear lookup - this should not happen in
> practice
> +	 * as it means that we failed to insert the node in the radix
> tree.
> +	 */
> +	if (ptr)
> +		virq = ptr - irq_map;
> +	else
> +		virq = irq_find_mapping(host, hwirq);
> +
> +	return virq;
> +}
> +
> +void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq,
> +			     irq_hw_number_t hwirq)
> +{
> +
> +	WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
> +
> +	/*
> +	 * Check if the radix tree exists yet.
> +	 * If not, then the irq will be inserted into the tree when it
> gets
> +	 * initialized.
> +	 */
> +	smp_rmb();
> +	if (revmap_trees_allocated < 1)
> +		return;
> +
> +	if (virq != NO_IRQ) {
> +		mutex_lock(&revmap_trees_mutex);
> +		radix_tree_insert(&host->revmap_data.tree, hwirq,
> +				  &irq_map[virq]);
> +		mutex_unlock(&revmap_trees_mutex);
> +	}
> +}
> +
> +unsigned int irq_linear_revmap(struct irq_host *host,
> +			       irq_hw_number_t hwirq)
> +{
> +	unsigned int *revmap;
> +
> +	WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR);
> +
> +	/* Check revmap bounds */
> +	if (unlikely(hwirq >= host->revmap_data.linear.size))
> +		return irq_find_mapping(host, hwirq);
> +
> +	/* Check if revmap was allocated */
> +	revmap = host->revmap_data.linear.revmap;
> +	if (unlikely(revmap == NULL))
> +		return irq_find_mapping(host, hwirq);
> +
> +	/* Fill up revmap with slow path if no mapping found */
> +	if (unlikely(revmap[hwirq] == NO_IRQ))
> +		revmap[hwirq] = irq_find_mapping(host, hwirq);
> +
> +	return revmap[hwirq];
> +}
> +
> +unsigned int irq_alloc_virt(struct irq_host *host,
> +			    unsigned int count,
> +			    unsigned int hint)
> +{
> +	unsigned long flags;
> +	unsigned int i, j, found = NO_IRQ;
> +
> +	if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
> +		return NO_IRQ;
> +
> +	raw_spin_lock_irqsave(&irq_big_lock, flags);
> +
> +	/* Use hint for 1 interrupt if any */
> +	if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
> +	    hint < irq_virq_count && irq_map[hint].host == NULL) {
> +		found = hint;
> +		goto hint_found;
> +	}
> +
> +	/* Look for count consecutive numbers in the allocatable
> +	 * (non-legacy) space
> +	 */
> +	for (i = NUM_ISA_INTERRUPTS, j = 0; i < irq_virq_count; i++) {
> +		if (irq_map[i].host != NULL)
> +			j = 0;
> +		else
> +			j++;
> +
> +		if (j == count) {
> +			found = i - count + 1;
> +			break;
> +		}
> +	}
> +	if (found == NO_IRQ) {
> +		raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +		return NO_IRQ;
> +	}
> + hint_found:
> +	for (i = found; i < (found + count); i++) {
> +		irq_map[i].hwirq = host->inval_irq;
> +		smp_wmb();
> +		irq_map[i].host = host;
> +	}
> +	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +	return found;
> +}
> +
> +void irq_free_virt(unsigned int virq, unsigned int count)
> +{
> +	unsigned long flags;
> +	unsigned int i;
> +
> +	WARN_ON (virq < NUM_ISA_INTERRUPTS);
> +	WARN_ON (count == 0 || (virq + count) > irq_virq_count);
> +
> +	raw_spin_lock_irqsave(&irq_big_lock, flags);
> +	for (i = virq; i < (virq + count); i++) {
> +		struct irq_host *host;
> +
> +		if (i < NUM_ISA_INTERRUPTS ||
> +		    (virq + count) > irq_virq_count)
> +			continue;
> +
> +		host = irq_map[i].host;
> +		irq_map[i].hwirq = host->inval_irq;
> +		smp_wmb();
> +		irq_map[i].host = NULL;
> +	}
> +	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> +}
> +
> +/* We need to create the radix trees late */
> +static int irq_late_init(void)
> +{
> +	struct irq_host *h;
> +	unsigned int i;
> +
> +	/*
> +	 * No mutual exclusion with respect to accessors of the tree is
> needed
> +	 * here as the synchronization is done via the state variable
> +	 * revmap_trees_allocated.
> +	 */
> +	list_for_each_entry(h, &irq_hosts, link) {
> +		if (h->revmap_type == IRQ_HOST_MAP_TREE)
> +			INIT_RADIX_TREE(&h->revmap_data.tree, GFP_KERNEL);
> +	}
> +
> +	/*
> +	 * Make sure the radix trees inits are visible before setting
> +	 * the flag
> +	 */
> +	smp_wmb();
> +	revmap_trees_allocated = 1;
> +
> +	/*
> +	 * Insert the reverse mapping for those interrupts already
> present
> +	 * in irq_map[].
> +	 */
> +	mutex_lock(&revmap_trees_mutex);
> +	for (i = 0; i < irq_virq_count; i++) {
> +		if (irq_map[i].host &&
> +		    (irq_map[i].host->revmap_type == IRQ_HOST_MAP_TREE))
> +
radix_tree_insert(&irq_map[i].host->revmap_data.tree,
> +					  irq_map[i].hwirq, &irq_map[i]);
> +	}
> +	mutex_unlock(&revmap_trees_mutex);
> +
> +	/*
> +	 * Make sure the radix trees insertions are visible before
> setting
> +	 * the flag
> +	 */
> +	smp_wmb();
> +	revmap_trees_allocated = 2;
> +
> +	return 0;
> +}
> +arch_initcall(irq_late_init);
> +
> +#ifdef CONFIG_VIRQ_DEBUG
> +static int virq_debug_show(struct seq_file *m, void *private)
> +{
> +	unsigned long flags;
> +	struct irq_desc *desc;
> +	const char *p;
> +	char none[] = "none";
> +	int i;
> +
> +	seq_printf(m, "%-5s  %-7s  %-15s  %s\n", "virq", "hwirq",
> +		      "chip name", "host name");
> +
> +	for (i = 1; i < nr_irqs; i++) {
> +		desc = irq_to_desc(i);
> +		if (!desc)
> +			continue;
> +
> +		raw_spin_lock_irqsave(&desc->lock, flags);
> +
> +		if (desc->action && desc->action->handler) {
> +			seq_printf(m, "%5d  ", i);
> +			seq_printf(m, "0x%05lx  ", virq_to_hw(i));
> +
> +			if (desc->chip && desc->chip->name)
> +				p = desc->chip->name;
> +			else
> +				p = none;
> +			seq_printf(m, "%-15s  ", p);
> +
> +			if (irq_map[i].host && irq_map[i].host->of_node)
> +				p = irq_map[i].host->of_node->full_name;
> +			else
> +				p = none;
> +			seq_printf(m, "%s\n", p);
> +		}
> +
> +		raw_spin_unlock_irqrestore(&desc->lock, flags);
> +	}
> +
> +	return 0;
> +}
> +
> +static int virq_debug_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, virq_debug_show, inode->i_private);
> +}
> +
> +static const struct file_operations virq_debug_fops = {
> +	.open = virq_debug_open,
> +	.read = seq_read,
> +	.llseek = seq_lseek,
> +	.release = single_release,
> +};
> +
> +static int __init irq_debugfs_init(void)
> +{
> +	if (debugfs_create_file("virq_mapping", S_IRUGO,
> powerpc_debugfs_root,
> +				 NULL, &virq_debug_fops) == NULL)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +__initcall(irq_debugfs_init);
> +#endif /* CONFIG_VIRQ_DEBUG */
> +
> 
> _______________________________________________
> devicetree-discuss mailing list
> devicetree-discuss@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/devicetree-discuss

^ 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