* [PATCH 1/4] ARM: runtime patching of __virt_to_phys() and __phys_to_virt()
From: Russell King - ARM Linux @ 2011-01-04 8:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294129208-15201-2-git-send-email-nico@fluxnic.net>
This is basically my patch with a few blank lines removed, a couple
of \n's also removed, a #error if __virt_to_phys is defined by a platform,
a minor tweak to the assembly and it being only usable on PXA.
I much prefer my patch over this as anyone can use it. That's one of
the reasons why I arranged the code testing for __virt_to_phys as I
did, so the config option could be offered without having a big long
dependency list attached to it.
On Tue, Jan 04, 2011 at 03:20:05AM -0500, Nicolas Pitre wrote:
> On ARM it is common to find different offsets for the location of
> physical memory. In order to support multiple machine types with
> a single kernel binary, we need to make PHYS_OFFSET a variable.
> But turning PHYS_OFFSET into a global variable would impact performance
> of many hot paths.
>
> In the context of __virt_to_phys() and __phys_to_virt(), we currently have:
>
> #define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
>
> This normally translates into the following assembly instruction:
>
> add rx, rx, #(PHYS_OFFSET - PAGE_OFFSET)
>
> If we can assume that the difference between PHYS_OFFSET and PAGE_OFFSET
> will always fit into 8 bits shifted to the MSBs, then we can easily patch
> this difference into the corresponding assembly instructions at run time.
> This is like saying that phys and virt offsets will always be at least
> 16 MB aligned which is a pretty safe assumption.
>
> So the idea is to create a table of pointers to all those add instructions,
> and have the early boot code to walk and patch them up before the kernel
> gets to use them. Result is equivalent to a variable PHYS_OFFSET with
> next to zero performance impact compared to the constant PHYS_OFFSET.
>
> Right now, the difference between PHYS_OFFSET and PAGE_OFFSET is determined
> by the actual physical address the kernel is executing from upon start,
> assuming that the kernel is located within the first 16 MB of RAM.
>
> Thanks to Eric Miao and Russell King for their contributions to this patch.
>
> Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
> ---
> arch/arm/Kconfig | 5 ++++
> arch/arm/include/asm/memory.h | 51 +++++++++++++++++++++++++++++++++--------
> arch/arm/kernel/head.S | 35 ++++++++++++++++++++++++++++
> arch/arm/kernel/vmlinux.lds.S | 4 +++
> 4 files changed, 85 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index d56d21c0..136ed9b 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -187,6 +187,11 @@ config VECTORS_BASE
> help
> The base address of exception vectors.
>
> +config ARM_PATCH_PHYS_VIRT
> + bool
> + depends on EXPERIMENTAL
> + depends on !XIP && !THUMB2_KERNEL
> +
> source "init/Kconfig"
>
> source "kernel/Kconfig.freezer"
> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> index 23c2e8e..2783ce2 100644
> --- a/arch/arm/include/asm/memory.h
> +++ b/arch/arm/include/asm/memory.h
> @@ -133,16 +133,6 @@
> #endif
>
> /*
> - * Physical vs virtual RAM address space conversion. These are
> - * private definitions which should NOT be used outside memory.h
> - * files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
> - */
> -#ifndef __virt_to_phys
> -#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
> -#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
> -#endif
> -
> -/*
> * Convert a physical address to a Page Frame Number and back
> */
> #define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT)
> @@ -157,6 +147,47 @@
> #ifndef __ASSEMBLY__
>
> /*
> + * Physical vs virtual RAM address space conversion. These are
> + * private definitions which should NOT be used outside memory.h
> + * files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
> + */
> +#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
> +
> +#ifdef __virt_to_phys
> +#error "this machine configuration uses complex __virt_to_phys/__phys_to_virt and cannot use CONFIG_ARM_PATCH_PHYS_VIRT"
> +#endif
> +
> +#define __pv_stub(from,to,instr) \
> + __asm__( \
> + "1: " instr "\t%0, %1, %2\n" \
> + " .pushsection .pv_table,\"a\"\n" \
> + " .long 1b\n" \
> + " .popsection" \
> + : "=r" (to) \
> + : "r" (from), "I" (1))
> +
> +static inline unsigned long __virt_to_phys(unsigned long x)
> +{
> + unsigned long t;
> +
> + __pv_stub(x, t, "add");
> + return t;
> +}
> +
> +static inline unsigned long __phys_to_virt(unsigned long x)
> +{
> + unsigned long t;
> +
> + __pv_stub(x, t, "sub");
> + return t;
> +}
> +
> +#else
> +#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
> +#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
> +#endif
> +
> +/*
> * The DMA mask corresponding to the maximum bus address allocatable
> * using GFP_DMA. The default here places no restriction on DMA
> * allocations. This must be the smallest DMA mask in the system,
> diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
> index 6bd82d2..eaaf0ad 100644
> --- a/arch/arm/kernel/head.S
> +++ b/arch/arm/kernel/head.S
> @@ -95,6 +95,9 @@ ENTRY(stext)
> #ifdef CONFIG_SMP_ON_UP
> bl __fixup_smp
> #endif
> +#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
> + bl __fixup_pv_table
> +#endif
> bl __create_page_tables
>
> /*
> @@ -433,4 +436,36 @@ smp_on_up:
>
> #endif
>
> +#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
> +
> +/* __fixup_pv_table - patch the stub instructions with the delta between
> + * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
> + * can be expressed by an immediate shifter operand. The stub instruction
> + * has a form of '(add|sub) rd, rn, #imm'.
> + */
> +__fixup_pv_table:
> + adr r0, 1f
> + ldmia r0, {r3-r5}
> + sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
> + mov r6, r3, lsr #24 @ constant for add/sub instructions
> + teq r3, r6, lsl #24 @ must be 16MiB aligned
> + bne __error
> + orr r6, r6, #0x400 @ mask in rotate right 8 bits
> + add r4, r4, r3
> + add r5, r5, r3
> +2: cmp r4, r5
> + ldrlo r7, [r4], #4
> + ldrlo ip, [r7, r3]
> + mov ip, ip, lsr #12
> + orr ip, r6, ip, lsl #12
> + strlo ip, [r7, r3]
> + blo 2b
> + mov pc, lr
> +ENDPROC(__fixup_phys_virt)
> +
> +1: .word .
> + .word __pv_table_begin
> + .word __pv_table_end
> +#endif
> +
> #include "head-common.S"
> diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
> index cead889..fb32c9d 100644
> --- a/arch/arm/kernel/vmlinux.lds.S
> +++ b/arch/arm/kernel/vmlinux.lds.S
> @@ -57,6 +57,10 @@ SECTIONS
> __smpalt_end = .;
> #endif
>
> + __pv_table_begin = .;
> + *(.pv_table)
> + __pv_table_end = .;
> +
> INIT_SETUP(16)
>
> INIT_CALLS
> --
> 1.7.3.2.193.g78bbb
>
^ permalink raw reply
* [PATCH 4/4] ARM: support for Thumb-2 instructions with CONFIG_ARM_PATCH_PHYS_VIRT
From: Nicolas Pitre @ 2011-01-04 8:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294129208-15201-1-git-send-email-nico@fluxnic.net>
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
---
arch/arm/Kconfig | 3 +--
arch/arm/include/asm/memory.h | 5 +++--
arch/arm/kernel/head.S | 31 +++++++++++++++++++++++++++----
3 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 136ed9b..feb374a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -189,8 +189,7 @@ config VECTORS_BASE
config ARM_PATCH_PHYS_VIRT
bool
- depends on EXPERIMENTAL
- depends on !XIP && !THUMB2_KERNEL
+ depends on !XIP && EXPERIMENTAL
source "init/Kconfig"
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index f83be97..80a939d 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -157,14 +157,15 @@
#error "this machine configuration uses complex __virt_to_phys/__phys_to_virt and cannot use CONFIG_ARM_PATCH_PHYS_VIRT"
#endif
+/* the stub constant 0xff000000 is used to force the required insn encoding */
#define __pv_stub(from,to,instr) \
__asm__( \
- "1: " instr "\t%0, %1, %2\n" \
+ "1: " instr "\t%0, %1, #0xff000000\n" \
" .pushsection .pv_table,\"a\"\n" \
" .long 1b\n" \
" .popsection" \
: "=r" (to) \
- : "r" (from), "I" (1))
+ : "r" (from))
static inline unsigned long __virt_to_phys(unsigned long x)
{
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 621c792..397271d 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -447,11 +447,11 @@ __fixup_pv_table:
adr r0, 1f
ldmia r0, {r3-r5}
sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
+ movs r6, r3, lsl #8 @ must be 16MiB aligned
+ bne __error
add r4, r4, r3
add r5, r5, r3
- mov r6, r3, lsr #24 @ constant for add/sub instructions
- teq r3, r6, lsl #24 @ must be 16MiB aligned
- bne __error
+ mov r6, r3
b __fixup_pv_table_loop
ENDPROC(__fixup_phys_virt)
@@ -462,6 +462,8 @@ ENDPROC(__fixup_phys_virt)
.pushsection .text
ENTRY(__fixup_pv_table_loop)
+#ifndef CONFIG_THUMB2_KERNEL
+ mov r6, r6, lsr #24 @ constant for add/sub instructions
orr r6, r6, #0x400 @ mask in rotate right 8 bits
2: cmp r4, r5
ldrlo r7, [r4], #4
@@ -471,6 +473,27 @@ ENTRY(__fixup_pv_table_loop)
strlo ip, [r7, r3]
blo 2b
mov pc, lr
+#else
+ teq r6, #0
+ beq 2f
+ clz r7, r6
+ lsr r6, #24
+ lsl r6, r7
+ bic r6, r6, #0x3080
+ lsrs r7, #1
+ orrcs r6, r6, #0x80
+ orr r6, r6, r7, lsl #12
+ orr r6, r6, #0x4000
+2: cmp r4, r5
+ bxhs lr
+ ldr r7, [r4], #4
+ add r7, r3
+ ldr ip, [r7, #2]
+ and ip, ip, #0x0f00
+ orr ip, ip, r6
+ str ip, [r7, #2]
+ b 2b
+#endif
ENDPROC(__fixup_phys_virt_loop)
/*
@@ -482,7 +505,7 @@ ENTRY(fixup_pv_table)
mov r3, #0 @ offset (zero as we're in virtual space)
mov r4, r0 @ loop start
mov r5, r1 @ loop end
- mov r6, r2, lsr #24 @ constant for add/sub instructions
+ mov r6, r2 @ PHYS_OFFSET - PAGE_OFFSET
bl __fixup_pv_table_loop
ldmfd sp!, {r4 - r7, pc}
ENDPROC(fixup_pv_table)
--
1.7.3.2.193.g78bbb
^ permalink raw reply related
* [PATCH 3/4] ARM: module support for CONFIG_ARM_PATCH_PHYS_VIRT
From: Nicolas Pitre @ 2011-01-04 8:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294129208-15201-1-git-send-email-nico@fluxnic.net>
Thanks to Russell King for his contribution to this patch.
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
---
arch/arm/include/asm/memory.h | 4 ++++
arch/arm/kernel/head.S | 35 +++++++++++++++++++++++++++++------
arch/arm/kernel/module.c | 13 +++++++++++++
3 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index acc3126..f83be97 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -185,6 +185,10 @@ static inline unsigned long __phys_to_virt(unsigned long x)
#undef PHYS_OFFSET
#define PHYS_OFFSET __virt_to_phys(PAGE_OFFSET)
+extern void fixup_pv_table(void *table_start, void *table_end, long v2p_offset);
+#define fixup_pv_table(start, end) \
+ fixup_pv_table(start, end, PHYS_OFFSET - PAGE_OFFSET)
+
#else
#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index eaaf0ad..621c792 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -447,12 +447,22 @@ __fixup_pv_table:
adr r0, 1f
ldmia r0, {r3-r5}
sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
+ add r4, r4, r3
+ add r5, r5, r3
mov r6, r3, lsr #24 @ constant for add/sub instructions
teq r3, r6, lsl #24 @ must be 16MiB aligned
bne __error
+ b __fixup_pv_table_loop
+ENDPROC(__fixup_phys_virt)
+
+1: .word .
+ .word __pv_table_begin
+ .word __pv_table_end
+
+ .pushsection .text
+
+ENTRY(__fixup_pv_table_loop)
orr r6, r6, #0x400 @ mask in rotate right 8 bits
- add r4, r4, r3
- add r5, r5, r3
2: cmp r4, r5
ldrlo r7, [r4], #4
ldrlo ip, [r7, r3]
@@ -461,11 +471,24 @@ __fixup_pv_table:
strlo ip, [r7, r3]
blo 2b
mov pc, lr
-ENDPROC(__fixup_phys_virt)
+ENDPROC(__fixup_phys_virt_loop)
+
+/*
+ * This is the C callable version used to fixup modules.
+ * void fixup_pv_table(void *table_start, void *table_end, long v2p_offset)
+ */
+ENTRY(fixup_pv_table)
+ stmfd sp!, {r4 - r7, lr}
+ mov r3, #0 @ offset (zero as we're in virtual space)
+ mov r4, r0 @ loop start
+ mov r5, r1 @ loop end
+ mov r6, r2, lsr #24 @ constant for add/sub instructions
+ bl __fixup_pv_table_loop
+ ldmfd sp!, {r4 - r7, pc}
+ENDPROC(fixup_pv_table)
+
+ .popsection
-1: .word .
- .word __pv_table_begin
- .word __pv_table_end
#endif
#include "head-common.S"
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index d9bd786..6fef8d2 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -329,6 +329,19 @@ int
module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
struct module *module)
{
+#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
+ char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+ const Elf_Shdr *s;
+
+ for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++)
+ if (!strcmp(".pv_table", secstrings + s->sh_name)) {
+ void *pv_table_begin = (void *)s->sh_addr;
+ void *pv_table_end = pv_table_begin + s->sh_size;
+ fixup_pv_table(pv_table_begin, pv_table_end);
+ break;
+ }
+#endif
+
register_unwind_tables(module);
return 0;
}
--
1.7.3.2.193.g78bbb
^ permalink raw reply related
* [PATCH 2/4] ARM: make PHYS_OFFSET actually variable
From: Nicolas Pitre @ 2011-01-04 8:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294129208-15201-1-git-send-email-nico@fluxnic.net>
Previous patch made the implementation of __virt_to_phys() and
__phys_to_virt() assuming PHYS_OFFSET was variable. Let's make
PHYS_OFFSET actually variable by defining it in terms of __virt_to_phys().
There is a catch with this approach, as PHYS_OFFSET cannot be used as
a static initializer anymore. Only one such case was found in generic
code and removed, and that is in the declaration of the init_tags
structure (those who wish to use CONFIG_ARM_PATCH_PHYS_VIRT are better
not to rely on that init_tags default at all anyway, or they'll get
what they deserve).
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
PAGE_OFFSET)
---
arch/arm/include/asm/memory.h | 3 +++
arch/arm/kernel/setup.c | 2 ++
2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 2783ce2..acc3126 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -182,6 +182,9 @@ static inline unsigned long __phys_to_virt(unsigned long x)
return t;
}
+#undef PHYS_OFFSET
+#define PHYS_OFFSET __virt_to_phys(PAGE_OFFSET)
+
#else
#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 336f14e..7502bc1 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -703,8 +703,10 @@ static struct init_tags {
} init_tags __initdata = {
{ tag_size(tag_core), ATAG_CORE },
{ 1, PAGE_SIZE, 0xff },
+#ifndef CONFIG_ARM_PATCH_PHYS_VIRT
{ tag_size(tag_mem32), ATAG_MEM },
{ MEM_SIZE, PHYS_OFFSET },
+#endif
{ 0, ATAG_NONE }
};
--
1.7.3.2.193.g78bbb
^ permalink raw reply related
* [PATCH 1/4] ARM: runtime patching of __virt_to_phys() and __phys_to_virt()
From: Nicolas Pitre @ 2011-01-04 8:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294129208-15201-1-git-send-email-nico@fluxnic.net>
On ARM it is common to find different offsets for the location of
physical memory. In order to support multiple machine types with
a single kernel binary, we need to make PHYS_OFFSET a variable.
But turning PHYS_OFFSET into a global variable would impact performance
of many hot paths.
In the context of __virt_to_phys() and __phys_to_virt(), we currently have:
#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
This normally translates into the following assembly instruction:
add rx, rx, #(PHYS_OFFSET - PAGE_OFFSET)
If we can assume that the difference between PHYS_OFFSET and PAGE_OFFSET
will always fit into 8 bits shifted to the MSBs, then we can easily patch
this difference into the corresponding assembly instructions at run time.
This is like saying that phys and virt offsets will always be at least
16 MB aligned which is a pretty safe assumption.
So the idea is to create a table of pointers to all those add instructions,
and have the early boot code to walk and patch them up before the kernel
gets to use them. Result is equivalent to a variable PHYS_OFFSET with
next to zero performance impact compared to the constant PHYS_OFFSET.
Right now, the difference between PHYS_OFFSET and PAGE_OFFSET is determined
by the actual physical address the kernel is executing from upon start,
assuming that the kernel is located within the first 16 MB of RAM.
Thanks to Eric Miao and Russell King for their contributions to this patch.
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
---
arch/arm/Kconfig | 5 ++++
arch/arm/include/asm/memory.h | 51 +++++++++++++++++++++++++++++++++--------
arch/arm/kernel/head.S | 35 ++++++++++++++++++++++++++++
arch/arm/kernel/vmlinux.lds.S | 4 +++
4 files changed, 85 insertions(+), 10 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d56d21c0..136ed9b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -187,6 +187,11 @@ config VECTORS_BASE
help
The base address of exception vectors.
+config ARM_PATCH_PHYS_VIRT
+ bool
+ depends on EXPERIMENTAL
+ depends on !XIP && !THUMB2_KERNEL
+
source "init/Kconfig"
source "kernel/Kconfig.freezer"
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 23c2e8e..2783ce2 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -133,16 +133,6 @@
#endif
/*
- * Physical vs virtual RAM address space conversion. These are
- * private definitions which should NOT be used outside memory.h
- * files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
- */
-#ifndef __virt_to_phys
-#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
-#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
-#endif
-
-/*
* Convert a physical address to a Page Frame Number and back
*/
#define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT)
@@ -157,6 +147,47 @@
#ifndef __ASSEMBLY__
/*
+ * Physical vs virtual RAM address space conversion. These are
+ * private definitions which should NOT be used outside memory.h
+ * files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
+ */
+#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
+
+#ifdef __virt_to_phys
+#error "this machine configuration uses complex __virt_to_phys/__phys_to_virt and cannot use CONFIG_ARM_PATCH_PHYS_VIRT"
+#endif
+
+#define __pv_stub(from,to,instr) \
+ __asm__( \
+ "1: " instr "\t%0, %1, %2\n" \
+ " .pushsection .pv_table,\"a\"\n" \
+ " .long 1b\n" \
+ " .popsection" \
+ : "=r" (to) \
+ : "r" (from), "I" (1))
+
+static inline unsigned long __virt_to_phys(unsigned long x)
+{
+ unsigned long t;
+
+ __pv_stub(x, t, "add");
+ return t;
+}
+
+static inline unsigned long __phys_to_virt(unsigned long x)
+{
+ unsigned long t;
+
+ __pv_stub(x, t, "sub");
+ return t;
+}
+
+#else
+#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
+#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
+#endif
+
+/*
* The DMA mask corresponding to the maximum bus address allocatable
* using GFP_DMA. The default here places no restriction on DMA
* allocations. This must be the smallest DMA mask in the system,
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 6bd82d2..eaaf0ad 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -95,6 +95,9 @@ ENTRY(stext)
#ifdef CONFIG_SMP_ON_UP
bl __fixup_smp
#endif
+#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
+ bl __fixup_pv_table
+#endif
bl __create_page_tables
/*
@@ -433,4 +436,36 @@ smp_on_up:
#endif
+#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
+
+/* __fixup_pv_table - patch the stub instructions with the delta between
+ * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
+ * can be expressed by an immediate shifter operand. The stub instruction
+ * has a form of '(add|sub) rd, rn, #imm'.
+ */
+__fixup_pv_table:
+ adr r0, 1f
+ ldmia r0, {r3-r5}
+ sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
+ mov r6, r3, lsr #24 @ constant for add/sub instructions
+ teq r3, r6, lsl #24 @ must be 16MiB aligned
+ bne __error
+ orr r6, r6, #0x400 @ mask in rotate right 8 bits
+ add r4, r4, r3
+ add r5, r5, r3
+2: cmp r4, r5
+ ldrlo r7, [r4], #4
+ ldrlo ip, [r7, r3]
+ mov ip, ip, lsr #12
+ orr ip, r6, ip, lsl #12
+ strlo ip, [r7, r3]
+ blo 2b
+ mov pc, lr
+ENDPROC(__fixup_phys_virt)
+
+1: .word .
+ .word __pv_table_begin
+ .word __pv_table_end
+#endif
+
#include "head-common.S"
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index cead889..fb32c9d 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -57,6 +57,10 @@ SECTIONS
__smpalt_end = .;
#endif
+ __pv_table_begin = .;
+ *(.pv_table)
+ __pv_table_end = .;
+
INIT_SETUP(16)
INIT_CALLS
--
1.7.3.2.193.g78bbb
^ permalink raw reply related
* [PATCH 0/4] variable PHYS_OFFSET support
From: Nicolas Pitre @ 2011-01-04 8:20 UTC (permalink / raw)
To: linux-arm-kernel
So here it is, in a form which I think should be ready for merging.
ARM mode is well tested.
Thumb2 mode is only compile tested at the moment.
Nicolas
^ permalink raw reply
* [patch 2/5] ulpi: handle ULPI_OTG_CTRL_CHRGVBUS
From: Igor Grinberg @ 2011-01-04 7:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87bp3y84ng.fsf@lechat.rtp-net.org>
On 01/03/11 16:04, Arnaud Patard (Rtp) wrote:
> Igor Grinberg <grinberg@compulab.co.il> writes:
>> On 01/03/11 13:41, Arnaud Patard (Rtp) wrote:
>>> Igor Grinberg <grinberg@compulab.co.il> writes:
>>> Hi,
>>>> On 12/23/10 22:11, Arnaud Patard (Rtp) wrote:
>>>>> Igor Grinberg <grinberg@compulab.co.il> writes:
>>>>>> Hi Arnaud,
>>>>>>
>>>>>> On 12/20/10 17:48, Arnaud Patard (Rtp) wrote:
>>>>>>> Current code doesn't handle setting CHRGVBUS when enabling vbus.
>>>>>>> Add support for it
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
>>>>>>> Index: tst-usb/drivers/usb/otg/ulpi.c
>>>>>>> ===================================================================
>>>>>>> --- tst-usb.orig/drivers/usb/otg/ulpi.c 2010-12-20 15:38:41.000000000 +0100
>>>>>>> +++ tst-usb/drivers/usb/otg/ulpi.c 2010-12-20 15:38:57.000000000 +0100
>>>>>>> @@ -234,7 +234,8 @@
>>>>>>> {
>>>>>>> unsigned int flags = otg_io_read(otg, ULPI_OTG_CTRL);
>>>>>>>
>>>>>>> - flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
>>>>>>> + flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT |
>>>>>>> + ULPI_OTG_CTRL_CHRGVBUS);
>>>>>>>
>>>>>>> if (on) {
>>>>>>> if (otg->flags & ULPI_OTG_DRVVBUS)
>>>>>>> @@ -242,6 +243,9 @@
>>>>>>>
>>>>>>> if (otg->flags & ULPI_OTG_DRVVBUS_EXT)
>>>>>>> flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
>>>>>>> +
>>>>>>> + if (otg->flags & ULPI_OTG_CHRGVBUS)
>>>>>>> + flags |= ULPI_OTG_CTRL_CHRGVBUS;
>>>>>>> }
>>>>>>>
>>>>>>> return otg_io_write(otg, flags, ULPI_OTG_CTRL);
>>>>>> I think this is a wrong place to set the ChrgVbus bit.
>>>>>> As for ULPI spec. 1.1:
>>>>>> "3.8.7.1 Session Request Protocol (SRP)
>>>>>> ULPI provides full SRP support. The Link uses the ChrgVbus and DischrgVbus bits
>>>>>> in the OTG Control register to begin and end a session."
>>>>>>
>>>>>> So it is used for SRP.
>>>>>> May be it is better to implement
>>>>>> int (*start_srp)(struct otg_transceiver *otg);
>>>>>> method for setting this bit?
>>>>>>
>>>>> I was not sure on where to put this so I took the same approach as the
>>>>> fsl bsp which was to set it in this function and to call this function
>>>>> _after_ usb_add_hcd() [ see my previous patch ]. Indeed, it fixed my
>>>>> issue so I believe it not so bad given that there has already been some
>>>>> troubles on the ehci-mxc init.
>>>> Well, the problem is that this is supposed to be a generic driver and it should
>>>> somehow follow the ULPI spec.
>>>> This patch makes it more like mxc specific.
>>>>
>>>> As far as I understand, Session Request Protocol (SRP) allows a B-device (Peripheral)
>>>> to nudge an A-device (Host) to turn on the USB's Vbus.
>>>> This patch enables SRP along with Vbus, which seems incorrect completely.
>>>> ulpi_set_vbus() should set the Vbus (as its name says) and that's it.
>>> so, if I add a srp hook as you're suggesting, which part of the driver
>>> should call it ?
>> It should be called from outside the ulpi driver by the OTG logic
>> (just like ulpi_set_vbus() is called).
>> But, again, SRP should be set by the B-device (peripheral) and Vbus by the A-device (Host).
>> Usually, the A-device and B-device are on the opposite sides of the USB cable.
>>
>>>> Have you tried without this patch or have you just applied it along with other
>>>> patches from the fsl bsp?
>>> I already tried without this patch and without it, things are not
>>> working on my systems.
>>>> Also, if this specific patch (2/5) makes your USB (Host as I understand) work,
>>>> it makes me think that there could be some problem with your Vbus supply.
>>>> Have you checked that?
>>> I don't have any schematics and I've access only to the source code of
>>> the kernel running on the efika nettop and smartbook [1]. I've not seen
>>> anything (even remotely) related to vbus supply except in the ulpi code
>>> and from what I've heard, it's unlikely that there's something to
>>> control it on theses systems. Of course, I'll be happy to be proven
>>> wrong. Without usb theses systems are useless so anything the can reduce
>>> the number of patches needed to get the systems working with mainline is
>>> welcome.
>> Well, I was certain you are trying to use that port in Host mode (A-device), but
>> now I'm confused...
>> You say "things are not working" - what are those things?
>> Can you, please, describe what are you trying to do?
>> What are you trying to connect to this USB port? What cable do you
>> use?
> I thought it was clear that "things" was everything connected on the usb
> ports. The problem is that this also means that the nettop/smartbook are
> kind of not working too as ethernet/wifi/bt/hid devs are all connected
> on usb [ they're all on the pcb ]. So, even if you boot on the mmc aka
> the only storage available which doesn't need usb, you won't be able to
> login.
>
Well, "everything connected on the usb ports" can be confusing, because
you can connect devices (usb disk drives, keyboards, mice, etc..) and hosts
(desktop PCs, mobile computers, etc..) to the OTG port.
That's why I wanted to be sure, what devices do you connect to that port.
Now it is clear, that your port should be in the Host mode and you should _not_
issue an SRP.
"ethernet/wifi/bt/hid devs are all connected on usb" - this means,
that they are either connected to several (different) usb ports or there is a usb hub
connected to that port and those devices are connected to it.
Can you confirm how those devices are connected?
>> Also, what ulpi vendor/product id is reported in ulpi_init()?
> ULPI transceiver vendor/product ID 0x0424/0x0006
> Found SMSC USB3319 ULPI transceiver.
SMSC USB3319 does not have either an integrated Vbus switch or Charge Pump,
For the device connected to that transceiver could work properly,
there is a need in _external Vbus switch_ , that should be enabled using
some kind of CPEN pin (can be GPIO).
This means, that you don't even need to call ulpi_set_vbus().
Either way, this patch is NAK.
I think you need to check your hardware (in particular Vbus supply).
--
Regards,
Igor.
^ permalink raw reply
* [PATCH] ARM: pxa168: Add support for UART3
From: Tanmay Upadhyay @ 2011-01-04 7:00 UTC (permalink / raw)
To: linux-arm-kernel
PXA168 has 3 onchip UARTs. Added support for the third one.
Signed-off-by: Tanmay Upadhyay <tanmay.upadhyay@einfochips.com>
---
arch/arm/mach-mmp/pxa168.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index 7564739..499d656 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -66,6 +66,7 @@ void __init pxa168_init_irq(void)
/* APB peripheral clocks */
static APBC_CLK(uart1, PXA168_UART1, 1, 14745600);
static APBC_CLK(uart2, PXA168_UART2, 1, 14745600);
+static APBC_CLK(uart3, PXA168_UART3, 1, 14745600);
static APBC_CLK(twsi0, PXA168_TWSI0, 1, 33000000);
static APBC_CLK(twsi1, PXA168_TWSI1, 1, 33000000);
static APBC_CLK(pwm1, PXA168_PWM1, 1, 13000000);
@@ -86,6 +87,7 @@ static APMU_CLK(lcd, LCD, 0x7f, 312000000);
static struct clk_lookup pxa168_clkregs[] = {
INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL),
INIT_CLKREG(&clk_uart2, "pxa2xx-uart.1", NULL),
+ INIT_CLKREG(&clk_uart3, "pxa2xx-uart.2", NULL),
INIT_CLKREG(&clk_twsi0, "pxa2xx-i2c.0", NULL),
INIT_CLKREG(&clk_twsi1, "pxa2xx-i2c.1", NULL),
INIT_CLKREG(&clk_pwm1, "pxa168-pwm.0", NULL),
@@ -149,6 +151,7 @@ void pxa168_clear_keypad_wakeup(void)
/* on-chip devices */
PXA168_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4017000, 0x30, 19, 20);
PXA168_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4018000, 0x30, 21, 22);
+PXA168_DEVICE(uart3, "pxa2xx-uart", 2, UART3, 0xd4026000, 0x30, 23, 24);
PXA168_DEVICE(twsi0, "pxa2xx-i2c", 0, TWSI0, 0xd4011000, 0x28);
PXA168_DEVICE(twsi1, "pxa2xx-i2c", 1, TWSI1, 0xd4025000, 0x28);
PXA168_DEVICE(pwm1, "pxa168-pwm", 0, NONE, 0xd401a000, 0x10);
--
1.7.0.4
^ permalink raw reply related
* ks8695_gettimeoffset
From: Dick Hollenbeck @ 2011-01-04 6:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4D22BDAA.2030902@softplc.com>
On 01/04/2011 12:26 AM, Dick Hollenbeck wrote:
> On 01/03/2011 01:55 PM, avictor.za at gmail.com wrote:
>> hi,
>>
>>> Is it possible at all to implement clocksource/clockevents for KS8695? As Dick and "Register Description" already said you cannot read the time register, so clocksource->read cannot be implemented to return ticks elapsed. Or do I see it wrong?
>> I'm pretty sure I tested this when I originally submitted the KS8685
>> processor support (May 2007), and the timer registers are readable.
>> I would of tested with a userspace program that sat in a tight loop
>> calling gettimeofday().
>>
>>
>> Regards,
>> Andrew Victor
>>
> Thanks Andrew. Can you explain how this is supposed to be a duration, even
> if one of these two registers was a readable down counter?
>
> elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD);
>
>
> Which of the two is a down counter, and why would it be added to one that is not, or is?
>
> While you are thinking about that, I have a userspace test program that can peek at the two registers using a memory mapped region pointer, and it will tell us if the value is changing over time.
Sorry, I should have just ran the program before posting. Both registers
are constant at 25000 while running. So my original summary is correct,
that this C function is not worth anything.
Where do we go from here?
Dick
^ permalink raw reply
* [PATCH RESEND] [ARM] Add missing include "asm/memory.h"
From: Nick Piggin @ 2011-01-04 6:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1293501397.32542.2.camel@mola>
On Tue, Dec 28, 2010 at 09:56:37AM +0800, Axel Lin wrote:
> This patch fixes below build error by adding the missing asm/memory.h,
> which is needed for arch_is_coherent().
This is the right approach, but needs to go through ARM tree.
> $ make pxa3xx_defconfig; make
> CC init/do_mounts_rd.o
> In file included from include/linux/list_bl.h:5,
> from include/linux/rculist_bl.h:7,
> from include/linux/dcache.h:7,
> from include/linux/fs.h:381,
> from init/do_mounts_rd.c:3:
> include/linux/bit_spinlock.h: In function 'bit_spin_unlock':
> include/linux/bit_spinlock.h:61: error: implicit declaration of function 'arch_is_coherent'
> make[1]: *** [init/do_mounts_rd.o] Error 1
> make: *** [init] Error 2
>
> Signed-off-by: Axel Lin <axel.lin@gmail.com>
> Acked-by: Peter Huewe <peterhuewe@gmx.de>
> ---
> It was posted on https://lkml.org/lkml/2010/12/17/9 .
> I still have the build error for today's linux-next tree.
> CC Eric Miao, maybe he will take this patch.
>
> Axel
>
> arch/arm/include/asm/system.h | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
> index ec4327a..3222ab8 100644
> --- a/arch/arm/include/asm/system.h
> +++ b/arch/arm/include/asm/system.h
> @@ -155,6 +155,7 @@ extern unsigned int user_debug;
> #define rmb() dmb()
> #define wmb() mb()
> #else
> +#include <asm/memory.h>
> #define mb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
> #define rmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
> #define wmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
Is this matching ARM convention to pull includes down here. It
usually ends up with more elusive dependency problems and strange
bugs (eg. some headers define intermediate symbols for convenience
and then undef them sometime later etc etc). Usual thing to avoid
problems is to move includes up to the top.
^ permalink raw reply
* ks8695_gettimeoffset
From: Dick Hollenbeck @ 2011-01-04 6:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTi=+9h20je24F+pnoZRFE-of-VZ5Xax8-tDzFs4=@mail.gmail.com>
On 01/03/2011 01:55 PM, avictor.za at gmail.com wrote:
> hi,
>
>> Is it possible at all to implement clocksource/clockevents for KS8695? As Dick and "Register Description" already said you cannot read the time register, so clocksource->read cannot be implemented to return ticks elapsed. Or do I see it wrong?
> I'm pretty sure I tested this when I originally submitted the KS8685
> processor support (May 2007), and the timer registers are readable.
> I would of tested with a userspace program that sat in a tight loop
> calling gettimeofday().
>
>
> Regards,
> Andrew Victor
>
Thanks Andrew. Can you explain how this is supposed to be a duration, even
if one of these two registers was a readable down counter?
elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD);
Which of the two is a down counter, and why would it be added to one that is not, or is?
While you are thinking about that, I have a userspace test program that can peek at the two registers using a memory mapped region pointer, and it will tell us if the value is changing over time.
The register description PDF document says the values *are readable*, but that description does not specifically say what is being read back, the preload value or the dynamically down counting value.
Doc Extraction:
TOUT1PC aka T1PD offset 0xE40C
This field specifies the duration that the TOUT1 pin is High in each
timeout period. Writing zero to this register may cuase unpredictalbe
behavior.
TOUT1TC aka T1TC offset 0xE404
This field specifies the duration that the TOUT1 pin is Low in each
timeout period. Writing zero to this register may cuase unpredictalbe
behavior.
Thanks,
Dick
^ permalink raw reply
* [PATCH v3 2/2] ARM i.MX53: Make MX53 EVK bootable
From: yong.shen at freescale.com @ 2011-01-04 6:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294122176-26450-1-git-send-email-yong.shen@freescale.com>
From: Yong Shen <yong.shen@linaro.org>
1. Add entries to Kconfig
2. Add machine definition
3. Add Uart platform data, pad setting and base address
4. Adjust GPIO irq number
Signed-off-by: Yong Shen <yong.shen@linaro.org>
---
arch/arm/mach-mx5/Kconfig | 18 +++++
arch/arm/mach-mx5/Makefile | 1 +
arch/arm/mach-mx5/Makefile.boot | 3 +
arch/arm/mach-mx5/board-mx53_evk.c | 84 +++++++++++++++++++++++++
arch/arm/mach-mx5/devices-imx53.h | 13 ++++
arch/arm/plat-mxc/devices/platform-imx-uart.c | 10 +++
arch/arm/plat-mxc/include/mach/iomux-mx53.h | 20 ++++++
arch/arm/plat-mxc/include/mach/irqs.h | 4 +-
arch/arm/plat-mxc/include/mach/uncompress.h | 4 +
9 files changed, 156 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-mx5/board-mx53_evk.c
create mode 100644 arch/arm/mach-mx5/devices-imx53.h
diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig
index 7c31757..55254b6 100644
--- a/arch/arm/mach-mx5/Kconfig
+++ b/arch/arm/mach-mx5/Kconfig
@@ -7,6 +7,9 @@ config ARCH_MX50
config ARCH_MX51
bool
+config ARCH_MX53
+ bool
+
config SOC_IMX50
bool
select MXC_TZIC
@@ -23,6 +26,12 @@ config SOC_IMX51
select ARCH_HAS_CPUFREQ
select ARCH_MX51
+config SOC_IMX53
+ bool
+ select MXC_TZIC
+ select ARCH_MXC_IOMUX_V3
+ select ARCH_MX53
+
comment "MX5 platforms:"
config MACH_MX51_BABBAGE
@@ -111,6 +120,15 @@ config MACH_MX51_EFIKAMX
Include support for Genesi Efika MX nettop. This includes specific
configurations for the board and its peripherals.
+config MACH_MX53_EVK
+ bool "Support MX53 EVK platforms"
+ select SOC_IMX53
+ select IMX_HAVE_PLATFORM_IMX_UART
+ help
+ Include support for MX53 EVK platform. This includes specific
+ configurations for the board and its peripherals.
+
+
config MACH_MX50_RDP
bool "Support MX50 reference design platform"
depends on BROKEN
diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile
index d99d7ab..0c398ba 100644
--- a/arch/arm/mach-mx5/Makefile
+++ b/arch/arm/mach-mx5/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_SOC_IMX50) += mm-mx50.o
obj-$(CONFIG_CPU_FREQ_IMX) += cpu_op-mx51.o
obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o
obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o
+obj-$(CONFIG_MACH_MX53_EVK) += board-mx53_evk.o
obj-$(CONFIG_MACH_EUKREA_CPUIMX51) += board-cpuimx51.o
obj-$(CONFIG_MACH_EUKREA_MBIMX51_BASEBOARD) += eukrea_mbimx51-baseboard.o
obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += board-cpuimx51sd.o
diff --git a/arch/arm/mach-mx5/Makefile.boot b/arch/arm/mach-mx5/Makefile.boot
index 854748f..e928be1 100644
--- a/arch/arm/mach-mx5/Makefile.boot
+++ b/arch/arm/mach-mx5/Makefile.boot
@@ -4,3 +4,6 @@ initrd_phys-$(CONFIG_ARCH_MX50) := 0x70800000
zreladdr-$(CONFIG_ARCH_MX51) := 0x90008000
params_phys-$(CONFIG_ARCH_MX51) := 0x90000100
initrd_phys-$(CONFIG_ARCH_MX51) := 0x90800000
+ zreladdr-$(CONFIG_ARCH_MX53) := 0x70008000
+params_phys-$(CONFIG_ARCH_MX53) := 0x70000100
+initrd_phys-$(CONFIG_ARCH_MX53) := 0x70800000
diff --git a/arch/arm/mach-mx5/board-mx53_evk.c b/arch/arm/mach-mx5/board-mx53_evk.c
new file mode 100644
index 0000000..fa97d0d
--- /dev/null
+++ b/arch/arm/mach-mx5/board-mx53_evk.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2010 Yong Shen. <Yong.Shen@linaro.org>
+ */
+
+/*
+ * 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.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/imx-uart.h>
+#include <mach/iomux-mx53.h>
+
+#include "crm_regs.h"
+#include "devices-imx53.h"
+
+static iomux_v3_cfg_t mx53_evk_pads[] = {
+ MX53_PAD_CSI0_D10__UART1_TXD,
+ MX53_PAD_CSI0_D11__UART1_RXD,
+ MX53_PAD_ATA_DIOW__UART1_TXD,
+ MX53_PAD_ATA_DMACK__UART1_RXD,
+
+ MX53_PAD_ATA_BUFFER_EN__UART2_RXD,
+ MX53_PAD_ATA_DMARQ__UART2_TXD,
+ MX53_PAD_ATA_DIOR__UART2_RTS,
+ MX53_PAD_ATA_INTRQ__UART2_CTS,
+
+ MX53_PAD_ATA_CS_0__UART3_TXD,
+ MX53_PAD_ATA_CS_1__UART3_RXD,
+ MX53_PAD_ATA_DA_1__UART3_CTS,
+ MX53_PAD_ATA_DA_2__UART3_RTS,
+};
+
+static const struct imxuart_platform_data mx53_evk_uart_pdata __initconst = {
+ .flags = IMXUART_HAVE_RTSCTS,
+};
+
+static inline void mx53_evk_init_uart(void)
+{
+ imx53_add_imx_uart(0, &mx53_evk_uart_pdata);
+ imx53_add_imx_uart(1, &mx53_evk_uart_pdata);
+ imx53_add_imx_uart(2, &mx53_evk_uart_pdata);
+}
+
+static void __init mx53_evk_board_init(void)
+{
+ mxc_iomux_v3_setup_multiple_pads(mx53_evk_pads,
+ ARRAY_SIZE(mx53_evk_pads));
+ mx53_evk_init_uart();
+}
+
+static void __init mx53_evk_timer_init(void)
+{
+ mx53_clocks_init(32768, 24000000, 22579200, 0);
+}
+
+static struct sys_timer mx53_evk_timer = {
+ .init = mx53_evk_timer_init,
+};
+
+MACHINE_START(MX53_EVK, "Freescale MX53 EVK Board")
+ .map_io = mx53_map_io,
+ .init_irq = mx53_init_irq,
+ .init_machine = mx53_evk_board_init,
+ .timer = &mx53_evk_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx5/devices-imx53.h b/arch/arm/mach-mx5/devices-imx53.h
new file mode 100644
index 0000000..9d0ec25
--- /dev/null
+++ b/arch/arm/mach-mx5/devices-imx53.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2010 Yong Shen. <Yong.Shen@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+#include <mach/mx53.h>
+#include <mach/devices-common.h>
+
+extern const struct imx_imx_uart_1irq_data imx53_imx_uart_data[] __initconst;
+#define imx53_add_imx_uart(id, pdata) \
+ imx_add_imx_uart_1irq(&imx53_imx_uart_data[id], pdata)
diff --git a/arch/arm/plat-mxc/devices/platform-imx-uart.c b/arch/arm/plat-mxc/devices/platform-imx-uart.c
index 0c69fbc..3c854c2 100644
--- a/arch/arm/plat-mxc/devices/platform-imx-uart.c
+++ b/arch/arm/plat-mxc/devices/platform-imx-uart.c
@@ -116,6 +116,16 @@ const struct imx_imx_uart_1irq_data imx51_imx_uart_data[] __initconst = {
};
#endif /* ifdef CONFIG_SOC_IMX51 */
+#ifdef CONFIG_SOC_IMX53
+const struct imx_imx_uart_1irq_data imx53_imx_uart_data[] __initconst = {
+#define imx53_imx_uart_data_entry(_id, _hwid) \
+ imx_imx_uart_1irq_data_entry(MX53, _id, _hwid, SZ_4K)
+ imx53_imx_uart_data_entry(0, 1),
+ imx53_imx_uart_data_entry(1, 2),
+ imx53_imx_uart_data_entry(2, 3),
+};
+#endif /* ifdef CONFIG_SOC_IMX53 */
+
struct platform_device *__init imx_add_imx_uart_3irq(
const struct imx_imx_uart_3irq_data *data,
const struct imxuart_platform_data *pdata)
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx53.h b/arch/arm/plat-mxc/include/mach/iomux-mx53.h
index 80cb3c5..5deee01 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-mx53.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-mx53.h
@@ -42,6 +42,26 @@ typedef enum iomux_config {
#define NON_MUX_I 0x00
#define NON_PAD_I 0x00
+#define MX53_UART_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \
+ PAD_CTL_DSE_HIGH | PAD_CTL_SRE_FAST | PAD_CTL_HYS)
+/* UART1 */
+#define MX53_PAD_CSI0_D10__UART1_TXD IOMUX_PAD(0x414, 0xE8, 2, 0x0, 0, MX53_UART_PAD_CTRL)
+#define MX53_PAD_CSI0_D11__UART1_RXD IOMUX_PAD(0x418, 0xEC, 2, 0x878, 1, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_DIOW__UART1_TXD IOMUX_PAD(0x5F0, 0x270, 3, 0x0, 0, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_DMACK__UART1_RXD IOMUX_PAD(0x5F4, 0x274, 3, 0x880, 3, MX53_UART_PAD_CTRL)
+
+/* UART2 */
+#define MX53_PAD_ATA_BUFFER_EN__UART2_RXD IOMUX_PAD(0x5FC, 0x27C, 3, 0x880, 3, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_DMARQ__UART2_TXD IOMUX_PAD(0x5F8, 0x278, 3, 0x0, 0, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_DIOR__UART2_RTS IOMUX_PAD(0x604, 0x284, 3, 0x87C, 3, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_INTRQ__UART2_CTS IOMUX_PAD(0x600, 0x280, 3, 0x0, 0, MX53_UART_PAD_CTRL)
+
+/* UART3 */
+#define MX53_PAD_ATA_CS_0__UART3_TXD IOMUX_PAD(0x61C, 0x29C, 4, 0x0, 0, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_CS_1__UART3_RXD IOMUX_PAD(0x620, 0x2A0, 4, 0x888, 3, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_DA_1__UART3_CTS IOMUX_PAD(0x614, 0x294, 4, 0x0, 0, MX53_UART_PAD_CTRL)
+#define MX53_PAD_ATA_DA_2__UART3_RTS IOMUX_PAD(0x618, 0x298, 4, 0x884, 5, MX53_UART_PAD_CTRL)
+
#define MX53_PAD_GPIO_19__GPIO_4_5 IOMUX_PAD(0x348, 0x20,IOMUX_CONFIG_ALT1, 0x0, 0, NO_PAD_CTRL)
#define MX53_PAD_KEY_COL0__GPIO_4_6 IOMUX_PAD(0x34C, 0x24,IOMUX_CONFIG_ALT1, 0x0, 0, NO_PAD_CTRL)
#define MX53_PAD_KEY_ROW0__GPIO_4_7 IOMUX_PAD(0x350, 0x28,IOMUX_CONFIG_ALT1, 0x0, 0, NO_PAD_CTRL)
diff --git a/arch/arm/plat-mxc/include/mach/irqs.h b/arch/arm/plat-mxc/include/mach/irqs.h
index 064026b..58a49cc 100644
--- a/arch/arm/plat-mxc/include/mach/irqs.h
+++ b/arch/arm/plat-mxc/include/mach/irqs.h
@@ -23,7 +23,9 @@
#define MXC_GPIO_IRQ_START MXC_INTERNAL_IRQS
/* these are ordered by size to support multi-SoC kernels */
-#if defined CONFIG_ARCH_MX2
+#if defined CONFIG_ARCH_MX53
+#define MXC_GPIO_IRQS (32 * 7)
+#elif defined CONFIG_ARCH_MX2
#define MXC_GPIO_IRQS (32 * 6)
#elif defined CONFIG_ARCH_MX50
#define MXC_GPIO_IRQS (32 * 6)
diff --git a/arch/arm/plat-mxc/include/mach/uncompress.h b/arch/arm/plat-mxc/include/mach/uncompress.h
index 5ccf3ef..3a70ebf 100644
--- a/arch/arm/plat-mxc/include/mach/uncompress.h
+++ b/arch/arm/plat-mxc/include/mach/uncompress.h
@@ -64,6 +64,7 @@ static inline void flush(void)
#define MX3X_UART2_BASE_ADDR 0x43F94000
#define MX51_UART1_BASE_ADDR 0x73fbc000
#define MX50_UART1_BASE_ADDR 0x53fbc000
+#define MX53_UART1_BASE_ADDR 0x53fbc000
static __inline__ void __arch_decomp_setup(unsigned long arch_id)
{
@@ -106,6 +107,9 @@ static __inline__ void __arch_decomp_setup(unsigned long arch_id)
case MACH_TYPE_MX50_RDP:
uart_base = MX50_UART1_BASE_ADDR;
break;
+ case MACH_TYPE_MX53_EVK:
+ uart_base = MX53_UART1_BASE_ADDR;
+ break;
default:
break;
}
--
1.7.0.4
^ permalink raw reply related
* [PATCH v3 1/2] ARM i.MX53: Some bug fix about MX53 MSL code
From: yong.shen at freescale.com @ 2011-01-04 6:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294122176-26450-1-git-send-email-yong.shen@freescale.com>
From: Yong Shen <yong.shen@linaro.org>
1. pll_base address should return right value
2. uart parent clk is from pll3
Signed-off-by: Yong Shen <yong.shen@linaro.org>
---
arch/arm/mach-mx5/clock-mx51-mx53.c | 25 ++++++++++++++++++++++++-
arch/arm/mach-mx5/crm_regs.h | 4 ++++
2 files changed, 28 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c
index 2f9eae2..b290f3f 100644
--- a/arch/arm/mach-mx5/clock-mx51-mx53.c
+++ b/arch/arm/mach-mx5/clock-mx51-mx53.c
@@ -124,7 +124,7 @@ static inline u32 _get_mux(struct clk *parent, struct clk *m0,
return -EINVAL;
}
-static inline void __iomem *_get_pll_base(struct clk *pll)
+static inline void __iomem *_mx51_get_pll_base(struct clk *pll)
{
if (pll == &pll1_main_clk)
return MX51_DPLL1_BASE;
@@ -132,6 +132,20 @@ static inline void __iomem *_get_pll_base(struct clk *pll)
return MX51_DPLL2_BASE;
else if (pll == &pll3_sw_clk)
return MX51_DPLL3_BASE;
+ else
+ BUG();
+
+ return NULL;
+}
+
+static inline void __iomem *_mx53_get_pll_base(struct clk *pll)
+{
+ if (pll == &pll1_main_clk)
+ return MX53_DPLL1_BASE;
+ else if (pll == &pll2_sw_clk)
+ return MX53_DPLL2_BASE;
+ else if (pll == &pll3_sw_clk)
+ return MX53_DPLL3_BASE;
else if (pll == &mx53_pll4_sw_clk)
return MX53_DPLL4_BASE;
else
@@ -140,6 +154,14 @@ static inline void __iomem *_get_pll_base(struct clk *pll)
return NULL;
}
+static inline void __iomem *_get_pll_base(struct clk *pll)
+{
+ if (cpu_is_mx51())
+ return _mx51_get_pll_base(pll);
+ else
+ return _mx53_get_pll_base(pll);
+}
+
static unsigned long clk_pll_get_rate(struct clk *clk)
{
long mfi, mfn, mfd, pdf, ref_clk, mfn_abs;
@@ -1243,6 +1265,7 @@ int __init mx53_clocks_init(unsigned long ckil, unsigned long osc,
clk_tree_init();
+ clk_set_parent(&uart_root_clk, &pll3_sw_clk);
clk_enable(&cpu_clk);
clk_enable(&main_bus_clk);
diff --git a/arch/arm/mach-mx5/crm_regs.h b/arch/arm/mach-mx5/crm_regs.h
index 51ff9bb..b462c22 100644
--- a/arch/arm/mach-mx5/crm_regs.h
+++ b/arch/arm/mach-mx5/crm_regs.h
@@ -19,6 +19,10 @@
#define MX51_GPC_BASE MX51_IO_ADDRESS(MX51_GPC_BASE_ADDR)
/*MX53*/
+#define MX53_CCM_BASE MX53_IO_ADDRESS(MX53_CCM_BASE_ADDR)
+#define MX53_DPLL1_BASE MX53_IO_ADDRESS(MX53_PLL1_BASE_ADDR)
+#define MX53_DPLL2_BASE MX53_IO_ADDRESS(MX53_PLL2_BASE_ADDR)
+#define MX53_DPLL3_BASE MX53_IO_ADDRESS(MX53_PLL3_BASE_ADDR)
#define MX53_DPLL4_BASE MX53_IO_ADDRESS(MX53_PLL3_BASE_ADDR)
/* PLL Register Offsets */
--
1.7.0.4
^ permalink raw reply related
* [PATCH v3 0/2] ARM i.MX53: Some bug fix about MX53 MSL code
From: yong.shen at freescale.com @ 2011-01-04 6:22 UTC (permalink / raw)
To: linux-arm-kernel
Change log:
1. rebased on latest code
2. 1/2 seperate _get_pll_base() for mx51 and mx53
3. 2/2 some minor changes about function or structure names
^ permalink raw reply
* [PATCH v4] ARM: mxs: Change duart device to use amba-pl011
From: Shawn Guo @ 2011-01-04 6:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1292857064-5032-1-git-send-email-shawn.guo@freescale.com>
The mxs duart is actually an amba-pl011 device. This commit changes
the duart device code to dynamically allocate amba-pl011 device,
so that drivers/serial/amba-pl011.c can be used on mxs.
Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
---
Changes for v4:
- Register xbus_clk for amba bus driver to get, in form of
dev_id="duart" and con_id="apb_pclk"
- Explicitly call clk_enable for some fundamental clocks that have
been on, to reflect the clock status and get its and parent's
usecount updated.
Changes for v3:
- Use alias "duart" of uart_clk for duart serial driver
- Add __initconst for duart amba_device
- Let MXS_HAVE_AMBA_DUART select ARM_AMBA
Changes for v2:
- Add #include <asm/irq.h> into amba-duart.c. When cleaning headers
includsion in other files, compiler start complaining NO_IRQ not
defined, so add the inclusion to fix it.
- Change MXS_AMBA_DEVICE to MXS_AMBA_DUART_DEVICE
arch/arm/mach-mxs/Kconfig | 4 +-
arch/arm/mach-mxs/clock-mx23.c | 12 +++++-
arch/arm/mach-mxs/clock-mx28.c | 12 +++++-
arch/arm/mach-mxs/devices-mx23.h | 4 +-
arch/arm/mach-mxs/devices-mx28.h | 4 +-
arch/arm/mach-mxs/devices.c | 17 +++++++-
arch/arm/mach-mxs/devices/Kconfig | 3 +-
arch/arm/mach-mxs/devices/Makefile | 2 +-
arch/arm/mach-mxs/devices/amba-duart.c | 40 +++++++++++++++++++
arch/arm/mach-mxs/devices/platform-duart.c | 48 -----------------------
arch/arm/mach-mxs/include/mach/devices-common.h | 11 ++---
11 files changed, 88 insertions(+), 69 deletions(-)
create mode 100644 arch/arm/mach-mxs/devices/amba-duart.c
delete mode 100644 arch/arm/mach-mxs/devices/platform-duart.c
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig
index c4ac7b4..8bfc8df 100644
--- a/arch/arm/mach-mxs/Kconfig
+++ b/arch/arm/mach-mxs/Kconfig
@@ -15,7 +15,7 @@ comment "MXS platforms:"
config MACH_MX23EVK
bool "Support MX23EVK Platform"
select SOC_IMX23
- select MXS_HAVE_PLATFORM_DUART
+ select MXS_HAVE_AMBA_DUART
default y
help
Include support for MX23EVK platform. This includes specific
@@ -24,7 +24,7 @@ config MACH_MX23EVK
config MACH_MX28EVK
bool "Support MX28EVK Platform"
select SOC_IMX28
- select MXS_HAVE_PLATFORM_DUART
+ select MXS_HAVE_AMBA_DUART
select MXS_HAVE_PLATFORM_FEC
default y
help
diff --git a/arch/arm/mach-mxs/clock-mx23.c b/arch/arm/mach-mxs/clock-mx23.c
index 8f5a19a..136341d 100644
--- a/arch/arm/mach-mxs/clock-mx23.c
+++ b/arch/arm/mach-mxs/clock-mx23.c
@@ -437,10 +437,12 @@ _DEFINE_CLOCK(clk32k_clk, XTAL, TIMROT_CLK32K_GATE, &ref_xtal_clk);
},
static struct clk_lookup lookups[] = {
- _REGISTER_CLOCK("mxs-duart.0", NULL, uart_clk)
+ /* for amba bus driver */
+ _REGISTER_CLOCK("duart", "apb_pclk", xbus_clk)
+ /* for amba-pl011 driver */
+ _REGISTER_CLOCK("duart", NULL, uart_clk)
_REGISTER_CLOCK("rtc", NULL, rtc_clk)
_REGISTER_CLOCK(NULL, "hclk", hbus_clk)
- _REGISTER_CLOCK(NULL, "xclk", xbus_clk)
_REGISTER_CLOCK(NULL, "usb", usb_clk)
_REGISTER_CLOCK(NULL, "audio", audio_clk)
_REGISTER_CLOCK(NULL, "pwm", pwm_clk)
@@ -518,6 +520,12 @@ int __init mx23_clocks_init(void)
{
clk_misc_init();
+ clk_enable(&cpu_clk);
+ clk_enable(&hbus_clk);
+ clk_enable(&xbus_clk);
+ clk_enable(&emi_clk);
+ clk_enable(&uart_clk);
+
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
mxs_timer_init(&clk32k_clk, MX23_INT_TIMER0);
diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c
index 74e2103..f20b254 100644
--- a/arch/arm/mach-mxs/clock-mx28.c
+++ b/arch/arm/mach-mxs/clock-mx28.c
@@ -602,12 +602,14 @@ _DEFINE_CLOCK(fec_clk, ENET, DISABLE, &hbus_clk);
},
static struct clk_lookup lookups[] = {
- _REGISTER_CLOCK("mxs-duart.0", NULL, uart_clk)
+ /* for amba bus driver */
+ _REGISTER_CLOCK("duart", "apb_pclk", xbus_clk)
+ /* for amba-pl011 driver */
+ _REGISTER_CLOCK("duart", NULL, uart_clk)
_REGISTER_CLOCK("fec.0", NULL, fec_clk)
_REGISTER_CLOCK("rtc", NULL, rtc_clk)
_REGISTER_CLOCK("pll2", NULL, pll2_clk)
_REGISTER_CLOCK(NULL, "hclk", hbus_clk)
- _REGISTER_CLOCK(NULL, "xclk", xbus_clk)
_REGISTER_CLOCK(NULL, "can0", can0_clk)
_REGISTER_CLOCK(NULL, "can1", can1_clk)
_REGISTER_CLOCK(NULL, "usb0", usb0_clk)
@@ -726,6 +728,12 @@ int __init mx28_clocks_init(void)
{
clk_misc_init();
+ clk_enable(&cpu_clk);
+ clk_enable(&hbus_clk);
+ clk_enable(&xbus_clk);
+ clk_enable(&emi_clk);
+ clk_enable(&uart_clk);
+
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
mxs_timer_init(&clk32k_clk, MX28_INT_TIMER0);
diff --git a/arch/arm/mach-mxs/devices-mx23.h b/arch/arm/mach-mxs/devices-mx23.h
index d0f49fc..1256788 100644
--- a/arch/arm/mach-mxs/devices-mx23.h
+++ b/arch/arm/mach-mxs/devices-mx23.h
@@ -11,6 +11,6 @@
#include <mach/mx23.h>
#include <mach/devices-common.h>
-extern const struct mxs_duart_data mx23_duart_data __initconst;
+extern const struct amba_device mx23_duart_device __initconst;
#define mx23_add_duart() \
- mxs_add_duart(&mx23_duart_data)
+ mxs_add_duart(&mx23_duart_device)
diff --git a/arch/arm/mach-mxs/devices-mx28.h b/arch/arm/mach-mxs/devices-mx28.h
index 00b736c..33773a6 100644
--- a/arch/arm/mach-mxs/devices-mx28.h
+++ b/arch/arm/mach-mxs/devices-mx28.h
@@ -11,9 +11,9 @@
#include <mach/mx28.h>
#include <mach/devices-common.h>
-extern const struct mxs_duart_data mx28_duart_data __initconst;
+extern const struct amba_device mx28_duart_device __initconst;
#define mx28_add_duart() \
- mxs_add_duart(&mx28_duart_data)
+ mxs_add_duart(&mx28_duart_device)
extern const struct mxs_fec_data mx28_fec_data[] __initconst;
#define mx28_add_fec(id, pdata) \
diff --git a/arch/arm/mach-mxs/devices.c b/arch/arm/mach-mxs/devices.c
index 6b60f02..c20d547 100644
--- a/arch/arm/mach-mxs/devices.c
+++ b/arch/arm/mach-mxs/devices.c
@@ -19,9 +19,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <linux/err.h>
#include <linux/platform_device.h>
-#include <mach/common.h>
+#include <linux/amba/bus.h>
struct platform_device *__init mxs_add_platform_device_dmamask(
const char *name, int id,
@@ -73,3 +72,17 @@ err:
return pdev;
}
+
+int __init mxs_add_amba_device(const struct amba_device *dev)
+{
+ struct amba_device *adev = kmalloc(sizeof(*adev), GFP_KERNEL);
+
+ if (!adev) {
+ pr_err("%s: failed to allocate memory", __func__);
+ return -ENOMEM;
+ }
+
+ *adev = *dev;
+
+ return amba_device_register(adev, &iomem_resource);
+}
diff --git a/arch/arm/mach-mxs/devices/Kconfig b/arch/arm/mach-mxs/devices/Kconfig
index a35a2dc..cf7dc1a 100644
--- a/arch/arm/mach-mxs/devices/Kconfig
+++ b/arch/arm/mach-mxs/devices/Kconfig
@@ -1,5 +1,6 @@
-config MXS_HAVE_PLATFORM_DUART
+config MXS_HAVE_AMBA_DUART
bool
+ select ARM_AMBA
config MXS_HAVE_PLATFORM_FEC
bool
diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile
index 4b5266a..d0a09f6 100644
--- a/arch/arm/mach-mxs/devices/Makefile
+++ b/arch/arm/mach-mxs/devices/Makefile
@@ -1,2 +1,2 @@
-obj-$(CONFIG_MXS_HAVE_PLATFORM_DUART) += platform-duart.o
+obj-$(CONFIG_MXS_HAVE_AMBA_DUART) += amba-duart.o
obj-$(CONFIG_MXS_HAVE_PLATFORM_FEC) += platform-fec.o
diff --git a/arch/arm/mach-mxs/devices/amba-duart.c b/arch/arm/mach-mxs/devices/amba-duart.c
new file mode 100644
index 0000000..a559db0
--- /dev/null
+++ b/arch/arm/mach-mxs/devices/amba-duart.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009-2010 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+#include <asm/irq.h>
+#include <mach/mx23.h>
+#include <mach/mx28.h>
+#include <mach/devices-common.h>
+
+#define MXS_AMBA_DUART_DEVICE(name, soc) \
+const struct amba_device name##_device __initconst = { \
+ .dev = { \
+ .init_name = "duart", \
+ }, \
+ .res = { \
+ .start = soc ## _DUART_BASE_ADDR, \
+ .end = (soc ## _DUART_BASE_ADDR) + SZ_8K - 1, \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ .irq = {soc ## _INT_DUART, NO_IRQ}, \
+}
+
+#ifdef CONFIG_SOC_IMX23
+MXS_AMBA_DUART_DEVICE(mx23_duart, MX23);
+#endif
+
+#ifdef CONFIG_SOC_IMX28
+MXS_AMBA_DUART_DEVICE(mx28_duart, MX28);
+#endif
+
+int __init mxs_add_duart(const struct amba_device *dev)
+{
+ return mxs_add_amba_device(dev);
+}
diff --git a/arch/arm/mach-mxs/devices/platform-duart.c b/arch/arm/mach-mxs/devices/platform-duart.c
deleted file mode 100644
index 2fe0df5..0000000
--- a/arch/arm/mach-mxs/devices/platform-duart.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2009-2010 Pengutronix
- * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
- *
- * Copyright 2010 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 2 as published by the
- * Free Software Foundation.
- */
-#include <mach/mx23.h>
-#include <mach/mx28.h>
-#include <mach/devices-common.h>
-
-#define mxs_duart_data_entry(soc) \
- { \
- .iobase = soc ## _DUART_BASE_ADDR, \
- .irq = soc ## _INT_DUART, \
- }
-
-#ifdef CONFIG_SOC_IMX23
-const struct mxs_duart_data mx23_duart_data __initconst =
- mxs_duart_data_entry(MX23);
-#endif
-
-#ifdef CONFIG_SOC_IMX28
-const struct mxs_duart_data mx28_duart_data __initconst =
- mxs_duart_data_entry(MX28);
-#endif
-
-struct platform_device *__init mxs_add_duart(
- const struct mxs_duart_data *data)
-{
- struct resource res[] = {
- {
- .start = data->iobase,
- .end = data->iobase + SZ_8K - 1,
- .flags = IORESOURCE_MEM,
- }, {
- .start = data->irq,
- .end = data->irq,
- .flags = IORESOURCE_IRQ,
- },
- };
-
- return mxs_add_platform_device("mxs-duart", 0, res, ARRAY_SIZE(res),
- NULL, 0);
-}
diff --git a/arch/arm/mach-mxs/include/mach/devices-common.h b/arch/arm/mach-mxs/include/mach/devices-common.h
index 3da48d4..6c3d1a1 100644
--- a/arch/arm/mach-mxs/include/mach/devices-common.h
+++ b/arch/arm/mach-mxs/include/mach/devices-common.h
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/init.h>
+#include <linux/amba/bus.h>
struct platform_device *mxs_add_platform_device_dmamask(
const char *name, int id,
@@ -24,14 +25,10 @@ static inline struct platform_device *mxs_add_platform_device(
name, id, res, num_resources, data, size_data, 0);
}
+int __init mxs_add_amba_device(const struct amba_device *dev);
+
/* duart */
-struct mxs_duart_data {
- resource_size_t iobase;
- resource_size_t iosize;
- resource_size_t irq;
-};
-struct platform_device *__init mxs_add_duart(
- const struct mxs_duart_data *data);
+int __init mxs_add_duart(const struct amba_device *dev);
/* fec */
#include <linux/fec.h>
--
1.7.1
^ permalink raw reply related
* [PATCH 15/23] Alternative mmc structure to support pxa168, pxa910, mmp2 family SD
From: zhangfei gao @ 2011-01-04 6:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <E5C4BC31-9A6C-4F51-ADCD-6755299DD122@marvell.com>
On Fri, Dec 31, 2010 at 5:08 PM, Mark Brown <markb@marvell.com> wrote:
> On Dec 31, 2010, at 1:46 AM, Haojian Zhuang wrote:
>
>> On Fri, Dec 31, 2010 at 2:03 PM, Philip Rakity <prakity@marvell.com> wrote:
>>>
>>> On Dec 30, 2010, at 9:46 PM, Haojian Zhuang wrote:
>>>
>>>> On Thu, Dec 23, 2010 at 6:58 AM, Philip Rakity <prakity@marvell.com> wrote:
>>>>>
>>>>> On Dec 22, 2010, at 6:10 AM, Arnd Bergmann wrote:
>>>>>
>>>>>> On Wednesday 22 December 2010 08:09:58 Philip Rakity wrote:
>>>>>>> The PXA168, PXA910, and MMP2 are not the same SOC. ?The family
>>>>>>> of embedded processors have slightly different internal blocks
>>>>>>> for SD, I2C, etc. ?Sometimes it is important to know which SOC
>>>>>>> is being used due to differences in the silicon. ?Sometimes it
>>>>>>> is important to know evaluation boards should be selected based
>>>>>>> on the SOC on the board.
>>>>>>
>>>>>> This looks like you're moving in the wrong direction.
>>>>>>
>>>>>> If the chips are just slightly different, you'd certainly
>>>>>> want to make sure that you can detect the difference at runtime,
>>>>>> and be able to use the same kernel on all of the variants.
>>>>>
>>>>> MMP2 used PJ4 core --- PXA168/PXA910 use PJ1 so rather different architecture.
>>>>>
>>>>> PXA168/PXA910 have slightly different internal peripherals with different quirks.
>>>>> Certainly possible to tell this apart at runtime but not all peripherals are the same
>>>>> and startup files ARE different.
>>>>>
>>>>>
>>>> MMP2 and PJ4 are different SoC silicons. But they're using similar SD
>
> I think you meant MMP2 and PJ1 there. MMP2 is a PJ4 core.
>
>>>> IP, so we can share same driver to them. Different quirks can be
>>>> handled by different flags in run time.
>
> Technically all of these SDHCI controllers are extremely similar. The point of the SDHCI-* family of drivers
> are there to specify the implementation differences.
>
> sdhci-pxa codebase is currently minimal it performs two operations:
> 1. Register sdhci instance using platform data.
> 2. Define common quirks.
> 3. Provides clock control callback
>
> The PXA168/PXA910 (PJ1) versions would have specific needs:
> 1. There is a difference in clock control and power up sequencing
> 2. There is a specific I/O accessor needed to access registers
> 3. There are workarounds for SDIO that are needed.
> 4. There specific quirks needed for the PXA168/PXA910.
For mmp2 and pxa910, they are same ip in fact, same silicon bug, and
same workaround, and fixed together in updated version.
That's the reason they worked well in currently code base with same
quirk, same driver.
pxa168 may be different since no future stepping.
It is true, one is v2, the other is v3, but this is handled in sdhci.c
and transparent for specific driver.
The only difference is different private register, that may because
when define v2 and can not foresee v3 definition.
Still not find strong reason to add more specific driver only handle
different private register, if so more driver will be added for mmp2x,
for example, though same ip are used.
BTW, brownstone with mmc support is already added if work based on
pxa-linux-2.6.git, devel branch.
>
> Even if we were to write a separate driver for PXA168/910 and MMP2 there would be no code duplication except for
> the code to interpret the platform data.
>
> In Philip's code he took the duplication into account and further abstracted it so that the platform data interpretation is done in
> sdhci-pxa.c and the differences are exposed in the sdhci-pxa168 and sdhci-mmp2 modules.
>
> There is already precedence for this already in sdhci-of-* implementation.
>
> I am not sure why people think combining all of these differences into one massive sdhci-pxa driver would make maintenance simpler when the
> relevant commonality is already abstracted away in sdhci.c.
>
>>>>
>>>
>>> The SD IP is not the same. ?One uses SD controller 3.0 and the other is 2.0.
>>>
>>> They are the same in the sense that they public registers adhere to the appropriate SD spec 2.0/3.0 but
>>> these accesses are handled by sdhci.c. ?(SD 3.0 extends the SD 2.0 spec by adding new registers and adding some bit definitions
>>> in a compatible manner inside the public space).
>>>
>> Yes, you confirmed that they're same in the sense. It's enough. User
>> needn't care whether it's SD2.0 or SD3.0. It's the business of silicon
>> engineer.
>>
>>> The private registers that need to be programmed are not extensions but are at different locations and bit fields do not have exactly the same meaning.
>>>
>>> The code to handle the differences is rather small and is not NOT placed in arch/arm/ directory but rather in the
>>> drivers/mmc/host directory and follows the conventions to handle this.
>>>
>> In your patch, you divide them into silicon depedant files. I think
>> that putting them into arch/arm is better.
>>
>>>
>>>> There's no reason to copy driver for each silicon
>>>
>>>>
>>>>>>
>>>>>> Instead, you promote each of the SOCs to a top-level family
>>>>>> in this patch, which makes it impossible to build a kernel
>>>>>> for more than one of them at a time.
>>>>>
>>>>> That was the intent to handle the case of development board selection.
>>>>> it is meaningless to select MMP2 development board with say PXA168 SoC.
>>>>>
>>>>> Open to other way to handle this problem. ?Suggestions welcome.
>>>>>
>>>> Again, you did wrong. You couldn't make patch for top-level family. It
>>>> will introduce a lot of error to maintainers.
>>>
>>> The patch selects ARCH-MMP (as now) but adds the ability
>>> to also know which specific SoC was chosen. ?(This is sort of done by choosing the development board today).
>>>
>> If you want to change mmc code, push it into mmc tree. If you want to
>> change pxa code, push it into pxa tree. If they're dependant, please
>> make sure the sequence is right.
>>>
>>>>
>>>> Your patches will make them mess.
>>>
>>> suggest a solution.
>>>
>>> The current mechanism of having the development board select the CPU does not seem right.
>>> One can select a development board for MMP2 and PXA168 and yet the arch files to support each CPU
>>> are different and not compatible. ?(for example cache handling).
>>>
>> Only one SoC can be installed on one board. If silicons are
>> pin-compatible to one board, we can register different boards. For
>> example, we can divide saarb as saarb_pv and saarb_mg.
>>
>> If MMP2 and PXA168 are both installed on one board, it means that you
>> must run two kernel images on two APs. Actually, I don't think anyone
>> will design system like this way. If so, why not adopt the above
>> policy? You can divide the board into two sub-board on naming policy.
>>>>
>>>> Thanks
>>>> Haojian
>>>>
>>>>>>
>>>>>> ? ? ? Arnd
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> linux-arm-kernel mailing list
>>>>> linux-arm-kernel at lists.infradead.org
>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>>>
>>>
>>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* [PATCH] Trivial: ARM: pxa168: Bug fix - correct UART DMA channels
From: Tanmay Upadhyay @ 2011-01-04 6:07 UTC (permalink / raw)
To: linux-arm-kernel
According to ARMADA_16x_Software_Manual.pdf, DMA channels for
UART1 are 19, 20 and DMA channels for UART2 are 21, 22.
It's of little significance for now as the PXA UART driver
doesn't use DMA as of now.
Signed-off-by: Tanmay Upadhyay <tanmay.upadhyay@einfochips.com>
---
arch/arm/mach-mmp/pxa168.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c
index 72b4e76..7564739 100644
--- a/arch/arm/mach-mmp/pxa168.c
+++ b/arch/arm/mach-mmp/pxa168.c
@@ -147,8 +147,8 @@ void pxa168_clear_keypad_wakeup(void)
}
/* on-chip devices */
-PXA168_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4017000, 0x30, 21, 22);
-PXA168_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4018000, 0x30, 23, 24);
+PXA168_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4017000, 0x30, 19, 20);
+PXA168_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4018000, 0x30, 21, 22);
PXA168_DEVICE(twsi0, "pxa2xx-i2c", 0, TWSI0, 0xd4011000, 0x28);
PXA168_DEVICE(twsi1, "pxa2xx-i2c", 1, TWSI1, 0xd4025000, 0x28);
PXA168_DEVICE(pwm1, "pxa168-pwm", 0, NONE, 0xd401a000, 0x10);
--
1.7.0.4
^ permalink raw reply related
* [PATCH] ARM: mxs: Change duart device to use amba-pl011
From: Shawn Guo @ 2011-01-04 5:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110103103913.GE26785@n2100.arm.linux.org.uk>
Hi Russell,
On Mon, Jan 03, 2011 at 10:39:13AM +0000, Russell King - ARM Linux wrote:
> On Mon, Dec 27, 2010 at 07:49:40PM +0800, Shawn Guo wrote:
> > I did not see this problem. It's true that the clock is turned off
> > in amba_device_register. But later, the clock will be turned on
> > again by amba_probe -> amba_get_enable_pclk very soon.
> >
> > But we should add something like below in mx28_clocks_init to reflect
> > the clock status and get usecount updated.
> >
> > clk_enable(&uart_clk)
> >
> > I ran into similar problem with fec driver, and would cover that in
> > fec patch set.
>
> This sounds like entirely the wrong approach. Please take some time to
> understand that there are different clocks which do different things.
>
> The PCLK is the name ARM Ltd give to the APB bus clock. This we name
> apb_pclk, and we expect platforms to provide it where possible. This
> name will be looked up for every primecell device on the system.
>
> If you don't provide an apb_pclk, it will find the device specific
> function clock. This in itself is no bad thing, unless you use the
> device outside of the AMBA driver (iow, before probe or after remove)
> and helps identify when you've forgotten to provide an apb_pclk.
>
I intended to let the amba bus driver get duart clock. But you are
right, I should provide an apb_pclk since it actually exists on mxs
processors. In case of duart, it is xbus (apbx) clock.
_REGISTER_CLOCK("duart", "apb_pclk", xbus_clk)
Thanks for the comment.
--
Regards,
Shawn
^ permalink raw reply
* [PATCH v4 4/4] mc13xxx-core: Add i2c driver
From: Marc Reilly @ 2011-01-04 5:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294119299-18352-1-git-send-email-marc@cpdesign.com.au>
Signed-off-by: Marc Reilly <marc@cpdesign.com.au>
---
drivers/mfd/Kconfig | 5 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/mc13xxx-i2c.c | 115 +++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/mc13xxx.h | 5 ++-
4 files changed, 125 insertions(+), 1 deletions(-)
create mode 100644 drivers/mfd/mc13xxx-i2c.c
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9ce1d42..fbbbdaa 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -455,6 +455,11 @@ config MFD_MC13XXX_SPI
select MFD_MC13XXX
depends on SPI_MASTER && (MFD_MC13783 || MFD_MC13892)
+config MFD_MC13XXX_I2C
+ tristate "Support Freescale MC13892 via I2C interface"
+ select MFD_MC13XXX
+ depends on I2C && MFD_MC13892
+
config PCF50633_ADC
tristate "Support for NXP PCF50633 ADC"
depends on MFD_PCF50633
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 492e881..b7d774f 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
+obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c
new file mode 100644
index 0000000..b3f2f2b
--- /dev/null
+++ b/drivers/mfd/mc13xxx-i2c.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2009-2010 Creative Product Design
+ * Marc Reilly marc at cpdesign.com.au
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13xxx.h>
+#include <linux/i2c.h>
+
+static int mc13xxx_i2c_reg_read(struct mc13xxx *mc13xxx, unsigned int offset,
+ u32 *val)
+{
+ int ret;
+ unsigned char buf[3] = {0, 0, 0};
+
+ ret = i2c_smbus_read_i2c_block_data(mc13xxx->i2cclient,
+ offset, 3, buf);
+ *val = buf[0] << 16 | buf[1] << 8 | buf[2];
+
+ return ret == 3 ? 0 : ret;
+}
+
+static int mc13xxx_i2c_reg_write(struct mc13xxx *mc13xxx, unsigned int offset,
+ u32 val)
+{
+ int ret;
+ unsigned char buf[3];
+
+ buf[0] = (val >> 16) & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+ buf[2] = val & 0xff;
+
+ ret = i2c_smbus_write_i2c_block_data(mc13xxx->i2cclient,
+ offset, 3, buf);
+
+ return ret;
+}
+
+static int mc13xxx_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mc13xxx *mc13xxx;
+ struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
+ int ret;
+
+ mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+ if (!mc13xxx)
+ return -ENOMEM;
+
+ dev_set_drvdata(&client->dev, mc13xxx);
+ mc13xxx->dev = &client->dev;
+ mc13xxx->i2cclient = client;
+ mc13xxx->read_dev = mc13xxx_i2c_reg_read;
+ mc13xxx->write_dev = mc13xxx_i2c_reg_write;
+
+ ret = mc13xxx_common_init(mc13xxx, pdata, client->irq);
+
+ if (ret == 0 && (id->driver_data != mc13xxx->ictype))
+ dev_warn(mc13xxx->dev,
+ "device id doesn't match auto detection!\n");
+
+ return ret;
+}
+
+static int __devexit mc13xxx_i2c_remove(struct i2c_client *client)
+{
+ struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev);
+
+ free_irq(client->irq, mc13xxx);
+
+ mfd_remove_devices(&client->dev);
+
+ kfree(mc13xxx);
+
+ return 0;
+}
+
+static const struct i2c_device_id mc13xxx_i2c_idtable[] = {
+ {"mc13892", MC13XXX_ID_MC13892},
+ { }
+};
+
+static struct i2c_driver mc13xxx_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mc13xxx-i2c"
+ },
+ .id_table = mc13xxx_i2c_idtable,
+ .probe = mc13xxx_i2c_probe,
+ .remove = __devexit_p(mc13xxx_i2c_remove),
+};
+
+static int __init mc13xxx_i2c_init(void)
+{
+ return i2c_add_driver(&mc13xxx_i2c_driver);
+}
+subsys_initcall(mc13xxx_i2c_init);
+
+static void __exit mc13xxx_i2c_exit(void)
+{
+ i2c_del_driver(&mc13xxx_i2c_driver);
+}
+module_exit(mc13xxx_i2c_exit);
+
+MODULE_DESCRIPTION("i2c driver for Freescale MC13XXX PMIC");
+MODULE_AUTHOR("Marc Reilly <marc@cpdesign.com.au");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h
index ab7deb3..c5ab08b 100644
--- a/include/linux/mfd/mc13xxx.h
+++ b/include/linux/mfd/mc13xxx.h
@@ -73,7 +73,10 @@ enum mc13xxx_id {
};
struct mc13xxx {
- struct spi_device *spidev;
+ union {
+ struct spi_device *spidev;
+ struct i2c_client *i2cclient;
+ };
struct device *dev;
enum mc13xxx_id ictype;
--
1.7.1
^ permalink raw reply related
* [PATCH v4 3/4] mc13xxx-core: Move spi specific code into separate module.
From: Marc Reilly @ 2011-01-04 5:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294119299-18352-1-git-send-email-marc@cpdesign.com.au>
This patch moves all spi specific code into a new module. The mc13xxx
struct moves to the include file by necessity.
Signed-off-by: Marc Reilly <marc@cpdesign.com.au>
---
drivers/mfd/Makefile | 1 +
drivers/mfd/mc13xxx-core.c | 166 ------------------------------------------
drivers/mfd/mc13xxx-spi.c | 168 +++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/mc13xxx.h | 21 ++++++
4 files changed, 190 insertions(+), 166 deletions(-)
create mode 100644 drivers/mfd/mc13xxx-spi.c
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f54b365..492e881 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
+obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index d1f1d7b..3201041 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -15,31 +15,9 @@
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
-#include <linux/spi/spi.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mc13xxx.h>
-enum mc13xxx_id {
- MC13XXX_ID_MC13783,
- MC13XXX_ID_MC13892,
- MC13XXX_ID_INVALID,
-};
-
-struct mc13xxx {
- struct spi_device *spidev;
-
- struct device *dev;
- enum mc13xxx_id ictype;
-
- struct mutex lock;
-
- int (*read_dev)(struct mc13xxx *, unsigned int, u32 *);
- int (*write_dev)(struct mc13xxx *, unsigned int, u32);
-
- irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
- void *irqdata[MC13XXX_NUM_IRQ];
-};
-
struct mc13783 {
struct mc13xxx mc13xxx;
@@ -180,38 +158,6 @@ void mc13xxx_unlock(struct mc13xxx *mc13xxx)
}
EXPORT_SYMBOL(mc13xxx_unlock);
-#define MC13XXX_REGOFFSET_SHIFT 25
-static int mc13xxx_spi_reg_read(struct mc13xxx *mc13xxx,
- unsigned int offset, u32 *val)
-{
- struct spi_transfer t;
- struct spi_message m;
- int ret;
-
- *val = offset << MC13XXX_REGOFFSET_SHIFT;
-
- memset(&t, 0, sizeof(t));
-
- t.tx_buf = val;
- t.rx_buf = val;
- t.len = sizeof(u32);
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
-
- ret = spi_sync(mc13xxx->spidev, &m);
-
- /* error in message.status implies error return from spi_sync */
- BUG_ON(!ret && m.status);
-
- if (ret)
- return ret;
-
- *val &= 0xffffff;
-
- return 0;
-}
-
int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
{
int ret;
@@ -228,35 +174,6 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
}
EXPORT_SYMBOL(mc13xxx_reg_read);
-static int mc13xxx_spi_reg_write(struct mc13xxx *mc13xxx, unsigned int offset,
- u32 val)
-{
- u32 buf;
- struct spi_transfer t;
- struct spi_message m;
- int ret;
-
- buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
-
- memset(&t, 0, sizeof(t));
-
- t.tx_buf = &buf;
- t.rx_buf = &buf;
- t.len = sizeof(u32);
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
-
- ret = spi_sync(mc13xxx->spidev, &m);
-
- BUG_ON(!ret && m.status);
-
- if (ret)
- return ret;
-
- return 0;
-}
-
int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
{
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
@@ -717,41 +634,6 @@ static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0);
}
-static int mc13xxx_probe(struct spi_device *spi)
-{
- struct mc13xxx *mc13xxx;
- struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
- int ret;
-
- mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
- if (!mc13xxx)
- return -ENOMEM;
-
- dev_set_drvdata(&spi->dev, mc13xxx);
- spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
- spi->bits_per_word = 32;
- spi_setup(spi);
-
- mc13xxx->dev = &spi->dev;
- mc13xxx->spidev = spi;
- mc13xxx->read_dev = mc13xxx_spi_reg_read;
- mc13xxx->write_dev = mc13xxx_spi_reg_write;
-
- ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq);
-
- if (ret) {
- dev_set_drvdata(&spi->dev, NULL);
- } else {
- const struct spi_device_id *devid =
- spi_get_device_id(mc13xxx->spidev);
- if (!devid || devid->driver_data != mc13xxx->ictype)
- dev_warn(mc13xxx->dev,
- "device id doesn't match auto detection!\n");
- }
-
- return ret;
-}
-
int mc13xxx_common_init(struct mc13xxx *mc13xxx,
struct mc13xxx_platform_data *pdata, int irq)
{
@@ -817,54 +699,6 @@ err_revision:
}
EXPORT_SYMBOL_GPL(mc13xxx_common_init);
-static int __devexit mc13xxx_remove(struct spi_device *spi)
-{
- struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
-
- free_irq(mc13xxx->spidev->irq, mc13xxx);
-
- mfd_remove_devices(&spi->dev);
-
- kfree(mc13xxx);
-
- return 0;
-}
-
-static const struct spi_device_id mc13xxx_device_id[] = {
- {
- .name = "mc13783",
- .driver_data = MC13XXX_ID_MC13783,
- }, {
- .name = "mc13892",
- .driver_data = MC13XXX_ID_MC13892,
- }, {
- /* sentinel */
- }
-};
-
-static struct spi_driver mc13xxx_driver = {
- .id_table = mc13xxx_device_id,
- .driver = {
- .name = "mc13xxx",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = mc13xxx_probe,
- .remove = __devexit_p(mc13xxx_remove),
-};
-
-static int __init mc13xxx_init(void)
-{
- return spi_register_driver(&mc13xxx_driver);
-}
-subsys_initcall(mc13xxx_init);
-
-static void __exit mc13xxx_exit(void)
-{
- spi_unregister_driver(&mc13xxx_driver);
-}
-module_exit(mc13xxx_exit);
-
MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c
new file mode 100644
index 0000000..d8ffff7
--- /dev/null
+++ b/drivers/mfd/mc13xxx-spi.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2009-2010 Pengutronix
+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
+ *
+ * loosely based on an earlier driver that has
+ * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mc13xxx.h>
+
+#define MC13XXX_REGOFFSET_SHIFT 25
+static int mc13xxx_spi_reg_read(struct mc13xxx *mc13xxx,
+ unsigned int offset, u32 *val)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ int ret;
+
+ *val = offset << MC13XXX_REGOFFSET_SHIFT;
+
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = val;
+ t.rx_buf = val;
+ t.len = sizeof(u32);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(mc13xxx->spidev, &m);
+
+ /* error in message.status implies error return from spi_sync */
+ BUG_ON(!ret && m.status);
+
+ if (ret)
+ return ret;
+
+ *val &= 0xffffff;
+
+ return 0;
+}
+
+static int mc13xxx_spi_reg_write(struct mc13xxx *mc13xxx, unsigned int offset,
+ u32 val)
+{
+ u32 buf;
+ struct spi_transfer t;
+ struct spi_message m;
+ int ret;
+
+ buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
+
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = &buf;
+ t.rx_buf = &buf;
+ t.len = sizeof(u32);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(mc13xxx->spidev, &m);
+
+ BUG_ON(!ret && m.status);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mc13xxx_spi_probe(struct spi_device *spi)
+{
+ struct mc13xxx *mc13xxx;
+ struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
+ int ret;
+
+ mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
+ if (!mc13xxx)
+ return -ENOMEM;
+
+ dev_set_drvdata(&spi->dev, mc13xxx);
+ spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+ spi->bits_per_word = 32;
+ spi_setup(spi);
+
+ mc13xxx->dev = &spi->dev;
+ mc13xxx->spidev = spi;
+ mc13xxx->read_dev = mc13xxx_spi_reg_read;
+ mc13xxx->write_dev = mc13xxx_spi_reg_write;
+
+ ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq);
+
+ if (ret) {
+ dev_set_drvdata(&spi->dev, NULL);
+ } else {
+ const struct spi_device_id *devid =
+ spi_get_device_id(mc13xxx->spidev);
+ if (!devid || devid->driver_data != mc13xxx->ictype)
+ dev_warn(mc13xxx->dev,
+ "device id doesn't match auto detection!\n");
+ }
+
+ return ret;
+}
+
+static int __devexit mc13xxx_spi_remove(struct spi_device *spi)
+{
+ struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
+
+ free_irq(mc13xxx->spidev->irq, mc13xxx);
+
+ mfd_remove_devices(&spi->dev);
+
+ kfree(mc13xxx);
+
+ return 0;
+}
+
+static const struct spi_device_id mc13xxx_device_id[] = {
+ {
+ .name = "mc13783",
+ .driver_data = MC13XXX_ID_MC13783,
+ }, {
+ .name = "mc13892",
+ .driver_data = MC13XXX_ID_MC13892,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct spi_driver mc13xxx_spi_driver = {
+ .id_table = mc13xxx_device_id,
+ .driver = {
+ .name = "mc13xxx",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mc13xxx_spi_probe,
+ .remove = __devexit_p(mc13xxx_spi_remove),
+};
+
+static int __init mc13xxx_spi_init(void)
+{
+ return spi_register_driver(&mc13xxx_spi_driver);
+}
+subsys_initcall(mc13xxx_spi_init);
+
+static void __exit mc13xxx_spi_exit(void)
+{
+ spi_unregister_driver(&mc13xxx_spi_driver);
+}
+module_exit(mc13xxx_spi_exit);
+
+MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h
index f5d277d..ab7deb3 100644
--- a/include/linux/mfd/mc13xxx.h
+++ b/include/linux/mfd/mc13xxx.h
@@ -66,6 +66,27 @@ int mc13xxx_get_flags(struct mc13xxx *mc13xxx);
#define MC13XXX_NUM_IRQ 46
+enum mc13xxx_id {
+ MC13XXX_ID_MC13783,
+ MC13XXX_ID_MC13892,
+ MC13XXX_ID_INVALID,
+};
+
+struct mc13xxx {
+ struct spi_device *spidev;
+
+ struct device *dev;
+ enum mc13xxx_id ictype;
+
+ struct mutex lock;
+
+ int (*read_dev)(struct mc13xxx *, unsigned int, u32 *);
+ int (*write_dev)(struct mc13xxx *, unsigned int, u32);
+
+ irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
+ void *irqdata[MC13XXX_NUM_IRQ];
+};
+
struct regulator_init_data;
struct mc13xxx_regulator_init_data {
--
1.7.1
^ permalink raw reply related
* [PATCH v4 2/4] mc13xxx-core: Kconfig: Config menu driven by specific IC type
From: Marc Reilly @ 2011-01-04 5:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294119299-18352-1-git-send-email-marc@cpdesign.com.au>
This patch makes config items for the mc13892 and mc13783 distinct and
splits introduces a separate config item for spi interface support in
preparation for a separate i2c and spi backend.
The mc13xxx generic core is therefore selected by the spi (or i2c) item:
having it the other way around doesn't work for other drivers that will
need to distinguish between mc13783 and mc13892 (at build time).
Signed-off-by: Marc Reilly <marc@cpdesign.com.au>
---
drivers/mfd/Kconfig | 34 +++++++++++++++++++++++++---------
1 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3a1493b..9ce1d42 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -425,20 +425,36 @@ config MFD_PCF50633
so that function-specific drivers can bind to them.
config MFD_MC13783
- tristate
-
-config MFD_MC13XXX
- tristate "Support Freescale MC13783 and MC13892"
depends on SPI_MASTER
- select MFD_CORE
- select MFD_MC13783
+ select MFD_MC13XXX_SPI
+ tristate "Support for Freescale MC13783 PMIC"
help
- Support for the Freescale (Atlas) PMIC and audio CODECs
- MC13783 and MC13892.
- This driver provides common support for accessing the device,
+ Support for the Freescale MC13783 PMIC and audio CODEC.
+ This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_MC13892
+ depends on SPI_MASTER || I2C
+ select MFD_MC13XXX
+ tristate "Support for Freescale MC13892 PMIC"
+ help
+ Enable support for the Freescale MC13892 PMIC.
+ As the MC13892 can connect by either I2C or SPI bus, you will
+ also need to select which of these you would like to support.
+ Additional drivers must be enabled in order to use the
+ functionality of the device.
+
+config MFD_MC13XXX
+ tristate
+ depends on SPI_MASTER || I2C
+ select MFD_CORE
+
+config MFD_MC13XXX_SPI
+ tristate "Support Freescale MC13783 and MC13892 via SPI interface"
+ select MFD_MC13XXX
+ depends on SPI_MASTER && (MFD_MC13783 || MFD_MC13892)
+
config PCF50633_ADC
tristate "Support for NXP PCF50633 ADC"
depends on MFD_PCF50633
--
1.7.1
^ permalink raw reply related
* [PATCH v4 1/4] mc13xxx-core: Prepare for separate spi and i2c backends.
From: Marc Reilly @ 2011-01-04 5:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294119299-18352-1-git-send-email-marc@cpdesign.com.au>
This patch abstracts the bus specific operations from the driver core.
Read and write handlers are introduced and generic initialization is
consolidated into mc13xxx_comon_init. The irq member is removed because
it is unused.
Signed-off-by: Marc Reilly <marc@cpdesign.com.au>
---
drivers/mfd/mc13xxx-core.c | 158 +++++++++++++++++++++++++-----------------
include/linux/mfd/mc13xxx.h | 5 ++
2 files changed, 99 insertions(+), 64 deletions(-)
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index a2ac2ed..d1f1d7b 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -19,10 +19,22 @@
#include <linux/mfd/core.h>
#include <linux/mfd/mc13xxx.h>
+enum mc13xxx_id {
+ MC13XXX_ID_MC13783,
+ MC13XXX_ID_MC13892,
+ MC13XXX_ID_INVALID,
+};
+
struct mc13xxx {
struct spi_device *spidev;
+
+ struct device *dev;
+ enum mc13xxx_id ictype;
+
struct mutex lock;
- int irq;
+
+ int (*read_dev)(struct mc13xxx *, unsigned int, u32 *);
+ int (*write_dev)(struct mc13xxx *, unsigned int, u32);
irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
void *irqdata[MC13XXX_NUM_IRQ];
@@ -150,36 +162,32 @@ EXPORT_SYMBOL(mc13783_to_mc13xxx);
void mc13xxx_lock(struct mc13xxx *mc13xxx)
{
if (!mutex_trylock(&mc13xxx->lock)) {
- dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n",
+ dev_dbg(mc13xxx->dev, "wait for %s from %pf\n",
__func__, __builtin_return_address(0));
mutex_lock(&mc13xxx->lock);
}
- dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+ dev_dbg(mc13xxx->dev, "%s from %pf\n",
__func__, __builtin_return_address(0));
}
EXPORT_SYMBOL(mc13xxx_lock);
void mc13xxx_unlock(struct mc13xxx *mc13xxx)
{
- dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
+ dev_dbg(mc13xxx->dev, "%s from %pf\n",
__func__, __builtin_return_address(0));
mutex_unlock(&mc13xxx->lock);
}
EXPORT_SYMBOL(mc13xxx_unlock);
#define MC13XXX_REGOFFSET_SHIFT 25
-int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
+static int mc13xxx_spi_reg_read(struct mc13xxx *mc13xxx,
+ unsigned int offset, u32 *val)
{
struct spi_transfer t;
struct spi_message m;
int ret;
- BUG_ON(!mutex_is_locked(&mc13xxx->lock));
-
- if (offset > MC13XXX_NUMREGS)
- return -EINVAL;
-
*val = offset << MC13XXX_REGOFFSET_SHIFT;
memset(&t, 0, sizeof(t));
@@ -201,26 +209,33 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
*val &= 0xffffff;
- dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
-
return 0;
}
-EXPORT_SYMBOL(mc13xxx_reg_read);
-int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
+int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
{
- u32 buf;
- struct spi_transfer t;
- struct spi_message m;
int ret;
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
- dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
-
- if (offset > MC13XXX_NUMREGS || val > 0xffffff)
+ if (offset > MC13XXX_NUMREGS)
return -EINVAL;
+ ret = mc13xxx->read_dev(mc13xxx, offset, val);
+ dev_vdbg(mc13xxx->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
+
+ return ret;
+}
+EXPORT_SYMBOL(mc13xxx_reg_read);
+
+static int mc13xxx_spi_reg_write(struct mc13xxx *mc13xxx, unsigned int offset,
+ u32 val)
+{
+ u32 buf;
+ struct spi_transfer t;
+ struct spi_message m;
+ int ret;
+
buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
memset(&t, 0, sizeof(t));
@@ -241,6 +256,18 @@ int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
return 0;
}
+
+int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
+{
+ BUG_ON(!mutex_is_locked(&mc13xxx->lock));
+
+ dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val);
+
+ if (offset > MC13XXX_NUMREGS || val > 0xffffff)
+ return -EINVAL;
+
+ return mc13xxx->write_dev(mc13xxx, offset, val);
+}
EXPORT_SYMBOL(mc13xxx_reg_write);
int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
@@ -445,7 +472,7 @@ static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx,
if (handled == IRQ_HANDLED)
num_handled++;
} else {
- dev_err(&mc13xxx->spidev->dev,
+ dev_err(mc13xxx->dev,
"BUG: irq %u but no handler\n",
baseirq + irq);
@@ -481,25 +508,23 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
return IRQ_RETVAL(handled);
}
-enum mc13xxx_id {
- MC13XXX_ID_MC13783,
- MC13XXX_ID_MC13892,
- MC13XXX_ID_INVALID,
-};
-
const char *mc13xxx_chipname[] = {
[MC13XXX_ID_MC13783] = "mc13783",
[MC13XXX_ID_MC13892] = "mc13892",
};
#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
-static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
+static int mc13xxx_identify(struct mc13xxx *mc13xxx)
{
u32 icid;
u32 revision;
- const char *name;
int ret;
+ /*
+ * Get the generation ID from register 46, as apparently some older
+ * IC revisions only have this info at this location. Newer ICs seem to
+ * have both.
+ */
ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
if (ret)
return ret;
@@ -508,26 +533,23 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
switch (icid) {
case 2:
- *id = MC13XXX_ID_MC13783;
- name = "mc13783";
+ mc13xxx->ictype = MC13XXX_ID_MC13783;
break;
case 7:
- *id = MC13XXX_ID_MC13892;
- name = "mc13892";
+ mc13xxx->ictype = MC13XXX_ID_MC13892;
break;
default:
- *id = MC13XXX_ID_INVALID;
+ mc13xxx->ictype = MC13XXX_ID_INVALID;
break;
}
- if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) {
+ if (mc13xxx->ictype == MC13XXX_ID_MC13783 ||
+ mc13xxx->ictype == MC13XXX_ID_MC13892) {
ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
- if (ret)
- return ret;
- dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, "
+ dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
"fin: %d, fab: %d, icid: %d/%d\n",
- mc13xxx_chipname[*id],
+ mc13xxx_chipname[mc13xxx->ictype],
maskval(revision, MC13XXX_REVISION_REVFULL),
maskval(revision, MC13XXX_REVISION_REVMETAL),
maskval(revision, MC13XXX_REVISION_FIN),
@@ -536,26 +558,12 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
maskval(revision, MC13XXX_REVISION_ICIDCODE));
}
- if (*id != MC13XXX_ID_INVALID) {
- const struct spi_device_id *devid =
- spi_get_device_id(mc13xxx->spidev);
- if (!devid || devid->driver_data != *id)
- dev_warn(&mc13xxx->spidev->dev, "device id doesn't "
- "match auto detection!\n");
- }
-
- return 0;
+ return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0;
}
static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
{
- const struct spi_device_id *devid =
- spi_get_device_id(mc13xxx->spidev);
-
- if (!devid)
- return NULL;
-
- return mc13xxx_chipname[devid->driver_data];
+ return mc13xxx_chipname[mc13xxx->ictype];
}
#include <linux/mfd/mc13783.h>
@@ -563,7 +571,7 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
{
struct mc13xxx_platform_data *pdata =
- dev_get_platdata(&mc13xxx->spidev->dev);
+ dev_get_platdata(mc13xxx->dev);
return pdata->flags;
}
@@ -601,7 +609,7 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
};
init_completion(&adcdone_data.done);
- dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__);
+ dev_dbg(mc13xxx->dev, "%s\n", __func__);
mc13xxx_lock(mc13xxx);
@@ -643,7 +651,7 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
return -EINVAL;
}
- dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__);
+ dev_dbg(mc13783->mc13xxx.dev, "%s: request irq\n", __func__);
mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE,
mc13783_handler_adcdone, __func__, &adcdone_data);
mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE);
@@ -701,7 +709,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
if (!cell.name)
return -ENOMEM;
- return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0);
+ return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0);
}
static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
@@ -713,7 +721,6 @@ static int mc13xxx_probe(struct spi_device *spi)
{
struct mc13xxx *mc13xxx;
struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
- enum mc13xxx_id id;
int ret;
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
@@ -725,13 +732,36 @@ static int mc13xxx_probe(struct spi_device *spi)
spi->bits_per_word = 32;
spi_setup(spi);
+ mc13xxx->dev = &spi->dev;
mc13xxx->spidev = spi;
+ mc13xxx->read_dev = mc13xxx_spi_reg_read;
+ mc13xxx->write_dev = mc13xxx_spi_reg_write;
+
+ ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq);
+
+ if (ret) {
+ dev_set_drvdata(&spi->dev, NULL);
+ } else {
+ const struct spi_device_id *devid =
+ spi_get_device_id(mc13xxx->spidev);
+ if (!devid || devid->driver_data != mc13xxx->ictype)
+ dev_warn(mc13xxx->dev,
+ "device id doesn't match auto detection!\n");
+ }
+
+ return ret;
+}
+
+int mc13xxx_common_init(struct mc13xxx *mc13xxx,
+ struct mc13xxx_platform_data *pdata, int irq)
+{
+ int ret;
mutex_init(&mc13xxx->lock);
mc13xxx_lock(mc13xxx);
- ret = mc13xxx_identify(mc13xxx, &id);
- if (ret || id == MC13XXX_ID_INVALID)
+ ret = mc13xxx_identify(mc13xxx);
+ if (ret)
goto err_revision;
/* mask all irqs */
@@ -743,14 +773,13 @@ static int mc13xxx_probe(struct spi_device *spi)
if (ret)
goto err_mask;
- ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread,
+ ret = request_threaded_irq(irq, NULL, mc13xxx_irq_thread,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
if (ret) {
err_mask:
err_revision:
mutex_unlock(&mc13xxx->lock);
- dev_set_drvdata(&spi->dev, NULL);
kfree(mc13xxx);
return ret;
}
@@ -786,6 +815,7 @@ err_revision:
return 0;
}
+EXPORT_SYMBOL_GPL(mc13xxx_common_init);
static int __devexit mc13xxx_remove(struct spi_device *spi)
{
diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h
index a1d391b..f5d277d 100644
--- a/include/linux/mfd/mc13xxx.h
+++ b/include/linux/mfd/mc13xxx.h
@@ -21,6 +21,11 @@ int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val);
int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
u32 mask, u32 val);
+struct mc13xxx_platform_data;
+
+int mc13xxx_common_init(struct mc13xxx *mc13xxx,
+ struct mc13xxx_platform_data *pdata, int irq);
+
int mc13xxx_get_flags(struct mc13xxx *mc13xxx);
int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
--
1.7.1
^ permalink raw reply related
* mc13xxx-core, support for i2c, V4
From: Marc Reilly @ 2011-01-04 5:34 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
These patches add i2c support for the mc13xxx-core drive. For v4 I've tried to take
in all previous comments, hopefully making it better to follow, and bisectable.
Cheers,
Marc
^ permalink raw reply
* [PATCH 2/2] arm/pxa: buildfix for pam27x without fb
From: Eric Miao @ 2011-01-04 4:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201101032308.00036.marek.vasut@gmail.com>
On Tue, Jan 4, 2011 at 6:07 AM, Marek Vasut <marek.vasut@gmail.com> wrote:
> On Monday 03 January 2011 16:57:27 Eric Miao wrote:
>> On Thu, Dec 30, 2010 at 4:33 AM, Sebastian Andrzej Siewior
>>
>> <bigeasy@linutronix.de> wrote:
>> > |arch/arm/mach-pxa/palmld.c: In function ?palmld_init?:
>> > |arch/arm/mach-pxa/palmld.c:330: error: ?palm_320x480_lcd_mode?
>> > |undeclared (first use in this function) arch/arm/mach-pxa/palmld.c:330:
>> > |error: (Each undeclared identifier is reported only once
>> > |arch/arm/mach-pxa/palmld.c:330: error: for each function it appears
>> > |in.)
>> >
>> > Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>>
>> Marek,
>>
>> I need your ack please.
>
> I don't like it being done with preprocessor macro. Maybe we should rather pull
> palm_320x320_new_lcd_mode and others outside the #if defined() ?
>
Yeah, that will be at least a bit more consistent.
>>
>> > ---
>> > ?arch/arm/mach-pxa/include/mach/palm27x.h | ? ?2 +-
>> > ?1 files changed, 1 insertions(+), 1 deletions(-)
>> >
>> > diff --git a/arch/arm/mach-pxa/include/mach/palm27x.h
>> > b/arch/arm/mach-pxa/include/mach/palm27x.h index 0a5e5ea..7b7927c 100644
>> > --- a/arch/arm/mach-pxa/include/mach/palm27x.h
>> > +++ b/arch/arm/mach-pxa/include/mach/palm27x.h
>> > @@ -34,7 +34,7 @@ extern struct pxafb_mode_info
>> > palm_320x320_new_lcd_mode; extern void __init palm27x_lcd_init(int
>> > power,
>> > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct pxafb_mode_info *mode);
>> > ?#else
>> > -static inline void palm27x_lcd_init(int power, struct pxafb_mode_info
>> > *mode) {} +#define palm27x_lcd_init(power, mode) ?do { } while (0)
>> > ?#endif
>> >
>> > ?#if ? ?defined(CONFIG_USB_GADGET_PXA27X) || \
>> > --
>> > 1.7.3.2
>
^ permalink raw reply
* [PATCH V2] ARM: S5PV310: Implement kernel timers using MCT
From: Kukjin Kim @ 2011-01-04 2:18 UTC (permalink / raw)
To: linux-arm-kernel
From: Changhwan Youn <chaos.youn@samsung.com>
The Multi-Core Timer(MCT) of S5PV310 is designed for implementing
clock source timer and clock event timers. This patch implements
1 clock source timer with 64 bit free running counter of MCT and
2 clock event timers with two of 31-bit tick counters.
Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
Changes since v1:
- Addressed comments from Russell King
(implemented global timer, used local timer and so on)
- Fixed small things
arch/arm/Kconfig | 2 +-
arch/arm/mach-s5pv310/Kconfig | 5 +
arch/arm/mach-s5pv310/Makefile | 10 +-
arch/arm/mach-s5pv310/include/mach/regs-mct.h | 58 +++
arch/arm/mach-s5pv310/mct.c | 492 +++++++++++++++++++++++++
5 files changed, 564 insertions(+), 3 deletions(-)
create mode 100644 arch/arm/mach-s5pv310/include/mach/regs-mct.h
create mode 100644 arch/arm/mach-s5pv310/mct.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d56d21c..d9ee300 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1291,7 +1291,7 @@ config LOCAL_TIMERS
bool "Use local timer interrupts"
depends on SMP
default y
- select HAVE_ARM_TWD
+ select HAVE_ARM_TWD if !S5PV310_MCT
help
Enable support for local timers on SMP platforms, rather then the
legacy IPI broadcast method. Local timers allows the system
diff --git a/arch/arm/mach-s5pv310/Kconfig b/arch/arm/mach-s5pv310/Kconfig
index 1150b36..5fecf1b 100644
--- a/arch/arm/mach-s5pv310/Kconfig
+++ b/arch/arm/mach-s5pv310/Kconfig
@@ -14,6 +14,11 @@ config CPU_S5PV310
help
Enable S5PV310 CPU support
+config S5PV310_MCT
+ bool "Kernel timer support by MCT"
+ help
+ Use MCT (Multi Core Timer) as kernel timers
+
config S5PV310_SETUP_I2C1
bool
help
diff --git a/arch/arm/mach-s5pv310/Makefile b/arch/arm/mach-s5pv310/Makefile
index 84afc64..5ca7645 100644
--- a/arch/arm/mach-s5pv310/Makefile
+++ b/arch/arm/mach-s5pv310/Makefile
@@ -13,10 +13,16 @@ obj- :=
# Core support for S5PV310 system
obj-$(CONFIG_CPU_S5PV310) += cpu.o init.o clock.o irq-combiner.o
-obj-$(CONFIG_CPU_S5PV310) += setup-i2c0.o time.o gpiolib.o irq-eint.o
+obj-$(CONFIG_CPU_S5PV310) += setup-i2c0.o gpiolib.o irq-eint.o
-obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+ifeq ($(CONFIG_S5PV310_MCT),y)
+obj-y += mct.o
+else
+obj-y += time.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
+endif
+
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
# machine support
diff --git a/arch/arm/mach-s5pv310/include/mach/regs-mct.h b/arch/arm/mach-s5pv310/include/mach/regs-mct.h
new file mode 100644
index 0000000..1ed14f0
--- /dev/null
+++ b/arch/arm/mach-s5pv310/include/mach/regs-mct.h
@@ -0,0 +1,58 @@
+/* arch/arm/mach-s5pv310/include/mach/regs-mct.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * S5PV310 MCT configutation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_REGS_MCT_H
+#define __ASM_ARCH_REGS_MCT_H __FILE__
+
+#include <mach/map.h>
+
+#define S5PV310_MCTREG(x) (S5P_VA_SYSTIMER + (x))
+
+#define S5PV310_MCT_G_CNT_L S5PV310_MCTREG(0x100)
+#define S5PV310_MCT_G_CNT_U S5PV310_MCTREG(0x104)
+#define S5PV310_MCT_G_CNT_WSTAT S5PV310_MCTREG(0x110)
+
+#define S5PV310_MCT_G_COMP0_L S5PV310_MCTREG(0x200)
+#define S5PV310_MCT_G_COMP0_U S5PV310_MCTREG(0x204)
+#define S5PV310_MCT_G_COMP0_ADD_INCR S5PV310_MCTREG(0x208)
+
+#define S5PV310_MCT_G_TCON S5PV310_MCTREG(0x240)
+
+#define S5PV310_MCT_G_INT_CSTAT S5PV310_MCTREG(0x244)
+#define S5PV310_MCT_G_INT_ENB S5PV310_MCTREG(0x248)
+#define S5PV310_MCT_G_WSTAT S5PV310_MCTREG(0x24C)
+
+#define S5PV310_MCT_L0_TCNTB S5PV310_MCTREG(0x300)
+#define S5PV310_MCT_L0_ICNTB S5PV310_MCTREG(0x308)
+#define S5PV310_MCT_L0_TCON S5PV310_MCTREG(0x320)
+
+#define S5PV310_MCT_L0_INT_CSTAT S5PV310_MCTREG(0x330)
+#define S5PV310_MCT_L0_INT_ENB S5PV310_MCTREG(0x334)
+#define S5PV310_MCT_L0_WSTAT S5PV310_MCTREG(0x340)
+
+#define S5PV310_MCT_L1_TCNTB S5PV310_MCTREG(0x400)
+#define S5PV310_MCT_L1_ICNTB S5PV310_MCTREG(0x408)
+#define S5PV310_MCT_L1_TCON S5PV310_MCTREG(0x420)
+
+#define S5PV310_MCT_L1_INT_CSTAT S5PV310_MCTREG(0x430)
+#define S5PV310_MCT_L1_INT_ENB S5PV310_MCTREG(0x434)
+#define S5PV310_MCT_L1_WSTAT S5PV310_MCTREG(0x440)
+
+#define MCT_G_TCON_START (1 << 8)
+#define MCT_G_TCON_COMP0_AUTO_INC (1 << 1)
+#define MCT_G_TCON_COMP0_ENABLE (1 << 0)
+
+#define MCT_L_TCON_INTERVAL_MODE (1 << 2)
+#define MCT_L_TCON_INT_START (1 << 1)
+#define MCT_L_TCON_TIMER_START (1 << 0)
+
+#endif /* __ASM_ARCH_REGS_MCT_H */
diff --git a/arch/arm/mach-s5pv310/mct.c b/arch/arm/mach-s5pv310/mct.c
new file mode 100644
index 0000000..05c2280
--- /dev/null
+++ b/arch/arm/mach-s5pv310/mct.c
@@ -0,0 +1,492 @@
+/* linux/arch/arm/mach-s5pv310/mct.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * S5PV310 MCT(Multi-Core Timer) support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/percpu.h>
+
+#include <mach/map.h>
+#include <mach/regs-mct.h>
+#include <asm/mach/time.h>
+
+static unsigned long clk_cnt_per_tick;
+static unsigned long clk_rate;
+static cycle_t time_suspended;
+
+enum mct_tick_type {
+ MCT_TICK0,
+ MCT_TICK1,
+};
+
+static void s5pv310_mct_write(unsigned int value, void *addr)
+{
+ void __iomem *stat_addr;
+ u32 mask;
+ u32 i;
+
+ __raw_writel(value, addr);
+
+ switch ((u32) addr) {
+ case (u32) S5PV310_MCT_G_TCON:
+ stat_addr = S5PV310_MCT_G_WSTAT;
+ mask = 1 << 16; /* G_TCON write status */
+ break;
+ case (u32) S5PV310_MCT_G_COMP0_L:
+ stat_addr = S5PV310_MCT_G_WSTAT;
+ mask = 1 << 0; /* G_COMP0_L write status */
+ break;
+ case (u32) S5PV310_MCT_G_COMP0_U:
+ stat_addr = S5PV310_MCT_G_WSTAT;
+ mask = 1 << 1; /* G_COMP0_U write status */
+ break;
+ case (u32) S5PV310_MCT_G_COMP0_ADD_INCR:
+ stat_addr = S5PV310_MCT_G_WSTAT;
+ mask = 1 << 2; /* G_COMP0_ADD_INCR write status */
+ break;
+ case (u32) S5PV310_MCT_G_CNT_L:
+ stat_addr = S5PV310_MCT_G_CNT_WSTAT;
+ mask = 1 << 0; /* G_CNT_L write status */
+ break;
+ case (u32) S5PV310_MCT_G_CNT_U:
+ stat_addr = S5PV310_MCT_G_CNT_WSTAT;
+ mask = 1 << 1; /* G_CNT_U write status */
+ break;
+ case (u32) S5PV310_MCT_L0_TCON:
+ stat_addr = S5PV310_MCT_L0_WSTAT;
+ mask = 1 << 3; /* L0_TCON write status */
+ break;
+ case (u32) S5PV310_MCT_L1_TCON:
+ stat_addr = S5PV310_MCT_L1_WSTAT;
+ mask = 1 << 3; /* L1_TCON write status */
+ break;
+ case (u32) S5PV310_MCT_L0_TCNTB:
+ stat_addr = S5PV310_MCT_L0_WSTAT;
+ mask = 1 << 0; /* L0_TCNTB write status */
+ break;
+ case (u32) S5PV310_MCT_L1_TCNTB:
+ stat_addr = S5PV310_MCT_L1_WSTAT;
+ mask = 1 << 0; /* L1_TCNTB write status */
+ break;
+ case (u32) S5PV310_MCT_L0_ICNTB:
+ stat_addr = S5PV310_MCT_L0_WSTAT;
+ mask = 1 << 1; /* L0_ICNTB write status */
+ break;
+ case (u32) S5PV310_MCT_L1_ICNTB:
+ stat_addr = S5PV310_MCT_L1_WSTAT;
+ mask = 1 << 1; /* L1_ICNTB write status */
+ break;
+ default:
+ return;
+ }
+
+ /* Wait maximum 1 ms until written values are applied */
+ for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
+ if (__raw_readl(stat_addr) & mask) {
+ __raw_writel(mask, stat_addr);
+ return;
+ }
+
+ panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr);
+}
+
+/* Clocksource handling */
+static void s5pv310_mct_frc_start(u32 hi, u32 lo)
+{
+ u32 reg;
+
+ s5pv310_mct_write(lo, S5PV310_MCT_G_CNT_L);
+ s5pv310_mct_write(hi, S5PV310_MCT_G_CNT_U);
+
+ reg = __raw_readl(S5PV310_MCT_G_TCON);
+ reg |= MCT_G_TCON_START;
+ s5pv310_mct_write(reg, S5PV310_MCT_G_TCON);
+}
+
+static cycle_t s5pv310_frc_read(struct clocksource *cs)
+{
+ unsigned int lo, hi;
+ u32 hi2 = __raw_readl(S5PV310_MCT_G_CNT_U);
+
+ do {
+ hi = hi2;
+ lo = __raw_readl(S5PV310_MCT_G_CNT_L);
+ hi2 = __raw_readl(S5PV310_MCT_G_CNT_U);
+ } while (hi != hi2);
+
+ return ((cycle_t)hi << 32) | lo;
+}
+
+static void s5pv310_frc_suspend(struct clocksource *cs)
+{
+ time_suspended = s5pv310_frc_read(cs);
+};
+
+static void s5pv310_frc_resume(struct clocksource *cs)
+{
+ s5pv310_mct_frc_start((u32)(time_suspended >> 32), (u32)time_suspended);
+};
+
+struct clocksource mct_frc = {
+ .name = "mct-frc",
+ .rating = 400,
+ .read = s5pv310_frc_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .suspend = s5pv310_frc_suspend,
+ .resume = s5pv310_frc_resume,
+};
+
+static void __init s5pv310_clocksource_init(void)
+{
+ s5pv310_mct_frc_start(0, 0);
+
+ if (clocksource_register_hz(&mct_frc, clk_rate))
+ panic("%s: can't register clocksource\n", mct_frc.name);
+}
+
+static void s5pv310_mct_comp0_stop(void)
+{
+ unsigned int tcon;
+
+ tcon = __raw_readl(S5PV310_MCT_G_TCON);
+ tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
+
+ s5pv310_mct_write(tcon, S5PV310_MCT_G_TCON);
+ s5pv310_mct_write(0, S5PV310_MCT_G_INT_ENB);
+}
+
+static void s5pv310_mct_comp0_start(enum clock_event_mode mode,
+ unsigned long cycles)
+{
+ unsigned int tcon;
+ cycle_t comp_cycle;
+
+ tcon = __raw_readl(S5PV310_MCT_G_TCON);
+
+ if (mode == CLOCK_EVT_MODE_PERIODIC) {
+ tcon |= MCT_G_TCON_COMP0_AUTO_INC;
+ s5pv310_mct_write(cycles, S5PV310_MCT_G_COMP0_ADD_INCR);
+ }
+
+ comp_cycle = s5pv310_frc_read(&mct_frc) + cycles;
+ s5pv310_mct_write((u32)comp_cycle, S5PV310_MCT_G_COMP0_L);
+ s5pv310_mct_write((u32)(comp_cycle >> 32), S5PV310_MCT_G_COMP0_U);
+
+ s5pv310_mct_write(0x1, S5PV310_MCT_G_INT_ENB);
+
+ tcon |= MCT_G_TCON_COMP0_ENABLE;
+ s5pv310_mct_write(tcon , S5PV310_MCT_G_TCON);
+}
+
+static int s5pv310_comp_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ s5pv310_mct_comp0_start(evt->mode, cycles);
+
+ return 0;
+}
+
+static void s5pv310_comp_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ s5pv310_mct_comp0_stop();
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ s5pv310_mct_comp0_start(mode, clk_cnt_per_tick);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+}
+
+static struct clock_event_device mct_comp_device = {
+ .name = "mct-comp",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 250,
+ .set_next_event = s5pv310_comp_set_next_event,
+ .set_mode = s5pv310_comp_set_mode,
+};
+
+irqreturn_t s5pv310_mct_comp_isr(int irq, void *dev_id)
+{
+ struct clock_event_device *evt;
+
+ s5pv310_mct_write(0x1, S5PV310_MCT_G_INT_CSTAT);
+
+ evt = &mct_comp_device;
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction mct_comp_event_irq = {
+ .name = "mct_comp_irq",
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = s5pv310_mct_comp_isr,
+};
+
+static void s5pv310_clockevent_init(void)
+{
+ clk_cnt_per_tick = clk_rate / 2 / HZ;
+
+ clockevents_calc_mult_shift(&mct_comp_device, clk_rate / 2, 5);
+ mct_comp_device.max_delta_ns =
+ clockevent_delta2ns(0xffffffff, &mct_comp_device);
+ mct_comp_device.min_delta_ns =
+ clockevent_delta2ns(0xf, &mct_comp_device);
+ mct_comp_device.cpumask = cpumask_of(0);
+ clockevents_register_device(&mct_comp_device);
+
+ setup_irq(IRQ_MCT_G0, &mct_comp_event_irq);
+}
+
+#ifdef CONFIG_LOCAL_TIMERS
+/* Clock event handling */
+static void s5pv310_mct_tick_stop(enum mct_tick_type type)
+{
+ unsigned long tmp;
+ void __iomem *addr;
+
+ if (type == MCT_TICK0)
+ addr = S5PV310_MCT_L0_TCON;
+ else
+ addr = S5PV310_MCT_L1_TCON;
+
+ tmp = __raw_readl(addr);
+ tmp &= ~(MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START);
+ s5pv310_mct_write(tmp, addr);
+}
+
+static void s5pv310_mct_tick_start(enum mct_tick_type type,
+ unsigned long cycles)
+{
+ unsigned long tmp;
+ unsigned int tmp1, tmp2;
+ void __iomem *addr;
+
+ s5pv310_mct_tick_stop(type);
+
+ tmp1 = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */
+ tmp2 = 1 << 0; /* L_INT_ENB_CNTIE */
+
+ if (type == MCT_TICK0) {
+ /* update interrupt count buffer */
+ s5pv310_mct_write(tmp1, S5PV310_MCT_L0_ICNTB);
+
+ /* enable MCT tick0 interupt */
+ s5pv310_mct_write(tmp2, S5PV310_MCT_L0_INT_ENB);
+
+ addr = S5PV310_MCT_L0_TCON;
+ } else {
+ /* update interrupt count buffer */
+ s5pv310_mct_write(tmp1, S5PV310_MCT_L1_ICNTB);
+
+ /* enable MCT tick1 interupt */
+ s5pv310_mct_write(tmp2, S5PV310_MCT_L1_INT_ENB);
+
+ addr = S5PV310_MCT_L1_TCON;
+ }
+
+ tmp = __raw_readl(addr);
+ tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
+ MCT_L_TCON_INTERVAL_MODE;
+ s5pv310_mct_write(tmp, addr);
+}
+
+static inline int s5pv310_tick_set_next_event(enum mct_tick_type type,
+ unsigned long cycles)
+{
+ s5pv310_mct_tick_start(type, cycles);
+
+ return 0;
+}
+
+static inline void s5pv310_tick_set_mode(enum mct_tick_type type,
+ enum clock_event_mode mode)
+{
+ s5pv310_mct_tick_stop(type);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ s5pv310_mct_tick_start(type, clk_cnt_per_tick);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+}
+
+static int s5pv310_tick0_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ return s5pv310_tick_set_next_event(MCT_TICK0, cycles);
+}
+
+static int s5pv310_tick1_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ return s5pv310_tick_set_next_event(MCT_TICK1, cycles);
+}
+
+static void s5pv310_tick0_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ s5pv310_tick_set_mode(MCT_TICK0, mode);
+}
+
+static void s5pv310_tick1_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ s5pv310_tick_set_mode(MCT_TICK1, mode);
+}
+
+irqreturn_t s5pv310_mct_tick0_isr(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ /*
+ * This is for supporting oneshot mode.
+ * Mct would generate interrupt periodically
+ * without explicit stopping.
+ */
+ if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
+ s5pv310_mct_tick_stop(MCT_TICK0);
+
+ /* Clear the MCT tick0 interrupt */
+ s5pv310_mct_write(0x1, S5PV310_MCT_L0_INT_CSTAT);
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t s5pv310_mct_tick1_isr(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ /*
+ * This is for supporting oneshot mode.
+ * Mct would generate interrupt periodically
+ * without explicit stopping.
+ */
+ if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
+ s5pv310_mct_tick_stop(MCT_TICK1);
+
+ /* Clear the MCT tick1 interrupt */
+ s5pv310_mct_write(0x1, S5PV310_MCT_L1_INT_CSTAT);
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction mct_tick0_event_irq = {
+ .name = "mct_tick0_irq",
+ .flags = IRQF_TIMER | IRQF_NOBALANCING,
+ .handler = s5pv310_mct_tick0_isr,
+};
+
+static struct irqaction mct_tick1_event_irq = {
+ .name = "mct_tick1_irq",
+ .flags = IRQF_TIMER | IRQF_NOBALANCING,
+ .handler = s5pv310_mct_tick1_isr,
+};
+
+static void s5pv310_mct_tick_init(struct clock_event_device *evt)
+{
+ unsigned int cpu = smp_processor_id();
+
+ if (cpu == 0) {
+ s5pv310_mct_write(0x1, S5PV310_MCT_L0_TCNTB);
+ evt->set_next_event = s5pv310_tick0_set_next_event;
+ evt->set_mode = s5pv310_tick0_set_mode;
+ evt->name = "mct_tick0";
+ } else {
+ s5pv310_mct_write(0x1, S5PV310_MCT_L1_TCNTB);
+ evt->set_next_event = s5pv310_tick1_set_next_event;
+ evt->set_mode = s5pv310_tick1_set_mode;
+ evt->name = "mct_tick1";
+ }
+
+ evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+ evt->rating = 450;
+
+ clockevents_calc_mult_shift(evt, clk_rate / 2, 5);
+ evt->max_delta_ns =
+ clockevent_delta2ns(0x7fffffff, evt);
+ evt->min_delta_ns =
+ clockevent_delta2ns(0xf, evt);
+
+ clockevents_register_device(evt);
+
+ if (cpu == 0) {
+ mct_tick0_event_irq.dev_id = evt;
+ setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
+ } else {
+ mct_tick1_event_irq.dev_id = evt;
+ irq_set_affinity(IRQ_MCT1, cpumask_of(1));
+ setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
+ }
+}
+
+/* Setup the local clock events for a CPU */
+void __cpuinit local_timer_setup(struct clock_event_device *evt)
+{
+ s5pv310_mct_tick_init(evt);
+}
+
+int local_timer_ack(void)
+{
+ return 0;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+void local_timer_stop(void)
+{
+ s5pv310_mct_tick_stop(smp_processor_id());
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+#endif /* CONFIG_LOCAL_TIMERS */
+
+static void __init s5pv310_timer_resources(void)
+{
+ struct clk *mct_clk;
+ mct_clk = clk_get(NULL, "xtal");
+
+ clk_rate = clk_get_rate(mct_clk);
+}
+
+static void __init s5pv310_timer_init(void)
+{
+ s5pv310_timer_resources();
+ s5pv310_clocksource_init();
+ s5pv310_clockevent_init();
+}
+
+struct sys_timer s5pv310_timer = {
+ .init = s5pv310_timer_init,
+};
--
1.6.2.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox