* [PATCH 0/0] RFC: ARM: Thumb-2: Symbol manipulation macros for function body copying
From: Russell King - ARM Linux @ 2011-01-12 16:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTimB1dcK=F1bFh6bUtBPY0gk+Y+Y0SQqrSRW0YFa@mail.gmail.com>
On Wed, Jan 12, 2011 at 10:00:25AM -0600, Dave Martin wrote:
> omap provides some infrastructure for both allocating SRAM space and
> doing the copy, using omap_sram_push() and friends. So I wasn't sure
> what the correct level of abstraction was for the new helpers.
> Certainly, providing a sort of "function memcpy" macro like your
> copy_fn_to_sram makes sense.
It'd just be a matter of splitting the copying out of omap_sram_push().
> I think this should still be safe from a type system perspective:
> providing the "blind" type casts using asm() appear somewhere in
> the execution flow C shouldn't make silly assumptions even if Linux
> ends up enabling multifile optimisation sometime in the future.
Yes. The only thing that is missing from my version is the
flush_icache_range() which should also be there.
> > Used by:
> > extern void my_func(int foo);
> > extern int my_func_size;
>
> Potentially, we could define, an extra assembler macro to complement
> ENDPROC() which records the size of a function automatically. What do
> you think?
That would pad the code out with a fair number of additional integers.
That's probably not a good idea.
> The model used in the omap code is to copy some functions into SRAM
> ahead of time and stash the pointers away to be called later: for that
> model, it's not so useful to have something like call_my_func
> directly. Also, I wasn't sure whether conflating other functionality
> such as cache flushing into the new macros would be a good idea -- is
> might be cleaner and more maintainable, but might result in less
> efficient usage. Any thoughts?
My example was only that - an example. You can also use it in the
way you describe too:
to = omap_sram_push(size);
_omap_sram_reprogram_clock = copy_fn_to_sram(to,
omap1_sram_reprogram_clock, size);
and it'll also ensure type-safety between the omap1_sram_reprogram_clock
and _omap_sram_reprogram_clock symbols, which the current code doesn't
do.
^ permalink raw reply
* ARM: relocation out of range (when loading a module)
From: Russell King - ARM Linux @ 2011-01-12 16:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTimAFp7ORrJLw2d8-i5pK0MZGTyAN5kmRR37ky1v@mail.gmail.com>
On Wed, Jan 12, 2011 at 10:05:19AM -0600, Dave Martin wrote:
> In general, do we expect always to be able to avoid the situation
> where branches in the kernel may need to cover too large a range ...
> and is there any strategy for working aroung it?
It's not that big a problem - 99.999999% of setups never run into the
problem. It's only those who use large initramfs's built into their
kernel image at present, and that is ultimately solvable.
I don't think we need to litter code with TODO comments.
> If we have problems branching from the modules area into vmlinux, we
> could possibly build modules with -fPIC : this would remove the
> restriction on branch range, though there would also be some
> performance impact for the modules...
That also brings in issues with GOT tables and the like, and also requires
different build options for modules and the main kernel. I don't think
kbuild is setup to do that - and I'd argue that it's unnecessary if we
fix the layout of the kernel image.
As I said a few emails ago, shuffling sections around in the image is
not as trivial as it looks on the face of it as we make assumptions
about what is in _stext.._etext, _sdata.._edata, whether there's anything
between _etext.._sdata, and other symbolic ranges.
At the moment, _stext.._etext + _sdata.._edata covers the entire kernel
image - with the init sections at the start of _stext. Putting the init
sections between _etext and _sdata makes a hole in the middle of that,
which may be suboptimal for page allocation. It also means that the
range no longer covers all kernel stuff. Also whether _stext = _text,
_sdata = _data, etc.
Another technicality is that some of these ranges are used for stuff like
DMA API debugging:
if (overlap(addr, len, _text, _etext) ||
overlap(addr, len, __start_rodata, __end_rodata))
err_printk(dev, NULL, "DMA-API: device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n", addr, len);
The last pieces of the puzzle is whether we have anything that implicitly
relies on the init section being low down and freeing its pages (eg, as
a way of stopping that memory being used for non-DMA stuff.)
I do have a patch which shuffles some of this stuff around, but I'm not
entirely happy with it yet.
^ permalink raw reply
* [RFC] arm: Defer lookup of machine_type and vet of atags to setup.c
From: Grant Likely @ 2011-01-12 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110112155215.GB11039@n2100.arm.linux.org.uk>
On Wed, Jan 12, 2011 at 8:52 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Jan 12, 2011 at 08:46:44AM -0700, Grant Likely wrote:
>> Looks like I've hit a hiccup though. ?When I remove the first call to
>> __lookup_machine_type, then it is only called after the MMU is turned
>> on and the kernel is no longer able to output the list of configured
>> machine ids when it doesn't recognize the value in r1 (tested with
>> qemu versatile emulation). ?I'm still investigating, so I'll defer
>> reposting the patch until I've got this issue solved.
>
> It only does this when DEBUG_LL is enabled - at which point you have
> printascii, printhex8, etc available (although there's no prototype
> for them.)
>
> You could use snprintf() to format a message and then use printascii()
> (conditional on CONFIG_DEBUG_LL as the existing code does) as well as
> printk("%s", buffer). ?That means if you have a debugger you can dump
> the kernel ring buffer and see the message, or see it via the serial
> port/debugging channel if DEBUG_LL is enabled.
Actually it looks like the real problem is that the mmu has been
turned on, but the virtual mappings for devices have not yet been
established, and so the debug macros aren't using a valid address.
I'm using printk to get output into the ring buffer in my patch to
rework lookup_machine_type() in C, and that does indeed output to the
ring buffer, but the kernel cannot spit stuff out the serial port.
It looks like I'd need to get past paging_init() in order to get
ll_debug working between turning on the mmu and paging_init(), but
paging_init() needs the mdesc pointer, and the whole point of the
error message is that the mdesc pointer is unknown! I don't see any
code that sets up a debug mapping of the uart before paging_init time.
I could try to implement something like that, but it is looking to be
more complicated than it is worth when the current code works just
fine.
Let me know if I've missed something, but I think I should drop the
removal of __lookup_machine_type from head.S from my patch.
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* ARM: relocation out of range (when loading a module)
From: Matthieu CASTET @ 2011-01-12 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110111155930.GH11039@n2100.arm.linux.org.uk>
Russell King - ARM Linux a ?crit :
> On Tue, Jan 11, 2011 at 09:16:38PM +0530, Rabin Vincent wrote:
>> It's possible to hack around this by placing the initramfs at the end of
>> the kernel image rather than at the beginning with the rest of the init
>> data. Something like the below should work, although you should also
>> probably take care of alignment and also have this section freed when
>> the rest of the init data is freed.
>
> You're then running into problems as _sdata.._edata is copied to RAM on
> XIP kernels, and you really don't want to waste time copying the
> initramfs to RAM.
>
But in this case initramfs is after edata and before bss.
So where is the problem ?
Matthieu
^ permalink raw reply
* [RFC] arm: Defer lookup of machine_type and vet of atags to setup.c
From: Russell King - ARM Linux @ 2011-01-12 16:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTinwGd9pa7YQ=5hXfarw=MB=YdpBuN7xJAd+Ps4f@mail.gmail.com>
On Wed, Jan 12, 2011 at 09:24:34AM -0700, Grant Likely wrote:
> Actually it looks like the real problem is that the mmu has been
> turned on, but the virtual mappings for devices have not yet been
> established, and so the debug macros aren't using a valid address.
They should be, as they're valid for use from assembly prior to MMU
turn-on, assembly after MMU turn-on and C code.
If CONFIG_DEBUG_LL is enabled, create_page_tables() should be setting
up the necessary initial mappings for printascii() to work.
Maybe the efforts to unify that stuff ended up breaking the printascii
debugging mechanism, and we now require a new debugging mechanism to
debug the printascii debugging mechanism... :-P
I've used it on Versatile Express during the last week and it did work
right from before setup_arch() was called.
> It looks like I'd need to get past paging_init() in order to get
> ll_debug working between turning on the mmu and paging_init(), but
> paging_init() needs the mdesc pointer, and the whole point of the
> error message is that the mdesc pointer is unknown! I don't see any
> code that sets up a debug mapping of the uart before paging_init time.
See the #ifdef CONFIG_DEBUG_LL section in create_page_tables in head.S
^ permalink raw reply
* [PATCH] ARM: Thumb-2: Fix out-of-range offset for Thumb-2 in proc-v7.S
From: Dave Martin @ 2011-01-12 16:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110112081446.GB24920@pengutronix.de>
2011/1/12 Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>:
> On Tue, Jan 11, 2011 at 06:03:33PM -0600, Dave Martin wrote:
>> The following patch introduces a pre-increment addressing
>> offset which is out of range for Thumb-2:
>> ? ? ARM: pgtable: switch order of Linux vs hardware page tables
> a commit id would be nice here
>
> Uwe
Apologies--- it's commit d30e45eeabefadc6039d7f876a59e5f5f6cb11c6
Cheers
---Dave
>>
>> 162: ?str ? ? r3, [r0, #2048]!
>>
>> Thumb-2 only permits offsets <256 for pre-increment addressing.
>>
>> This patch replaces the store instruction with a suitable add-str
>> pair for the Thumb-2 case.
>>
>> Signed-off-by: Dave Martin <dave.martin@linaro.org>
>> ---
>> KernelVersion: v2.6.37
>>
>> ?arch/arm/mm/proc-v7.S | ? ?4 +++-
>> ?1 files changed, 3 insertions(+), 1 deletions(-)
>>
>> diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
>> index b49fab2..0c1172b 100644
>> --- a/arch/arm/mm/proc-v7.S
>> +++ b/arch/arm/mm/proc-v7.S
>> @@ -159,7 +159,9 @@ ENTRY(cpu_v7_set_pte_ext)
>> ? ? ? tstne ? r1, #L_PTE_PRESENT
>> ? ? ? moveq ? r3, #0
>>
>> - ? ? str ? ? r3, [r0, #2048]!
>> + ARM( ? ? ? ?str ? ? r3, [r0, #2048]! )
>> + THUMB( ? ? ?add ? ? r0, r0, #2048 )
>> + THUMB( ? ? ?str ? ? r3, [r0] )
>> ? ? ? mcr ? ? p15, 0, r0, c7, c10, 1 ? ? ? ? ?@ flush_pte
>> ?#endif
>> ? ? ? mov ? ? pc, lr
>> --
>> 1.7.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>
> --
> Pengutronix e.K. ? ? ? ? ? ? ? ? ? ? ? ? ? | Uwe Kleine-K?nig ? ? ? ? ? ?|
> Industrial Linux Solutions ? ? ? ? ? ? ? ? | http://www.pengutronix.de/ ?|
>
^ permalink raw reply
* ARM: relocation out of range (when loading a module)
From: Russell King - ARM Linux @ 2011-01-12 16:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4D2DD5F8.5000200@parrot.com>
On Wed, Jan 12, 2011 at 05:25:28PM +0100, Matthieu CASTET wrote:
> Russell King - ARM Linux a ?crit :
>> On Tue, Jan 11, 2011 at 09:16:38PM +0530, Rabin Vincent wrote:
>>> It's possible to hack around this by placing the initramfs at the end of
>>> the kernel image rather than at the beginning with the rest of the init
>>> data. Something like the below should work, although you should also
>>> probably take care of alignment and also have this section freed when
>>> the rest of the init data is freed.
>>
>> You're then running into problems as _sdata.._edata is copied to RAM on
>> XIP kernels, and you really don't want to waste time copying the
>> initramfs to RAM.
>>
> But in this case initramfs is after edata and before bss.
Right, so that isn't a concern.
> So where is the problem ?
There is a problem with it, as noted by Rabin. The initramfs will
never be freed. Let's try to find a proper solution to this rather
than everyone sticking their half-baked ideas at it. Let's see if
we can rearrange the _entire_ layout in a way that sorts this out
once and for all without making things even more complicated than
they already are today.
We've already got enough variables to think about here without having
yet more freeable sections scattered in the middle of the existing
layout.
^ permalink raw reply
* [RFC] arm: Defer lookup of machine_type and vet of atags to setup.c
From: Nicolas Pitre @ 2011-01-12 16:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTinwGd9pa7YQ=5hXfarw=MB=YdpBuN7xJAd+Ps4f@mail.gmail.com>
On Wed, 12 Jan 2011, Grant Likely wrote:
> Actually it looks like the real problem is that the mmu has been
> turned on, but the virtual mappings for devices have not yet been
> established, and so the debug macros aren't using a valid address.
A temporary virtual mapping should be there -- look for addruart in
head.S.
> I'm using printk to get output into the ring buffer in my patch to
> rework lookup_machine_type() in C, and that does indeed output to the
> ring buffer, but the kernel cannot spit stuff out the serial port.
>
> It looks like I'd need to get past paging_init() in order to get
> ll_debug working between turning on the mmu and paging_init(), but
> paging_init() needs the mdesc pointer, and the whole point of the
> error message is that the mdesc pointer is unknown! I don't see any
> code that sets up a debug mapping of the uart before paging_init time.
See above.
> I could try to implement something like that, but it is looking to be
> more complicated than it is worth when the current code works just
> fine.
My bet is that there is a bug with the current code that you are
exposing.
> Let me know if I've missed something, but I think I should drop the
> removal of __lookup_machine_type from head.S from my patch.
It's not the location of that code which is a problem. Even if you
leave that code in place, you want to call it later and I bet that the
display would be broken even if __lookup_machine_type doesn't move.
Nicolas
^ permalink raw reply
* [PATCH 0/0] RFC: ARM: Thumb-2: Symbol manipulation macros for function body copying
From: Dave Martin @ 2011-01-12 16:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110112161151.GB18833@n2100.arm.linux.org.uk>
On Wed, Jan 12, 2011 at 10:11 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Jan 12, 2011 at 10:00:25AM -0600, Dave Martin wrote:
>> omap provides some infrastructure for both allocating SRAM space and
>> doing the copy, using omap_sram_push() and friends. ?So I wasn't sure
>> what the correct level of abstraction was for the new helpers.
>> Certainly, providing a sort of "function memcpy" macro like your
>> copy_fn_to_sram makes sense.
>
> It'd just be a matter of splitting the copying out of omap_sram_push().
>
>> I think this should still be safe from a type system perspective:
>> providing the "blind" type casts using asm() appear somewhere in
>> the execution flow C shouldn't make silly assumptions even if Linux
>> ends up enabling multifile optimisation sometime in the future.
>
> Yes. ?The only thing that is missing from my version is the
> flush_icache_range() which should also be there.
>
>> > Used by:
>> > extern void my_func(int foo);
>> > extern int my_func_size;
>>
>> Potentially, we could define, an extra assembler macro to complement
>> ENDPROC() which records the size of a function automatically. ?What do
>> you think?
Sure -- we shouldn't change ENDPROC() itself, but we could have, say,
and ENDPROC_SZ() macro which people should use strictly when they know
they need it.
>
> That would pad the code out with a fair number of additional integers.
> That's probably not a good idea.
>
>> The model used in the omap code is to copy some functions into SRAM
>> ahead of time and stash the pointers away to be called later: for that
>> model, it's not so useful to have something like call_my_func
>> directly. ?Also, I wasn't sure whether conflating other functionality
>> such as cache flushing into the new macros would be a good idea -- is
>> might be cleaner and more maintainable, but might result in less
>> efficient usage. ?Any thoughts?
>
> My example was only that - an example. ?You can also use it in the
> way you describe too:
>
> ? ? ? ?to = omap_sram_push(size);
> ? ? ? ?_omap_sram_reprogram_clock = copy_fn_to_sram(to,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?omap1_sram_reprogram_clock, size);
>
> and it'll also ensure type-safety between the omap1_sram_reprogram_clock
> and _omap_sram_reprogram_clock symbols, which the current code doesn't
> do.
>
Ah, OK -- I'd interpreted call_my_func() as part of the API, rather
than a usage example.
I'll have a think and update the patch.
Cheers
---Dave
^ permalink raw reply
* [PATCH] ARM: fix dma_unmap_sg() documentation
From: Linus Walleij @ 2011-01-12 17:10 UTC (permalink / raw)
To: linux-arm-kernel
The kerneldoc for this function is at odds with the DMA-API
document, which holds, so fix it.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
---
arch/arm/mm/dma-mapping.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 1b3fc5c..82a093c 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -586,7 +586,7 @@ EXPORT_SYMBOL(dma_map_sg);
* dma_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
* @sg: list of buffers
- * @nents: number of buffers to unmap (returned from dma_map_sg)
+ * @nents: number of buffers to unmap (same as was passed to dma_map_sg)
* @dir: DMA transfer direction (same as was passed to dma_map_sg)
*
* Unmap a set of streaming mode DMA translations. Again, CPU access
--
1.7.3.2
^ permalink raw reply related
* [RFC] arm: Defer lookup of machine_type and vet of atags to setup.c
From: Grant Likely @ 2011-01-12 17:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.00.1101121145430.24532@xanadu.home>
On Wed, Jan 12, 2011 at 9:53 AM, Nicolas Pitre <nico@fluxnic.net> wrote:
> On Wed, 12 Jan 2011, Grant Likely wrote:
>
>> Actually it looks like the real problem is that the mmu has been
>> turned on, but the virtual mappings for devices have not yet been
>> established, and so the debug macros aren't using a valid address.
>
> A temporary virtual mapping should be there -- look for addruart in
> head.S.
Hi Russell and Nicolas,
Oops, yes all the early debug stuff works fine. Stupid human trick on
my end, but I've sorted it out now. Thanks for the help. I'll have
patches to post later today.
g.
>
>> I'm using printk to get output into the ring buffer in my patch to
>> rework lookup_machine_type() in C, and that does indeed output to the
>> ring buffer, but the kernel cannot spit stuff out the serial port.
>>
>> It looks like I'd need to get past paging_init() in order to get
>> ll_debug working between turning on the mmu and paging_init(), but
>> paging_init() needs the mdesc pointer, and the whole point of the
>> error message is that the mdesc pointer is unknown! ?I don't see any
>> code that sets up a debug mapping of the uart before paging_init time.
>
> See above.
>
>> ?I could try to implement something like that, but it is looking to be
>> more complicated than it is worth when the current code works just
>> fine.
>
> My bet is that there is a bug with the current code that you are
> exposing.
>
>> Let me know if I've missed something, but I think I should drop the
>> removal of __lookup_machine_type from head.S from my patch.
>
> It's not the location of that code which is a problem. ?Even if you
> leave that code in place, you want to call it later and I bet that the
> display would be broken even if __lookup_machine_type doesn't move.
>
>
> Nicolas
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [RFC] arm: Defer lookup of machine_type and vet of atags to setup.c
From: Russell King - ARM Linux @ 2011-01-12 17:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTinPibTVUu2zoMbDYnSWXGvB6dVjCp54DoN9S1p3@mail.gmail.com>
On Wed, Jan 12, 2011 at 10:16:44AM -0700, Grant Likely wrote:
> On Wed, Jan 12, 2011 at 9:53 AM, Nicolas Pitre <nico@fluxnic.net> wrote:
> > On Wed, 12 Jan 2011, Grant Likely wrote:
> >
> >> Actually it looks like the real problem is that the mmu has been
> >> turned on, but the virtual mappings for devices have not yet been
> >> established, and so the debug macros aren't using a valid address.
> >
> > A temporary virtual mapping should be there -- look for addruart in
> > head.S.
>
> Hi Russell and Nicolas,
>
> Oops, yes all the early debug stuff works fine. Stupid human trick on
> my end, but I've sorted it out now. Thanks for the help. I'll have
> patches to post later today.
I just hacked this up, and on Versatile (real hardware) it produces
the below for an invalid r1 value - and of course works for a proper r1
value.
Uncompressing Linux... done, booting the kernel.
Error: unrecognized/unsupported machine ID (r1 = 0x00123456).
Available machine support:
ID (hex) NAME
00000183 ARM-Versatile PB
0000025e ARM-Versatile AB
Please check your kernel config and/or bootloader.
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index bbecaac..c84b57d 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -25,81 +25,6 @@
* machine ID for example).
*/
__HEAD
-__error_a:
-#ifdef CONFIG_DEBUG_LL
- mov r4, r1 @ preserve machine ID
- adr r0, str_a1
- bl printascii
- mov r0, r4
- bl printhex8
- adr r0, str_a2
- bl printascii
- adr r3, __lookup_machine_type_data
- ldmia r3, {r4, r5, r6} @ get machine desc list
- sub r4, r3, r4 @ get offset between virt&phys
- add r5, r5, r4 @ convert virt addresses to
- add r6, r6, r4 @ physical address space
-1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type
- bl printhex8
- mov r0, #'\t'
- bl printch
- ldr r0, [r5, #MACHINFO_NAME] @ get machine name
- add r0, r0, r4
- bl printascii
- mov r0, #'\n'
- bl printch
- add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
- cmp r5, r6
- blo 1b
- adr r0, str_a3
- bl printascii
- b __error
-ENDPROC(__error_a)
-
-str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x"
-str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
-str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n"
- .align
-#endif
-
-/*
- * Lookup machine architecture in the linker-build list of architectures.
- * Note that we can't use the absolute addresses for the __arch_info
- * lists since we aren't running with the MMU on (and therefore, we are
- * not in the correct address space). We have to calculate the offset.
- *
- * r1 = machine architecture number
- * Returns:
- * r3, r4, r6 corrupted
- * r5 = mach_info pointer in physical address space
- */
-__lookup_machine_type:
- adr r3, __lookup_machine_type_data
- ldmia r3, {r4, r5, r6}
- sub r3, r3, r4 @ get offset between virt&phys
- add r5, r5, r3 @ convert virt addresses to
- add r6, r6, r3 @ physical address space
-1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
- teq r3, r1 @ matches loader number?
- beq 2f @ found
- add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
- cmp r5, r6
- blo 1b
- mov r5, #0 @ unknown machine
-2: mov pc, lr
-ENDPROC(__lookup_machine_type)
-
-/*
- * Look in arch/arm/kernel/arch.[ch] for information about the
- * __arch_info structures.
- */
- .align 2
- .type __lookup_machine_type_data, %object
-__lookup_machine_type_data:
- .long .
- .long __arch_info_begin
- .long __arch_info_end
- .size __lookup_machine_type_data, . - __lookup_machine_type_data
/* Determine validity of the r2 atags pointer. The heuristic requires
* that the pointer be aligned, in the first 16k of physical RAM and
@@ -107,8 +32,6 @@ __lookup_machine_type_data:
* of this function may be more lenient with the physical address and
* may also be able to move the ATAGS block if necessary.
*
- * r8 = machinfo
- *
* Returns:
* r2 either valid atags pointer, or zero
* r5, r6 corrupted
@@ -183,17 +106,6 @@ __mmap_switched_data:
.size __mmap_switched_data, . - __mmap_switched_data
/*
- * This provides a C-API version of __lookup_machine_type
- */
-ENTRY(lookup_machine_type)
- stmfd sp!, {r4 - r6, lr}
- mov r1, r0
- bl __lookup_machine_type
- mov r0, r5
- ldmfd sp!, {r4 - r6, pc}
-ENDPROC(lookup_machine_type)
-
-/*
* This provides a C-API version of __lookup_processor_type
*/
ENTRY(lookup_processor_type)
diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S
index 814ce1a..6b1e0ad 100644
--- a/arch/arm/kernel/head-nommu.S
+++ b/arch/arm/kernel/head-nommu.S
@@ -44,9 +44,6 @@ ENTRY(stext)
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error 'p'
- bl __lookup_machine_type @ r5=machinfo
- movs r8, r5 @ invalid machine (r5=0)?
- beq __error_a @ yes, error 'a'
adr lr, BSYM(__after_proc_init) @ return (PIC) address
ARM( add pc, r10, #PROCINFO_INITFUNC )
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 06aed19..084db6c 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -87,14 +87,10 @@ ENTRY(stext)
movs r10, r5 @ invalid processor (r5=0)?
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p @ yes, error 'p'
- bl __lookup_machine_type @ r5=machinfo
- movs r8, r5 @ invalid machine (r5=0)?
- THUMB( it eq ) @ force fixup-able long branch encoding
- beq __error_a @ yes, error 'a'
/*
* r1 = machine no, r2 = atags,
- * r8 = machinfo, r9 = cpuid, r10 = procinfo
+ * r9 = cpuid, r10 = procinfo
*/
bl __vet_atags
#ifdef CONFIG_SMP_ON_UP
@@ -108,7 +104,7 @@ ENTRY(stext)
/*
* The following calls CPU specific code in a position independent
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
- * xxx_proc_info structure selected by __lookup_machine_type
+ * xxx_proc_info structure selected by __lookup_processor_type
* above. On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
@@ -127,7 +123,6 @@ ENDPROC(stext)
* amount which are required to get the kernel running, which
* generally means mapping in the kernel code.
*
- * r8 = machinfo
* r9 = cpuid
* r10 = procinfo
*
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 7c5499d..eb952a7 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -308,7 +308,44 @@ static void __init cacheid_init(void)
* already provide the required functionality.
*/
extern struct proc_info_list *lookup_processor_type(unsigned int);
-extern struct machine_desc *lookup_machine_type(unsigned int);
+
+static void __init early_print(const char *str, ...)
+{
+ extern void printascii(const char *);
+ char buf[256];
+ va_list ap;
+
+ va_start(ap, str);
+ vsnprintf(buf, sizeof(buf), str, ap);
+ va_end(ap);
+
+#ifdef CONFIG_DEBUG_LL
+ printascii(buf);
+#endif
+ printk("%s", buf);
+}
+
+static struct machine_desc * __init lookup_machine_type(unsigned int type)
+{
+ extern struct machine_desc __arch_info_begin[], __arch_info_end[];
+ struct machine_desc *p;
+
+ for (p = __arch_info_begin; p < __arch_info_end; p++)
+ if (type == p->nr)
+ return p;
+
+ early_print("\n"
+ "Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n"
+ "Available machine support:\n\nID (hex)\tNAME\n", type);
+
+ for (p = __arch_info_begin; p < __arch_info_end; p++)
+ early_print("%08x\t%s\n", p->nr, p->name);
+
+ early_print("\nPlease check your kernel config and/or bootloader.\n");
+
+ while (true)
+ /* can't use cpu_relax() here as it may require MMU setup */;
+}
static void __init feat_v6_fixup(void)
{
^ permalink raw reply related
* [PATCH] ARM: fix dma_unmap_sg() documentation
From: Russell King - ARM Linux @ 2011-01-12 17:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294852244-11644-1-git-send-email-linus.walleij@stericsson.com>
On Wed, Jan 12, 2011 at 06:10:44PM +0100, Linus Walleij wrote:
> The kerneldoc for this function is at odds with the DMA-API
> document, which holds, so fix it.
Patch is fine, patch system please? Thanks.
^ permalink raw reply
* [RFC] arm: Defer lookup of machine_type and vet of atags to setup.c
From: Grant Likely @ 2011-01-12 17:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110112172552.GF11039@n2100.arm.linux.org.uk>
On Wed, Jan 12, 2011 at 10:25 AM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Jan 12, 2011 at 10:16:44AM -0700, Grant Likely wrote:
>> On Wed, Jan 12, 2011 at 9:53 AM, Nicolas Pitre <nico@fluxnic.net> wrote:
>> > On Wed, 12 Jan 2011, Grant Likely wrote:
>> >
>> >> Actually it looks like the real problem is that the mmu has been
>> >> turned on, but the virtual mappings for devices have not yet been
>> >> established, and so the debug macros aren't using a valid address.
>> >
>> > A temporary virtual mapping should be there -- look for addruart in
>> > head.S.
>>
>> Hi Russell and Nicolas,
>>
>> Oops, yes all the early debug stuff works fine. ?Stupid human trick on
>> my end, but I've sorted it out now. ?Thanks for the help. ?I'll have
>> patches to post later today.
>
> I just hacked this up, and on Versatile (real hardware) it produces
> the below for an invalid r1 value - and of course works for a proper r1
> value.
Heh, that look pretty close to identical to what I was just about to send.
Acked-by: Grant Likely <grant.likely@secretlab.ca>
I'll send the updated atags patch rebased and retested on top of this one.
g.
>
> Uncompressing Linux... done, booting the kernel.
>
> Error: unrecognized/unsupported machine ID (r1 = 0x00123456).
>
> Available machine support:
>
> ID (hex) ? ? ? ?NAME
> 00000183 ? ? ? ?ARM-Versatile PB
> 0000025e ? ? ? ?ARM-Versatile AB
>
> Please check your kernel config and/or bootloader.
>
> diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
> index bbecaac..c84b57d 100644
> --- a/arch/arm/kernel/head-common.S
> +++ b/arch/arm/kernel/head-common.S
> @@ -25,81 +25,6 @@
> ?* machine ID for example).
> ?*/
> ? ? ? ?__HEAD
> -__error_a:
> -#ifdef CONFIG_DEBUG_LL
> - ? ? ? mov ? ? r4, r1 ? ? ? ? ? ? ? ? ? ? ? ? ?@ preserve machine ID
> - ? ? ? adr ? ? r0, str_a1
> - ? ? ? bl ? ? ?printascii
> - ? ? ? mov ? ? r0, r4
> - ? ? ? bl ? ? ?printhex8
> - ? ? ? adr ? ? r0, str_a2
> - ? ? ? bl ? ? ?printascii
> - ? ? ? adr ? ? r3, __lookup_machine_type_data
> - ? ? ? ldmia ? r3, {r4, r5, r6} ? ? ? ? ? ? ? ?@ get machine desc list
> - ? ? ? sub ? ? r4, r3, r4 ? ? ? ? ? ? ? ? ? ? ?@ get offset between virt&phys
> - ? ? ? add ? ? r5, r5, r4 ? ? ? ? ? ? ? ? ? ? ?@ convert virt addresses to
> - ? ? ? add ? ? r6, r6, r4 ? ? ? ? ? ? ? ? ? ? ?@ physical address space
> -1: ? ? ldr ? ? r0, [r5, #MACHINFO_TYPE] ? ? ? ?@ get machine type
> - ? ? ? bl ? ? ?printhex8
> - ? ? ? mov ? ? r0, #'\t'
> - ? ? ? bl ? ? ?printch
> - ? ? ? ldr ? ? r0, [r5, #MACHINFO_NAME] ? ? ? ?@ get machine name
> - ? ? ? add ? ? r0, r0, r4
> - ? ? ? bl ? ? ?printascii
> - ? ? ? mov ? ? r0, #'\n'
> - ? ? ? bl ? ? ?printch
> - ? ? ? add ? ? r5, r5, #SIZEOF_MACHINE_DESC ? ?@ next machine_desc
> - ? ? ? cmp ? ? r5, r6
> - ? ? ? blo ? ? 1b
> - ? ? ? adr ? ? r0, str_a3
> - ? ? ? bl ? ? ?printascii
> - ? ? ? b ? ? ? __error
> -ENDPROC(__error_a)
> -
> -str_a1: ? ? ? ?.asciz ?"\nError: unrecognized/unsupported machine ID (r1 = 0x"
> -str_a2: ? ? ? ?.asciz ?").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
> -str_a3: ? ? ? ?.asciz ?"\nPlease check your kernel config and/or bootloader.\n"
> - ? ? ? .align
> -#endif
> -
> -/*
> - * Lookup machine architecture in the linker-build list of architectures.
> - * Note that we can't use the absolute addresses for the __arch_info
> - * lists since we aren't running with the MMU on (and therefore, we are
> - * not in the correct address space). ?We have to calculate the offset.
> - *
> - * ?r1 = machine architecture number
> - * Returns:
> - * ?r3, r4, r6 corrupted
> - * ?r5 = mach_info pointer in physical address space
> - */
> -__lookup_machine_type:
> - ? ? ? adr ? ? r3, __lookup_machine_type_data
> - ? ? ? ldmia ? r3, {r4, r5, r6}
> - ? ? ? sub ? ? r3, r3, r4 ? ? ? ? ? ? ? ? ? ? ?@ get offset between virt&phys
> - ? ? ? add ? ? r5, r5, r3 ? ? ? ? ? ? ? ? ? ? ?@ convert virt addresses to
> - ? ? ? add ? ? r6, r6, r3 ? ? ? ? ? ? ? ? ? ? ?@ physical address space
> -1: ? ? ldr ? ? r3, [r5, #MACHINFO_TYPE] ? ? ? ?@ get machine type
> - ? ? ? teq ? ? r3, r1 ? ? ? ? ? ? ? ? ? ? ? ? ?@ matches loader number?
> - ? ? ? beq ? ? 2f ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@ found
> - ? ? ? add ? ? r5, r5, #SIZEOF_MACHINE_DESC ? ?@ next machine_desc
> - ? ? ? cmp ? ? r5, r6
> - ? ? ? blo ? ? 1b
> - ? ? ? mov ? ? r5, #0 ? ? ? ? ? ? ? ? ? ? ? ? ?@ unknown machine
> -2: ? ? mov ? ? pc, lr
> -ENDPROC(__lookup_machine_type)
> -
> -/*
> - * Look in arch/arm/kernel/arch.[ch] for information about the
> - * __arch_info structures.
> - */
> - ? ? ? .align ?2
> - ? ? ? .type ? __lookup_machine_type_data, %object
> -__lookup_machine_type_data:
> - ? ? ? .long ? .
> - ? ? ? .long ? __arch_info_begin
> - ? ? ? .long ? __arch_info_end
> - ? ? ? .size ? __lookup_machine_type_data, . - __lookup_machine_type_data
>
> ?/* Determine validity of the r2 atags pointer. ?The heuristic requires
> ?* that the pointer be aligned, in the first 16k of physical RAM and
> @@ -107,8 +32,6 @@ __lookup_machine_type_data:
> ?* of this function may be more lenient with the physical address and
> ?* may also be able to move the ATAGS block if necessary.
> ?*
> - * r8 ?= machinfo
> - *
> ?* Returns:
> ?* ?r2 either valid atags pointer, or zero
> ?* ?r5, r6 corrupted
> @@ -183,17 +106,6 @@ __mmap_switched_data:
> ? ? ? ?.size ? __mmap_switched_data, . - __mmap_switched_data
>
> ?/*
> - * This provides a C-API version of __lookup_machine_type
> - */
> -ENTRY(lookup_machine_type)
> - ? ? ? stmfd ? sp!, {r4 - r6, lr}
> - ? ? ? mov ? ? r1, r0
> - ? ? ? bl ? ? ?__lookup_machine_type
> - ? ? ? mov ? ? r0, r5
> - ? ? ? ldmfd ? sp!, {r4 - r6, pc}
> -ENDPROC(lookup_machine_type)
> -
> -/*
> ?* This provides a C-API version of __lookup_processor_type
> ?*/
> ?ENTRY(lookup_processor_type)
> diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S
> index 814ce1a..6b1e0ad 100644
> --- a/arch/arm/kernel/head-nommu.S
> +++ b/arch/arm/kernel/head-nommu.S
> @@ -44,9 +44,6 @@ ENTRY(stext)
> ? ? ? ?bl ? ? ?__lookup_processor_type ? ? ? ? @ r5=procinfo r9=cpuid
> ? ? ? ?movs ? ?r10, r5 ? ? ? ? ? ? ? ? ? ? ? ? @ invalid processor (r5=0)?
> ? ? ? ?beq ? ? __error_p ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @ yes, error 'p'
> - ? ? ? bl ? ? ?__lookup_machine_type ? ? ? ? ? @ r5=machinfo
> - ? ? ? movs ? ?r8, r5 ? ? ? ? ? ? ? ? ? ? ? ? ?@ invalid machine (r5=0)?
> - ? ? ? beq ? ? __error_a ? ? ? ? ? ? ? ? ? ? ? @ yes, error 'a'
>
> ? ? ? ?adr ? ? lr, BSYM(__after_proc_init) ? ? @ return (PIC) address
> ?ARM( ?add ? ? pc, r10, #PROCINFO_INITFUNC ? ? )
> diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
> index 06aed19..084db6c 100644
> --- a/arch/arm/kernel/head.S
> +++ b/arch/arm/kernel/head.S
> @@ -87,14 +87,10 @@ ENTRY(stext)
> ? ? ? ?movs ? ?r10, r5 ? ? ? ? ? ? ? ? ? ? ? ? @ invalid processor (r5=0)?
> ?THUMB( it ? ? eq ) ? ? ? ? ? ?@ force fixup-able long branch encoding
> ? ? ? ?beq ? ? __error_p ? ? ? ? ? ? ? ? ? ? ? @ yes, error 'p'
> - ? ? ? bl ? ? ?__lookup_machine_type ? ? ? ? ? @ r5=machinfo
> - ? ? ? movs ? ?r8, r5 ? ? ? ? ? ? ? ? ? ? ? ? ?@ invalid machine (r5=0)?
> - THUMB( it ? ? eq ) ? ? ? ? ? ?@ force fixup-able long branch encoding
> - ? ? ? beq ? ? __error_a ? ? ? ? ? ? ? ? ? ? ? @ yes, error 'a'
>
> ? ? ? ?/*
> ? ? ? ? * r1 = machine no, r2 = atags,
> - ? ? ? ?* r8 = machinfo, r9 = cpuid, r10 = procinfo
> + ? ? ? ?* r9 = cpuid, r10 = procinfo
> ? ? ? ? */
> ? ? ? ?bl ? ? ?__vet_atags
> ?#ifdef CONFIG_SMP_ON_UP
> @@ -108,7 +104,7 @@ ENTRY(stext)
> ? ? ? ?/*
> ? ? ? ? * The following calls CPU specific code in a position independent
> ? ? ? ? * manner. ?See arch/arm/mm/proc-*.S for details. ?r10 = base of
> - ? ? ? ?* xxx_proc_info structure selected by __lookup_machine_type
> + ? ? ? ?* xxx_proc_info structure selected by __lookup_processor_type
> ? ? ? ? * above. ?On return, the CPU will be ready for the MMU to be
> ? ? ? ? * turned on, and r0 will hold the CPU control register value.
> ? ? ? ? */
> @@ -127,7 +123,6 @@ ENDPROC(stext)
> ?* amount which are required to get the kernel running, which
> ?* generally means mapping in the kernel code.
> ?*
> - * r8 ?= machinfo
> ?* r9 ?= cpuid
> ?* r10 = procinfo
> ?*
> diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
> index 7c5499d..eb952a7 100644
> --- a/arch/arm/kernel/setup.c
> +++ b/arch/arm/kernel/setup.c
> @@ -308,7 +308,44 @@ static void __init cacheid_init(void)
> ?* already provide the required functionality.
> ?*/
> ?extern struct proc_info_list *lookup_processor_type(unsigned int);
> -extern struct machine_desc *lookup_machine_type(unsigned int);
> +
> +static void __init early_print(const char *str, ...)
> +{
> + ? ? ? extern void printascii(const char *);
> + ? ? ? char buf[256];
> + ? ? ? va_list ap;
> +
> + ? ? ? va_start(ap, str);
> + ? ? ? vsnprintf(buf, sizeof(buf), str, ap);
> + ? ? ? va_end(ap);
> +
> +#ifdef CONFIG_DEBUG_LL
> + ? ? ? printascii(buf);
> +#endif
> + ? ? ? printk("%s", buf);
> +}
> +
> +static struct machine_desc * __init lookup_machine_type(unsigned int type)
> +{
> + ? ? ? extern struct machine_desc __arch_info_begin[], __arch_info_end[];
> + ? ? ? struct machine_desc *p;
> +
> + ? ? ? for (p = __arch_info_begin; p < __arch_info_end; p++)
> + ? ? ? ? ? ? ? if (type == p->nr)
> + ? ? ? ? ? ? ? ? ? ? ? return p;
> +
> + ? ? ? early_print("\n"
> + ? ? ? ? ? ? ? "Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n"
> + ? ? ? ? ? ? ? "Available machine support:\n\nID (hex)\tNAME\n", type);
> +
> + ? ? ? for (p = __arch_info_begin; p < __arch_info_end; p++)
> + ? ? ? ? ? ? ? early_print("%08x\t%s\n", p->nr, p->name);
> +
> + ? ? ? early_print("\nPlease check your kernel config and/or bootloader.\n");
> +
> + ? ? ? while (true)
> + ? ? ? ? ? ? ? /* can't use cpu_relax() here as it may require MMU setup */;
> +}
>
> ?static void __init feat_v6_fixup(void)
> ?{
>
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH v2] arm: Defer vetting of atags to setup.c
From: Grant Likely @ 2011-01-12 18:03 UTC (permalink / raw)
To: linux-arm-kernel
Since the debug macros no longer depend on atag data, the vetting can
be deferred to setup_arch() in setup.c which simplifies the code
somewhat.
This patch removes __vet_atags() from head.S and moves it setup_arch().
I've tried to preserve the existing behaviour in this patch so the
extra atags vetting is only using when CONFIG_MMU is selected.
v2: Move removal of __machine_type_lookup to a separate patch.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
arch/arm/kernel/head-common.S | 51 +++++++----------------------------------
arch/arm/kernel/head.S | 1 -
arch/arm/kernel/setup.c | 16 +++++++++++++
3 files changed, 25 insertions(+), 43 deletions(-)
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index c84b57d..8bb1829 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -11,50 +11,8 @@
*
*/
-#define ATAG_CORE 0x54410001
-#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
-#define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
-
-/*
- * Exception handling. Something went wrong and we can't proceed. We
- * ought to tell the user, but since we don't have any guarantee that
- * we're even running on the right architecture, we do virtually nothing.
- *
- * If CONFIG_DEBUG_LL is set we try to print out something about the error
- * and hope for the best (useful if bootloader fails to pass a proper
- * machine ID for example).
- */
__HEAD
-/* Determine validity of the r2 atags pointer. The heuristic requires
- * that the pointer be aligned, in the first 16k of physical RAM and
- * that the ATAG_CORE marker is first and present. Future revisions
- * of this function may be more lenient with the physical address and
- * may also be able to move the ATAGS block if necessary.
- *
- * Returns:
- * r2 either valid atags pointer, or zero
- * r5, r6 corrupted
- */
-__vet_atags:
- tst r2, #0x3 @ aligned?
- bne 1f
-
- ldr r5, [r2, #0] @ is first tag ATAG_CORE?
- cmp r5, #ATAG_CORE_SIZE
- cmpne r5, #ATAG_CORE_SIZE_EMPTY
- bne 1f
- ldr r5, [r2, #4]
- ldr r6, =ATAG_CORE
- cmp r5, r6
- bne 1f
-
- mov pc, lr @ atag pointer is ok
-
-1: mov r2, #0
- mov pc, lr
-ENDPROC(__vet_atags)
-
/*
* The following fragment of code is executed with the MMU on in MMU mode,
* and uses absolute addresses; this is not position independent.
@@ -158,6 +116,15 @@ __lookup_processor_type_data:
.long __proc_info_end
.size __lookup_processor_type_data, . - __lookup_processor_type_data
+/*
+ * Exception handling. Something went wrong and we can't proceed. We
+ * ought to tell the user, but since we don't have any guarantee that
+ * we're even running on the right architecture, we do virtually nothing.
+ *
+ * If CONFIG_DEBUG_LL is set we try to print out something about the error
+ * and hope for the best (useful if bootloader fails to pass a proper
+ * machine ID for example).
+ */
__error_p:
#ifdef CONFIG_DEBUG_LL
adr r0, str_p1
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 19f3909..4b0cf4a 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -92,7 +92,6 @@ ENTRY(stext)
* r1 = machine no, r2 = atags,
* r9 = cpuid, r10 = procinfo
*/
- bl __vet_atags
#ifdef CONFIG_SMP_ON_UP
bl __fixup_smp
#endif
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 77266a8..47cf110 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -851,6 +851,22 @@ void __init setup_arch(char **cmdline_p)
if (mdesc->soft_reboot)
reboot_setup("s");
+#if defined(CONFIG_MMU)
+ /*
+ * Determine validity of the atags pointer. The heuristic requires
+ * that the pointer be aligned, and that the ATAG_CORE marker is
+ * first and present.
+ */
+ if (__atags_pointer & 0x3)
+ __atags_pointer = 0;
+ if (__atags_pointer) {
+ struct tag *t = phys_to_virt(__atags_pointer);
+ if ((t->hdr.size != tag_size(tag_core)) &&
+ (t->hdr.size != sizeof(struct tag_header)) &&
+ (t->hdr.tag != ATAG_CORE))
+ __atags_pointer = 0;
+ }
+#endif
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
else if (mdesc->boot_params)
^ permalink raw reply related
* [PATCH 0/5] mmc: add double buffering for mmc block requests
From: Per Forlin @ 2011-01-12 18:13 UTC (permalink / raw)
To: linux-arm-kernel
Add support to prepare one MMC request while another is active on
the host. This is done by making the issue_rw_rq() asynchronous.
The increase in throughput is proportional to the time it takes to
prepare a request and how fast the memory is. The faster the MMC/SD is
the more significant the prepare request time becomes. Measurements on U5500
and U8500 on eMMC shows significant performance gain for DMA on MMC for large
reads. In the PIO case there is some gain in performance for large reads too.
There seems to be no or small performance gain for write, don't have a good
explanation for this yet.
There are two optional hooks pre_req() and post_req() that the host driver
may implement in order to improve double buffering. In the DMA case pre_req()
may do dma_map_sg() and prepare the dma descriptor and post_req runs the
dma_unmap_sg.
The mmci host driver implementation for double buffering is not intended
nor ready for mainline yet. It is only an example of how to implement
pre_req() and post_req(). The reason for this is that the basic DMA support
for MMCI is not complete yet. The mmci patches are sent in a separate patch
series "[FYI 0/4] arm: mmci: example implementation of double buffering".
Issues/Questions for issue_rw_rq() in block.c:
* Is it safe to claim the host for the first MMC request and wait to release
it until the MMC queue is empty again? Or must the host be claimed and
released for every request?
* Is it possible to predict the result from __blk_end_request().
If there are no errors for a completed MMC request and the
blk_rq_bytes(req) == data.bytes_xfered, will it be guaranteed that
__blk_end_request will return 0?
Here follows the IOZone results for u8500 v1.1 on eMMC.
The numbers for DMA are a bit to good here due to the fact that the
CPU speed is decreased compared to u8500 v2. This makes the cache handling
even more significant.
Command line used: ./iozone -az -i0 -i1 -i2 -s 50m -I -f /iozone.tmp -e -R -+u
Relative diff: VANILLA-MMC-PIO -> 2BUF-MMC-PIO
cpu load is abs diff
random random
KB reclen write rewrite read reread read write
51200 4 +0% +0% +0% +0% +0% +0%
cpu: +0.1 -0.1 -0.5 -0.3 -0.1 -0.0
51200 8 +0% +0% +6% +6% +8% +0%
cpu: +0.1 -0.1 -0.3 -0.4 -0.8 +0.0
51200 16 +0% -2% +0% +0% -3% +0%
cpu: +0.0 -0.2 +0.0 +0.0 -0.2 +0.0
51200 32 +0% +1% +0% +0% +0% +0%
cpu: +0.1 +0.0 -0.3 +0.0 +0.0 +0.0
51200 64 +0% +0% +0% +0% +0% +0%
cpu: +0.1 +0.0 +0.0 +0.0 +0.0 +0.0
51200 128 +0% +1% +1% +1% +1% +0%
cpu: +0.0 +0.2 +0.1 -0.3 +0.4 +0.0
51200 256 +0% +0% +1% +1% +1% +0%
cpu: +0.0 -0.0 +0.1 +0.1 +0.1 +0.0
51200 512 +0% +1% +2% +2% +2% +0%
cpu: +0.1 +0.0 +0.2 +0.2 +0.2 +0.1
51200 1024 +0% +2% +2% +2% +3% +0%
cpu: +0.2 +0.1 +0.2 +0.5 -0.8 +0.0
51200 2048 +0% +2% +3% +3% +3% +0%
cpu: +0.0 -0.2 +0.4 +0.8 -0.5 +0.2
51200 4096 +0% +1% +3% +3% +3% +1%
cpu: +0.2 +0.1 +0.9 +0.9 +0.5 +0.1
51200 8192 +1% +0% +3% +3% +3% +1%
cpu: +0.2 +0.2 +1.3 +1.3 +1.0 +0.0
51200 16384 +0% +1% +3% +3% +3% +1%
cpu: +0.2 +0.1 +1.0 +1.3 +1.0 +0.5
Relative diff: VANILLA-MMC-DMA -> 2BUF-MMC-MMCI-DMA
cpu load is abs diff
random random
KB reclen write rewrite read reread read write
51200 4 +0% -3% +6% +5% +5% +0%
cpu: +0.0 -0.2 -0.6 -0.1 +0.3 +0.0
51200 8 +0% +0% +7% +7% +7% +0%
cpu: +0.0 +0.1 +0.8 +0.6 +0.9 +0.0
51200 16 +0% +0% +7% +7% +8% +0%
cpu: +0.0 -0.0 +0.7 +0.7 +0.8 +0.0
51200 32 +0% +0% +8% +8% +9% +0%
cpu: +0.0 +0.1 +0.7 +0.7 +0.3 +0.0
51200 64 +0% +1% +9% +9% +9% +0%
cpu: +0.0 +0.0 +0.8 +0.7 +0.8 +0.0
51200 128 +1% +0% +13% +13% +14% +0%
cpu: +0.2 +0.0 +1.0 +1.0 +1.1 +0.0
51200 256 +1% +2% +8% +8% +11% +0%
cpu: +0.0 +0.3 +0.0 +0.7 +1.5 +0.0
51200 512 +1% +2% +16% +16% +17% +0%
cpu: +0.2 +0.2 +2.2 +2.1 +2.2 +0.1
51200 1024 +1% +2% +20% +20% +20% +1%
cpu: +0.2 +0.1 +2.6 +1.9 +2.6 +0.0
51200 2048 +0% +2% +22% +22% +21% +0%
cpu: +0.0 +0.3 +2.3 +2.9 +2.1 -0.0
51200 4096 +1% +2% +23% +23% +23% +1%
cpu: +0.2 +0.1 +2.0 +3.2 +3.1 +0.0
51200 8192 +1% +5% +24% +24% +24% +1%
cpu: +1.4 -0.0 +4.2 +3.0 +2.8 +0.1
51200 16384 +1% +3% +24% +24% +24% +2%
cpu: +0.0 +0.3 +3.4 +3.8 +3.7 +0.1
Here follows the IOZone results for u5500 on eMMC.
These numbers for DMA are more as expected.
Command line used: ./iozone -az -i0 -i1 -i2 -s 50m -I -f /iozone.tmp -e -R -+u
Relative diff: VANILLA-MMC-DMA -> 2BUF-MMC-MMCI-DMA
cpu load is abs diff
random random
KB reclen write rewrite read reread read write
51200 128 +1% +1% +10% +9% +10% +0%
cpu: +0.1 +0.0 +1.3 +0.1 +0.8 +0.1
51200 256 +2% +2% +7% +7% +9% +0%
cpu: +0.1 +0.4 +0.5 +0.6 +0.7 +0.0
51200 512 +2% +2% +12% +12% +12% +1%
cpu: +0.4 +0.6 +1.8 +2.4 +2.4 +0.2
51200 1024 +2% +3% +14% +14% +14% +0%
cpu: +0.3 +0.1 +2.1 +1.4 +1.4 +0.2
51200 2048 +3% +3% +16% +16% +16% +1%
cpu: +0.2 +0.2 +2.5 +1.8 +2.4 -0.2
51200 4096 +3% +3% +17% +17% +18% +3%
cpu: +0.1 -0.1 +2.7 +2.0 +2.7 -0.1
51200 8192 +3% +3% +18% +18% +18% +3%
cpu: -0.1 +0.2 +3.0 +2.3 +2.2 +0.2
51200 16384 +3% +3% +18% +18% +18% +4%
cpu: +0.2 +0.2 +2.8 +3.5 +2.4 -0.0
Per Forlin (5):
mmc: add member in mmc queue struct to hold request data
mmc: Add a block request prepare function
mmc: Add a second mmc queue request member
mmc: Store the mmc block request struct in mmc queue
mmc: Add double buffering for mmc block requests
drivers/mmc/card/block.c | 337 ++++++++++++++++++++++++++++++----------------
drivers/mmc/card/queue.c | 171 +++++++++++++++---------
drivers/mmc/card/queue.h | 31 +++-
drivers/mmc/core/core.c | 77 +++++++++--
include/linux/mmc/core.h | 7 +-
include/linux/mmc/host.h | 8 +
6 files changed, 432 insertions(+), 199 deletions(-)
^ permalink raw reply
* [PATCH 1/5] mmc: add member in mmc queue struct to hold request data
From: Per Forlin @ 2011-01-12 18:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294856043-13447-1-git-send-email-per.forlin@linaro.org>
The way the request data is organized in the mmc queue struct
it only allows processing of one request at the time.
This patch adds a new struct to hold mmc queue request data such as
sg list, request and bounce buffers, and update functions depending on
the mmc queue struct. This lies the ground for
using multiple active request for one mmc queue.
Signed-off-by: Per Forlin <per.forlin@linaro.org>
---
drivers/mmc/card/block.c | 8 ++--
drivers/mmc/card/queue.c | 129 ++++++++++++++++++++++++----------------------
drivers/mmc/card/queue.h | 22 +++++---
3 files changed, 85 insertions(+), 74 deletions(-)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 217f820..be51bde 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -398,8 +398,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
mmc_set_data_timeout(&brq.data, card);
- brq.data.sg = mq->sg;
- brq.data.sg_len = mmc_queue_map_sg(mq);
+ brq.data.sg = mq->mqrq_cur->sg;
+ brq.data.sg_len = mmc_queue_map_sg(mq, mq->mqrq_cur);
/*
* Adjust the sg list so it is the same size as the
@@ -420,11 +420,11 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
brq.data.sg_len = i;
}
- mmc_queue_bounce_pre(mq);
+ mmc_queue_bounce_pre(mq->mqrq_cur);
mmc_wait_for_req(card->host, &brq.mrq);
- mmc_queue_bounce_post(mq);
+ mmc_queue_bounce_post(mq->mqrq_cur);
/*
* Check for errors here, but don't jump to cmd_err
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 4e42d03..8a8d88b 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -57,7 +57,7 @@ static int mmc_queue_thread(void *d)
set_current_state(TASK_INTERRUPTIBLE);
if (!blk_queue_plugged(q))
req = blk_fetch_request(q);
- mq->req = req;
+ mq->mqrq_cur->req = req;
spin_unlock_irq(q->queue_lock);
if (!req) {
@@ -98,10 +98,25 @@ static void mmc_request(struct request_queue *q)
return;
}
- if (!mq->req)
+ if (!mq->mqrq_cur->req)
wake_up_process(mq->thread);
}
+struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
+{
+ struct scatterlist *sg;
+
+ sg = kmalloc(sizeof(struct scatterlist)*sg_len, GFP_KERNEL);
+ if (!sg)
+ *err = -ENOMEM;
+ else {
+ *err = 0;
+ sg_init_table(sg, sg_len);
+ }
+
+ return sg;
+}
+
/**
* mmc_init_queue - initialise a queue structure.
* @mq: mmc queue
@@ -115,6 +130,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
struct mmc_host *host = card->host;
u64 limit = BLK_BOUNCE_HIGH;
int ret;
+ struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
limit = *mmc_dev(host)->dma_mask;
@@ -124,8 +140,9 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
if (!mq->queue)
return -ENOMEM;
+ memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
+ mq->mqrq_cur = mqrq_cur;
mq->queue->queuedata = mq;
- mq->req = NULL;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
@@ -159,53 +176,44 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
bouncesz = host->max_blk_count * 512;
if (bouncesz > 512) {
- mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
- if (!mq->bounce_buf) {
+ mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
+ if (!mqrq_cur->bounce_buf) {
printk(KERN_WARNING "%s: unable to "
- "allocate bounce buffer\n",
+ "allocate bounce cur buffer\n",
mmc_card_name(card));
}
}
- if (mq->bounce_buf) {
+ if (mqrq_cur->bounce_buf) {
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
blk_queue_max_hw_sectors(mq->queue, bouncesz / 512);
blk_queue_max_segments(mq->queue, bouncesz / 512);
blk_queue_max_segment_size(mq->queue, bouncesz);
- mq->sg = kmalloc(sizeof(struct scatterlist),
- GFP_KERNEL);
- if (!mq->sg) {
- ret = -ENOMEM;
+ mqrq_cur->sg = mmc_alloc_sg(1, &ret);
+ if (ret)
goto cleanup_queue;
- }
- sg_init_table(mq->sg, 1);
- mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
- bouncesz / 512, GFP_KERNEL);
- if (!mq->bounce_sg) {
- ret = -ENOMEM;
+ mqrq_cur->bounce_sg =
+ mmc_alloc_sg(bouncesz / 512, &ret);
+ if (ret)
goto cleanup_queue;
- }
- sg_init_table(mq->bounce_sg, bouncesz / 512);
+
}
}
#endif
- if (!mq->bounce_buf) {
+ if (!mqrq_cur->bounce_buf) {
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
blk_queue_max_segments(mq->queue, host->max_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
- mq->sg = kmalloc(sizeof(struct scatterlist) *
- host->max_segs, GFP_KERNEL);
- if (!mq->sg) {
- ret = -ENOMEM;
+ mqrq_cur->sg = mmc_alloc_sg(host->max_segs, &ret);
+ if (ret)
goto cleanup_queue;
- }
- sg_init_table(mq->sg, host->max_segs);
+
}
sema_init(&mq->thread_sem, 1);
@@ -220,16 +228,15 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
return 0;
free_bounce_sg:
- if (mq->bounce_sg)
- kfree(mq->bounce_sg);
- mq->bounce_sg = NULL;
+ kfree(mqrq_cur->bounce_sg);
+ mqrq_cur->bounce_sg = NULL;
+
cleanup_queue:
- if (mq->sg)
- kfree(mq->sg);
- mq->sg = NULL;
- if (mq->bounce_buf)
- kfree(mq->bounce_buf);
- mq->bounce_buf = NULL;
+ kfree(mqrq_cur->sg);
+ mqrq_cur->sg = NULL;
+ kfree(mqrq_cur->bounce_buf);
+ mqrq_cur->bounce_buf = NULL;
+
blk_cleanup_queue(mq->queue);
return ret;
}
@@ -238,6 +245,7 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
{
struct request_queue *q = mq->queue;
unsigned long flags;
+ struct mmc_queue_req *mqrq_cur = mq->mqrq_cur;
/* Make sure the queue isn't suspended, as that will deadlock */
mmc_queue_resume(mq);
@@ -251,16 +259,14 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
blk_start_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
- if (mq->bounce_sg)
- kfree(mq->bounce_sg);
- mq->bounce_sg = NULL;
+ kfree(mqrq_cur->bounce_sg);
+ mqrq_cur->bounce_sg = NULL;
- kfree(mq->sg);
- mq->sg = NULL;
+ kfree(mqrq_cur->sg);
+ mqrq_cur->sg = NULL;
- if (mq->bounce_buf)
- kfree(mq->bounce_buf);
- mq->bounce_buf = NULL;
+ kfree(mqrq_cur->bounce_buf);
+ mqrq_cur->bounce_buf = NULL;
mq->card = NULL;
}
@@ -313,27 +319,27 @@ void mmc_queue_resume(struct mmc_queue *mq)
/*
* Prepare the sg list(s) to be handed of to the host driver
*/
-unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
+unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq)
{
unsigned int sg_len;
size_t buflen;
struct scatterlist *sg;
int i;
- if (!mq->bounce_buf)
- return blk_rq_map_sg(mq->queue, mq->req, mq->sg);
+ if (!mqrq->bounce_buf)
+ return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
- BUG_ON(!mq->bounce_sg);
+ BUG_ON(!mqrq->bounce_sg);
- sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg);
+ sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
- mq->bounce_sg_len = sg_len;
+ mqrq->bounce_sg_len = sg_len;
buflen = 0;
- for_each_sg(mq->bounce_sg, sg, sg_len, i)
+ for_each_sg(mqrq->bounce_sg, sg, sg_len, i)
buflen += sg->length;
- sg_init_one(mq->sg, mq->bounce_buf, buflen);
+ sg_init_one(mqrq->sg, mqrq->bounce_buf, buflen);
return 1;
}
@@ -342,19 +348,19 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
* If writing, bounce the data to the buffer before the request
* is sent to the host driver
*/
-void mmc_queue_bounce_pre(struct mmc_queue *mq)
+void mmc_queue_bounce_pre(struct mmc_queue_req *mqrq)
{
unsigned long flags;
- if (!mq->bounce_buf)
+ if (!mqrq->bounce_buf)
return;
- if (rq_data_dir(mq->req) != WRITE)
+ if (rq_data_dir(mqrq->req) != WRITE)
return;
local_irq_save(flags);
- sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len,
- mq->bounce_buf, mq->sg[0].length);
+ sg_copy_to_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len,
+ mqrq->bounce_buf, mqrq->sg[0].length);
local_irq_restore(flags);
}
@@ -362,19 +368,18 @@ void mmc_queue_bounce_pre(struct mmc_queue *mq)
* If reading, bounce the data from the buffer after the request
* has been handled by the host driver
*/
-void mmc_queue_bounce_post(struct mmc_queue *mq)
+void mmc_queue_bounce_post(struct mmc_queue_req *mqrq)
{
unsigned long flags;
- if (!mq->bounce_buf)
+ if (!mqrq->bounce_buf)
return;
- if (rq_data_dir(mq->req) != READ)
+ if (rq_data_dir(mqrq->req) != READ)
return;
local_irq_save(flags);
- sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len,
- mq->bounce_buf, mq->sg[0].length);
+ sg_copy_from_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len,
+ mqrq->bounce_buf, mqrq->sg[0].length);
local_irq_restore(flags);
}
-
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 64e66e0..96c440d 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -4,19 +4,24 @@
struct request;
struct task_struct;
+struct mmc_queue_req {
+ struct request *req;
+ struct scatterlist *sg;
+ char *bounce_buf;
+ struct scatterlist *bounce_sg;
+ unsigned int bounce_sg_len;
+};
+
struct mmc_queue {
struct mmc_card *card;
struct task_struct *thread;
struct semaphore thread_sem;
unsigned int flags;
- struct request *req;
int (*issue_fn)(struct mmc_queue *, struct request *);
void *data;
struct request_queue *queue;
- struct scatterlist *sg;
- char *bounce_buf;
- struct scatterlist *bounce_sg;
- unsigned int bounce_sg_len;
+ struct mmc_queue_req mqrq[1];
+ struct mmc_queue_req *mqrq_cur;
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
@@ -24,8 +29,9 @@ extern void mmc_cleanup_queue(struct mmc_queue *);
extern void mmc_queue_suspend(struct mmc_queue *);
extern void mmc_queue_resume(struct mmc_queue *);
-extern unsigned int mmc_queue_map_sg(struct mmc_queue *);
-extern void mmc_queue_bounce_pre(struct mmc_queue *);
-extern void mmc_queue_bounce_post(struct mmc_queue *);
+extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
+ struct mmc_queue_req *);
+extern void mmc_queue_bounce_pre(struct mmc_queue_req *);
+extern void mmc_queue_bounce_post(struct mmc_queue_req *);
#endif
--
1.7.1
^ permalink raw reply related
* [PATCH 2/5] mmc: Add a block request prepare function
From: Per Forlin @ 2011-01-12 18:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294856043-13447-1-git-send-email-per.forlin@linaro.org>
Break out code from mmc_blk_issue_rw_rq to create a
block request prepare function. This doesn't change
any functionallity.
Signed-off-by: Per Forlin <per.forlin@linaro.org>
---
drivers/mmc/card/block.c | 173 +++++++++++++++++++++++++---------------------
1 files changed, 94 insertions(+), 79 deletions(-)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index be51bde..3f98b15 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -331,97 +331,112 @@ out:
return err ? 0 : 1;
}
-static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
+static void mmc_blk_issue_rw_rq_prep(struct mmc_blk_request *brq,
+ struct mmc_queue_req *mqrq,
+ struct request *req,
+ struct mmc_card *card,
+ int disable_multi,
+ struct mmc_queue *mq)
{
- struct mmc_blk_data *md = mq->data;
- struct mmc_card *card = md->queue.card;
- struct mmc_blk_request brq;
- int ret = 1, disable_multi = 0;
+ u32 readcmd, writecmd;
- mmc_claim_host(card->host);
- do {
- struct mmc_command cmd;
- u32 readcmd, writecmd, status = 0;
-
- memset(&brq, 0, sizeof(struct mmc_blk_request));
- brq.mrq.cmd = &brq.cmd;
- brq.mrq.data = &brq.data;
-
- brq.cmd.arg = blk_rq_pos(req);
- if (!mmc_card_blockaddr(card))
- brq.cmd.arg <<= 9;
- brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
- brq.data.blksz = 512;
- brq.stop.opcode = MMC_STOP_TRANSMISSION;
- brq.stop.arg = 0;
- brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- brq.data.blocks = blk_rq_sectors(req);
+ memset(brq, 0, sizeof(struct mmc_blk_request));
- /*
- * The block layer doesn't support all sector count
- * restrictions, so we need to be prepared for too big
- * requests.
- */
- if (brq.data.blocks > card->host->max_blk_count)
- brq.data.blocks = card->host->max_blk_count;
+ brq->mrq.cmd = &brq->cmd;
+ brq->mrq.data = &brq->data;
- /*
- * After a read error, we redo the request one sector at a time
- * in order to accurately determine which sectors can be read
- * successfully.
- */
- if (disable_multi && brq.data.blocks > 1)
- brq.data.blocks = 1;
-
- if (brq.data.blocks > 1) {
- /* SPI multiblock writes terminate using a special
- * token, not a STOP_TRANSMISSION request.
- */
- if (!mmc_host_is_spi(card->host)
- || rq_data_dir(req) == READ)
- brq.mrq.stop = &brq.stop;
- readcmd = MMC_READ_MULTIPLE_BLOCK;
- writecmd = MMC_WRITE_MULTIPLE_BLOCK;
- } else {
- brq.mrq.stop = NULL;
- readcmd = MMC_READ_SINGLE_BLOCK;
- writecmd = MMC_WRITE_BLOCK;
- }
- if (rq_data_dir(req) == READ) {
- brq.cmd.opcode = readcmd;
- brq.data.flags |= MMC_DATA_READ;
- } else {
- brq.cmd.opcode = writecmd;
- brq.data.flags |= MMC_DATA_WRITE;
- }
+ brq->cmd.arg = blk_rq_pos(req);
+ if (!mmc_card_blockaddr(card))
+ brq->cmd.arg <<= 9;
+ brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ brq->data.blksz = 512;
+ brq->stop.opcode = MMC_STOP_TRANSMISSION;
+ brq->stop.arg = 0;
+ brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ brq->data.blocks = blk_rq_sectors(req);
- mmc_set_data_timeout(&brq.data, card);
+ /*
+ * The block layer doesn't support all sector count
+ * restrictions, so we need to be prepared for too big
+ * requests.
+ */
+ if (brq->data.blocks > card->host->max_blk_count)
+ brq->data.blocks = card->host->max_blk_count;
- brq.data.sg = mq->mqrq_cur->sg;
- brq.data.sg_len = mmc_queue_map_sg(mq, mq->mqrq_cur);
+ /*
+ * After a read error, we redo the request one sector at a time
+ * in order to accurately determine which sectors can be read
+ * successfully.
+ */
+ if (disable_multi && brq->data.blocks > 1)
+ brq->data.blocks = 1;
- /*
- * Adjust the sg list so it is the same size as the
- * request.
+
+ if (brq->data.blocks > 1) {
+ /* SPI multiblock writes terminate using a special
+ * token, not a STOP_TRANSMISSION request.
*/
- if (brq.data.blocks != blk_rq_sectors(req)) {
- int i, data_size = brq.data.blocks << 9;
- struct scatterlist *sg;
-
- for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {
- data_size -= sg->length;
- if (data_size <= 0) {
- sg->length += data_size;
- i++;
- break;
- }
+ if (!mmc_host_is_spi(card->host)
+ || rq_data_dir(req) == READ)
+ brq->mrq.stop = &brq->stop;
+ readcmd = MMC_READ_MULTIPLE_BLOCK;
+ writecmd = MMC_WRITE_MULTIPLE_BLOCK;
+ } else {
+ brq->mrq.stop = NULL;
+ readcmd = MMC_READ_SINGLE_BLOCK;
+ writecmd = MMC_WRITE_BLOCK;
+ }
+ if (rq_data_dir(req) == READ) {
+ brq->cmd.opcode = readcmd;
+ brq->data.flags |= MMC_DATA_READ;
+ } else {
+ brq->cmd.opcode = writecmd;
+ brq->data.flags |= MMC_DATA_WRITE;
+ }
+
+ mmc_set_data_timeout(&brq->data, card);
+
+ brq->data.sg = mqrq->sg;
+ brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
+
+ /*
+ * Adjust the sg list so it is the same size as the
+ * request.
+ */
+ if (brq->data.blocks != blk_rq_sectors(req)) {
+ int i, data_size = brq->data.blocks << 9;
+ struct scatterlist *sg;
+
+ for_each_sg(brq->data.sg, sg, brq->data.sg_len, i) {
+ data_size -= sg->length;
+ if (data_size <= 0) {
+ sg->length += data_size;
+ i++;
+ break;
}
- brq.data.sg_len = i;
+ brq->data.sg_len = i;
}
+ }
+
+ mmc_queue_bounce_pre(mqrq);
+}
- mmc_queue_bounce_pre(mq->mqrq_cur);
+static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ struct mmc_blk_request brq;
+ int ret = 1, disable_multi = 0;
+
+ mmc_claim_host(card->host);
+
+ do {
+ struct mmc_command cmd;
+ u32 status = 0;
+ mmc_blk_issue_rw_rq_prep(&brq, mq->mqrq_cur, req, card,
+ disable_multi, mq);
mmc_wait_for_req(card->host, &brq.mrq);
mmc_queue_bounce_post(mq->mqrq_cur);
--
1.7.1
^ permalink raw reply related
* [PATCH 3/5] mmc: Add a second mmc queue request member
From: Per Forlin @ 2011-01-12 18:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294856043-13447-1-git-send-email-per.forlin@linaro.org>
Add an additional mmc queue request instance to make way for
double buffering. One request may be active while the
other request is being prepared.
Signed-off-by: Per Forlin <per.forlin@linaro.org>
---
drivers/mmc/card/queue.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
drivers/mmc/card/queue.h | 4 +++-
2 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 8a8d88b..30d4707 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -131,6 +131,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
u64 limit = BLK_BOUNCE_HIGH;
int ret;
struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
+ struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
limit = *mmc_dev(host)->dma_mask;
@@ -141,7 +142,9 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
return -ENOMEM;
memset(&mq->mqrq_cur, 0, sizeof(mq->mqrq_cur));
+ memset(&mq->mqrq_prev, 0, sizeof(mq->mqrq_prev));
mq->mqrq_cur = mqrq_cur;
+ mq->mqrq_prev = mqrq_prev;
mq->queue->queuedata = mq;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
@@ -182,9 +185,17 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
"allocate bounce cur buffer\n",
mmc_card_name(card));
}
+ mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
+ if (!mqrq_prev->bounce_buf) {
+ printk(KERN_WARNING "%s: unable to "
+ "allocate bounce prev buffer\n",
+ mmc_card_name(card));
+ kfree(mqrq_cur->bounce_buf);
+ mqrq_cur->bounce_buf = NULL;
+ }
}
- if (mqrq_cur->bounce_buf) {
+ if (mqrq_cur->bounce_buf && mqrq_prev->bounce_buf) {
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
blk_queue_max_hw_sectors(mq->queue, bouncesz / 512);
blk_queue_max_segments(mq->queue, bouncesz / 512);
@@ -199,11 +210,19 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
if (ret)
goto cleanup_queue;
+ mqrq_prev->sg = mmc_alloc_sg(1, &ret);
+ if (ret)
+ goto cleanup_queue;
+
+ mqrq_prev->bounce_sg =
+ mmc_alloc_sg(bouncesz / 512, &ret);
+ if (ret)
+ goto cleanup_queue;
}
}
#endif
- if (!mqrq_cur->bounce_buf) {
+ if (!mqrq_cur->bounce_buf && !mqrq_prev->bounce_buf) {
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
@@ -214,6 +233,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
if (ret)
goto cleanup_queue;
+
+ mqrq_prev->sg = mmc_alloc_sg(host->max_segs, &ret);
+ if (ret)
+ goto cleanup_queue;
}
sema_init(&mq->thread_sem, 1);
@@ -230,6 +253,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
free_bounce_sg:
kfree(mqrq_cur->bounce_sg);
mqrq_cur->bounce_sg = NULL;
+ kfree(mqrq_prev->bounce_sg);
+ mqrq_prev->bounce_sg = NULL;
cleanup_queue:
kfree(mqrq_cur->sg);
@@ -237,6 +262,11 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
kfree(mqrq_cur->bounce_buf);
mqrq_cur->bounce_buf = NULL;
+ kfree(mqrq_prev->sg);
+ mqrq_prev->sg = NULL;
+ kfree(mqrq_prev->bounce_buf);
+ mqrq_prev->bounce_buf = NULL;
+
blk_cleanup_queue(mq->queue);
return ret;
}
@@ -246,6 +276,7 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
struct request_queue *q = mq->queue;
unsigned long flags;
struct mmc_queue_req *mqrq_cur = mq->mqrq_cur;
+ struct mmc_queue_req *mqrq_prev = mq->mqrq_prev;
/* Make sure the queue isn't suspended, as that will deadlock */
mmc_queue_resume(mq);
@@ -268,6 +299,15 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
kfree(mqrq_cur->bounce_buf);
mqrq_cur->bounce_buf = NULL;
+ kfree(mqrq_prev->bounce_sg);
+ mqrq_prev->bounce_sg = NULL;
+
+ kfree(mqrq_prev->sg);
+ mqrq_prev->sg = NULL;
+
+ kfree(mqrq_prev->bounce_buf);
+ mqrq_prev->bounce_buf = NULL;
+
mq->card = NULL;
}
EXPORT_SYMBOL(mmc_cleanup_queue);
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 96c440d..f65eb88 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -20,8 +20,10 @@ struct mmc_queue {
int (*issue_fn)(struct mmc_queue *, struct request *);
void *data;
struct request_queue *queue;
- struct mmc_queue_req mqrq[1];
+
+ struct mmc_queue_req mqrq[2];
struct mmc_queue_req *mqrq_cur;
+ struct mmc_queue_req *mqrq_prev;
};
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
--
1.7.1
^ permalink raw reply related
* [PATCH 4/5] mmc: Store the mmc block request struct in mmc queue
From: Per Forlin @ 2011-01-12 18:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294856043-13447-1-git-send-email-per.forlin@linaro.org>
Move the mmc block request to the mmc queue struct in
order to make way for processing two brqs simultanously.
Signed-off-by: Per Forlin <per.forlin@linaro.org>
---
drivers/mmc/card/block.c | 68 +++++++++++++++++++++-------------------------
drivers/mmc/card/queue.h | 9 +++++-
2 files changed, 39 insertions(+), 38 deletions(-)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 3f98b15..028b2b8 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -165,13 +165,6 @@ static const struct block_device_operations mmc_bdops = {
.owner = THIS_MODULE,
};
-struct mmc_blk_request {
- struct mmc_request mrq;
- struct mmc_command cmd;
- struct mmc_command stop;
- struct mmc_data data;
-};
-
static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
{
int err;
@@ -422,11 +415,11 @@ static void mmc_blk_issue_rw_rq_prep(struct mmc_blk_request *brq,
mmc_queue_bounce_pre(mqrq);
}
-static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
+static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
- struct mmc_blk_request brq;
+ struct mmc_blk_request *brqc = &mq->mqrq_cur->brq;
int ret = 1, disable_multi = 0;
mmc_claim_host(card->host);
@@ -435,9 +428,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
struct mmc_command cmd;
u32 status = 0;
- mmc_blk_issue_rw_rq_prep(&brq, mq->mqrq_cur, req, card,
+ mmc_blk_issue_rw_rq_prep(brqc, mq->mqrq_cur, rqc, card,
disable_multi, mq);
- mmc_wait_for_req(card->host, &brq.mrq);
+ mmc_wait_for_req(card->host, &brqc->mrq);
mmc_queue_bounce_post(mq->mqrq_cur);
@@ -446,43 +439,43 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
* until later as we need to wait for the card to leave
* programming mode even when things go wrong.
*/
- if (brq.cmd.error || brq.data.error || brq.stop.error) {
- if (brq.data.blocks > 1 && rq_data_dir(req) == READ) {
+ if (brqc->cmd.error || brqc->data.error || brqc->stop.error) {
+ if (brqc->data.blocks > 1 && rq_data_dir(rqc) == READ) {
/* Redo read one sector at a time */
printk(KERN_WARNING "%s: retrying using single "
- "block read\n", req->rq_disk->disk_name);
+ "block read\n", rqc->rq_disk->disk_name);
disable_multi = 1;
continue;
}
- status = get_card_status(card, req);
+ status = get_card_status(card, rqc);
}
- if (brq.cmd.error) {
+ if (brqc->cmd.error) {
printk(KERN_ERR "%s: error %d sending read/write "
"command, response %#x, card status %#x\n",
- req->rq_disk->disk_name, brq.cmd.error,
- brq.cmd.resp[0], status);
+ rqc->rq_disk->disk_name, brqc->cmd.error,
+ brqc->cmd.resp[0], status);
}
- if (brq.data.error) {
- if (brq.data.error == -ETIMEDOUT && brq.mrq.stop)
+ if (brqc->data.error) {
+ if (brqc->data.error == -ETIMEDOUT && brqc->mrq.stop)
/* 'Stop' response contains card status */
- status = brq.mrq.stop->resp[0];
+ status = brqc->mrq.stop->resp[0];
printk(KERN_ERR "%s: error %d transferring data,"
" sector %u, nr %u, card status %#x\n",
- req->rq_disk->disk_name, brq.data.error,
- (unsigned)blk_rq_pos(req),
- (unsigned)blk_rq_sectors(req), status);
+ rqc->rq_disk->disk_name, brqc->data.error,
+ (unsigned)blk_rq_pos(rqc),
+ (unsigned)blk_rq_sectors(rqc), status);
}
- if (brq.stop.error) {
+ if (brqc->stop.error) {
printk(KERN_ERR "%s: error %d sending stop command, "
"response %#x, card status %#x\n",
- req->rq_disk->disk_name, brq.stop.error,
- brq.stop.resp[0], status);
+ rqc->rq_disk->disk_name, brqc->stop.error,
+ brqc->stop.resp[0], status);
}
- if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
+ if (!mmc_host_is_spi(card->host) && rq_data_dir(rqc) != READ) {
do {
int err;
@@ -492,7 +485,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
err = mmc_wait_for_cmd(card->host, &cmd, 5);
if (err) {
printk(KERN_ERR "%s: error %d requesting status\n",
- req->rq_disk->disk_name, err);
+ rqc->rq_disk->disk_name, err);
goto cmd_err;
}
/*
@@ -506,21 +499,22 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
#if 0
if (cmd.resp[0] & ~0x00000900)
printk(KERN_ERR "%s: status = %08x\n",
- req->rq_disk->disk_name, cmd.resp[0]);
+ rqc->rq_disk->disk_name, cmd.resp[0]);
if (mmc_decode_status(cmd.resp))
goto cmd_err;
#endif
}
- if (brq.cmd.error || brq.stop.error || brq.data.error) {
- if (rq_data_dir(req) == READ) {
+ if (brqc->cmd.error || brqc->stop.error || brqc->data.error) {
+ if (rq_data_dir(rqc) == READ) {
/*
* After an error, we redo I/O one sector at a
* time, so we only reach here after trying to
* read a single sector.
*/
spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, -EIO, brq.data.blksz);
+ ret = __blk_end_request(rqc, -EIO,
+ brqc->data.blksz);
spin_unlock_irq(&md->lock);
continue;
}
@@ -531,7 +525,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
* A block was successfully transferred.
*/
spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
+ ret = __blk_end_request(rqc, 0, brqc->data.bytes_xfered);
spin_unlock_irq(&md->lock);
} while (ret);
@@ -554,12 +548,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, 0, blocks << 9);
+ ret = __blk_end_request(rqc, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
+ ret = __blk_end_request(rqc, 0, brqc->data.bytes_xfered);
spin_unlock_irq(&md->lock);
}
@@ -567,7 +561,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
spin_lock_irq(&md->lock);
while (ret)
- ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
+ ret = __blk_end_request(rqc, -EIO, blk_rq_cur_bytes(rqc));
spin_unlock_irq(&md->lock);
return 0;
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index f65eb88..bf3dee9 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -4,12 +4,20 @@
struct request;
struct task_struct;
+struct mmc_blk_request {
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_data data;
+};
+
struct mmc_queue_req {
struct request *req;
struct scatterlist *sg;
char *bounce_buf;
struct scatterlist *bounce_sg;
unsigned int bounce_sg_len;
+ struct mmc_blk_request brq;
};
struct mmc_queue {
@@ -20,7 +28,6 @@ struct mmc_queue {
int (*issue_fn)(struct mmc_queue *, struct request *);
void *data;
struct request_queue *queue;
-
struct mmc_queue_req mqrq[2];
struct mmc_queue_req *mqrq_cur;
struct mmc_queue_req *mqrq_prev;
--
1.7.1
^ permalink raw reply related
* [PATCH 5/5] mmc: Add double buffering for mmc block requests
From: Per Forlin @ 2011-01-12 18:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294856043-13447-1-git-send-email-per.forlin@linaro.org>
Change mmc_blk_issue_rw_rq() to become asynchronous.
The execution flow looks like this:
The mmc-queue calls issue_rw_rq(), which sends the request
to the host and returns back to the mmc-queue. The mmc-queue calls
isuue_rw_rq() again with a new request. This new request is prepared,
in isuue_rw_rq(), then it waits for the active request to complete before
pushing it to the host. When to mmc-queue is empty it will call
isuue_rw_rq() with req=NULL to finish off the active request
without starting a new request.
Signed-off-by: Per Forlin <per.forlin@linaro.org>
---
drivers/mmc/card/block.c | 170 +++++++++++++++++++++++++++++++++++----------
drivers/mmc/card/queue.c | 2 +-
drivers/mmc/core/core.c | 77 ++++++++++++++++++---
include/linux/mmc/core.h | 7 ++-
include/linux/mmc/host.h | 8 ++
5 files changed, 214 insertions(+), 50 deletions(-)
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 028b2b8..11e6e97 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -420,62 +420,98 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brqc = &mq->mqrq_cur->brq;
- int ret = 1, disable_multi = 0;
+ struct mmc_blk_request *brqp = &mq->mqrq_prev->brq;
+ struct mmc_queue_req *mqrqp = mq->mqrq_prev;
+ struct request *rqp = mqrqp->req;
+ int ret = 0;
+ int disable_multi = 0;
+ bool complete_transfer = true;
+
+ if (!rqc && !rqp) {
+ brqc->mrq.data = NULL;
+ brqp->mrq.data = NULL;
+ return 0;
+ }
- mmc_claim_host(card->host);
+ /*
+ * TODO: Find out if it is OK to only claim host for the first request.
+ * For the first request the previous request is NULL
+ */
+ if (!rqp && rqc)
+ mmc_claim_host(card->host);
+
+ if (rqc) {
+ /* Prepare a new request */
+ mmc_blk_issue_rw_rq_prep(brqc, mq->mqrq_cur,
+ rqc, card, 0, mq);
+ mmc_pre_req(card->host, &brqc->mrq, !rqp);
+ }
do {
struct mmc_command cmd;
u32 status = 0;
- mmc_blk_issue_rw_rq_prep(brqc, mq->mqrq_cur, rqc, card,
- disable_multi, mq);
- mmc_wait_for_req(card->host, &brqc->mrq);
-
- mmc_queue_bounce_post(mq->mqrq_cur);
+ /* In case of error redo prepare and resend */
+ if (ret) {
+ mmc_blk_issue_rw_rq_prep(brqp, mqrqp, rqp, card,
+ disable_multi, mq);
+ mmc_pre_req(card->host, &brqc->mrq, !rqp);
+ mmc_start_req(card->host, &brqp->mrq);
+ }
+ /*
+ * If there is an ongoing request, indicated by rqp, wait for
+ * it to finish before starting a new one.
+ */
+ if (rqp) {
+ mmc_wait_for_req_done(&brqp->mrq);
+ } else {
+ /* start a new asynchronous request */
+ mmc_start_req(card->host, &brqc->mrq);
+ goto out;
+ }
/*
* Check for errors here, but don't jump to cmd_err
* until later as we need to wait for the card to leave
* programming mode even when things go wrong.
*/
- if (brqc->cmd.error || brqc->data.error || brqc->stop.error) {
- if (brqc->data.blocks > 1 && rq_data_dir(rqc) == READ) {
+ if (brqp->cmd.error || brqp->data.error || brqp->stop.error) {
+ if (brqp->data.blocks > 1 && rq_data_dir(rqp) == READ) {
/* Redo read one sector at a time */
printk(KERN_WARNING "%s: retrying using single "
- "block read\n", rqc->rq_disk->disk_name);
+ "block read\n", rqp->rq_disk->disk_name);
disable_multi = 1;
continue;
}
- status = get_card_status(card, rqc);
+ status = get_card_status(card, rqp);
}
- if (brqc->cmd.error) {
+ if (brqp->cmd.error) {
printk(KERN_ERR "%s: error %d sending read/write "
"command, response %#x, card status %#x\n",
- rqc->rq_disk->disk_name, brqc->cmd.error,
- brqc->cmd.resp[0], status);
+ rqp->rq_disk->disk_name, brqp->cmd.error,
+ brqp->cmd.resp[0], status);
}
- if (brqc->data.error) {
- if (brqc->data.error == -ETIMEDOUT && brqc->mrq.stop)
+ if (brqp->data.error) {
+ if (brqp->data.error == -ETIMEDOUT && brqp->mrq.stop)
/* 'Stop' response contains card status */
- status = brqc->mrq.stop->resp[0];
+ status = brqp->mrq.stop->resp[0];
printk(KERN_ERR "%s: error %d transferring data,"
" sector %u, nr %u, card status %#x\n",
- rqc->rq_disk->disk_name, brqc->data.error,
- (unsigned)blk_rq_pos(rqc),
- (unsigned)blk_rq_sectors(rqc), status);
+ rqp->rq_disk->disk_name, brqp->data.error,
+ (unsigned)blk_rq_pos(rqp),
+ (unsigned)blk_rq_sectors(rqp), status);
}
- if (brqc->stop.error) {
+ if (brqp->stop.error) {
printk(KERN_ERR "%s: error %d sending stop command, "
"response %#x, card status %#x\n",
- rqc->rq_disk->disk_name, brqc->stop.error,
- brqc->stop.resp[0], status);
+ rqp->rq_disk->disk_name, brqp->stop.error,
+ brqp->stop.resp[0], status);
}
- if (!mmc_host_is_spi(card->host) && rq_data_dir(rqc) != READ) {
+ if (!mmc_host_is_spi(card->host) && rq_data_dir(rqp) != READ) {
do {
int err;
@@ -485,7 +521,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
err = mmc_wait_for_cmd(card->host, &cmd, 5);
if (err) {
printk(KERN_ERR "%s: error %d requesting status\n",
- rqc->rq_disk->disk_name, err);
+ rqp->rq_disk->disk_name, err);
goto cmd_err;
}
/*
@@ -499,22 +535,22 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
#if 0
if (cmd.resp[0] & ~0x00000900)
printk(KERN_ERR "%s: status = %08x\n",
- rqc->rq_disk->disk_name, cmd.resp[0]);
+ rqp->rq_disk->disk_name, cmd.resp[0]);
if (mmc_decode_status(cmd.resp))
goto cmd_err;
#endif
}
- if (brqc->cmd.error || brqc->stop.error || brqc->data.error) {
- if (rq_data_dir(rqc) == READ) {
+ if (brqp->cmd.error || brqp->stop.error || brqp->data.error) {
+ if (rq_data_dir(rqp) == READ) {
/*
* After an error, we redo I/O one sector at a
* time, so we only reach here after trying to
* read a single sector.
*/
spin_lock_irq(&md->lock);
- ret = __blk_end_request(rqc, -EIO,
- brqc->data.blksz);
+ ret = __blk_end_request(rqp, -EIO,
+ brqp->data.blksz);
spin_unlock_irq(&md->lock);
continue;
}
@@ -524,14 +560,72 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
/*
* A block was successfully transferred.
*/
+ /*
+ * TODO: Find out if it safe to only check if
+ * blk_rq_bytes(req) == data.bytes_xfered to make sure
+ * the entire request is completed. If equal, defer
+ * __blk_end_request until after the new request is started.
+ */
+ if (blk_rq_bytes(rqp) != brqp->data.bytes_xfered ||
+ !complete_transfer) {
+ complete_transfer = false;
+ mmc_post_req(card->host, &brqp->mrq);
+ mmc_queue_bounce_post(mqrqp);
+
+ spin_lock_irq(&md->lock);
+ ret = __blk_end_request(rqp, 0,
+ brqp->data.bytes_xfered);
+ spin_unlock_irq(&md->lock);
+ }
+ } while (ret);
+
+ /* Previous request is completed, start the new request if any */
+ if (rqc)
+ mmc_start_req(card->host, &brqc->mrq);
+
+ /* Post process the previous request while the new request is active */
+ if (complete_transfer) {
+ mmc_post_req(card->host, &brqp->mrq);
+ mmc_queue_bounce_post(mqrqp);
+
spin_lock_irq(&md->lock);
- ret = __blk_end_request(rqc, 0, brqc->data.bytes_xfered);
+ ret = __blk_end_request(rqp, 0, brqp->data.bytes_xfered);
spin_unlock_irq(&md->lock);
- } while (ret);
- mmc_release_host(card->host);
+ /*
+ * TODO: Make sure "ret" can never be true and remove the
+ * if-statement and the code inside it.
+ */
+ if (ret) {
+ /* This should never happen */
+ printk(KERN_ERR "[%s] BUG: rq_bytes %d xfered %d\n",
+ __func__, blk_rq_bytes(rqp),
+ brqp->data.bytes_xfered);
+ BUG();
+ }
+ }
+ /* 1 indicates one request has been completed */
+ ret = 1;
+ out:
+ /*
+ * TODO: Find out if it is OK to only release host after the
+ * last request. For the last request the current request
+ * is NULL, which means no requests are pending.
+ */
+ if (!rqc)
+ mmc_release_host(card->host);
+
+ do {
+ /* Current request becomes previous request and vice versa. */
+ struct mmc_queue_req *tmp;
+ mq->mqrq_prev->brq.mrq.data = NULL;
+ mq->mqrq_prev->req = NULL;
+ tmp = mq->mqrq_prev;
+ mq->mqrq_prev = mq->mqrq_cur;
+ mq->mqrq_cur = tmp;
+ } while (0);
- return 1;
+ return ret;
cmd_err:
/*
@@ -548,12 +642,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
spin_lock_irq(&md->lock);
- ret = __blk_end_request(rqc, 0, blocks << 9);
+ ret = __blk_end_request(rqp, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
spin_lock_irq(&md->lock);
- ret = __blk_end_request(rqc, 0, brqc->data.bytes_xfered);
+ ret = __blk_end_request(rqp, 0, brqp->data.bytes_xfered);
spin_unlock_irq(&md->lock);
}
@@ -561,7 +655,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
spin_lock_irq(&md->lock);
while (ret)
- ret = __blk_end_request(rqc, -EIO, blk_rq_cur_bytes(rqc));
+ ret = __blk_end_request(rqp, -EIO, blk_rq_cur_bytes(rqp));
spin_unlock_irq(&md->lock);
return 0;
@@ -569,7 +663,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
- if (req->cmd_flags & REQ_DISCARD) {
+ if (req && req->cmd_flags & REQ_DISCARD) {
if (req->cmd_flags & REQ_SECURE)
return mmc_blk_issue_secdiscard_rq(mq, req);
else
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 30d4707..30f8ae9 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -60,6 +60,7 @@ static int mmc_queue_thread(void *d)
mq->mqrq_cur->req = req;
spin_unlock_irq(q->queue_lock);
+ mq->issue_fn(mq, req);
if (!req) {
if (kthread_should_stop()) {
set_current_state(TASK_RUNNING);
@@ -72,7 +73,6 @@ static int mmc_queue_thread(void *d)
}
set_current_state(TASK_RUNNING);
- mq->issue_fn(mq, req);
} while (1);
up(&mq->thread_sem);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index a3a780f..63b4684 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -195,30 +195,87 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
static void mmc_wait_done(struct mmc_request *mrq)
{
- complete(mrq->done_data);
+ complete(&mrq->completion);
}
/**
- * mmc_wait_for_req - start a request and wait for completion
+ * mmc_pre_req - Prepare for a new request
+ * @host: MMC host to prepare command
+ * @mrq: MMC request to prepare for
+ * @host_is_idle: true if the host it not processing a request,
+ * false if a request may be active on the host.
+ *
+ * mmc_pre_req() is called in prior to mmc_start_req() to let
+ * host prepare for the new request. Preparation of a request may be
+ * performed while another request is running on the host.
+ */
+void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
+ bool host_is_idle)
+{
+ if (host->ops->pre_req)
+ host->ops->pre_req(host, mrq, host_is_idle);
+}
+EXPORT_SYMBOL(mmc_pre_req);
+
+/**
+ * mmc_post_req - Post process a completed request
+ * @host: MMC host to post process command
+ * @mrq: MMC request to post process for
+ *
+ * Let the host post process a completed request. Post processing of
+ * a request may be performed while another reuqest is running.
+ */
+void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq)
+{
+ if (host->ops->post_req)
+ host->ops->post_req(host, mrq);
+}
+EXPORT_SYMBOL(mmc_post_req);
+
+/**
+ * mmc_start_req - start a request
* @host: MMC host to start command
* @mrq: MMC request to start
*
- * Start a new MMC custom command request for a host, and wait
- * for the command to complete. Does not attempt to parse the
- * response.
+ * Start a new MMC custom command request for a host.
+ * Does not wait for the command to complete.
*/
-void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
+void mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
- DECLARE_COMPLETION_ONSTACK(complete);
-
- mrq->done_data = &complete;
+ init_completion(&mrq->completion);
mrq->done = mmc_wait_done;
mmc_start_request(host, mrq);
+}
+EXPORT_SYMBOL(mmc_start_req);
- wait_for_completion(&complete);
+/**
+ * mmc_wait_for_req_done - wait for completion of request
+ * @mrq: MMC request to wait for
+ *
+ * Wait for the command to complete. Does not attempt to parse the
+ * response.
+ */
+void mmc_wait_for_req_done(struct mmc_request *mrq)
+{
+ wait_for_completion(&mrq->completion);
}
+EXPORT_SYMBOL(mmc_wait_for_req_done);
+/**
+ * mmc_wait_for_req - start a request and wait for completion
+ * @host: MMC host to start command
+ * @mrq: MMC request to start
+ *
+ * Start a new MMC custom command request for a host, and wait
+ * for the command to complete. Does not attempt to parse the
+ * response.
+ */
+void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
+{
+ mmc_start_req(host, mrq);
+ mmc_wait_for_req_done(mrq);
+}
EXPORT_SYMBOL(mmc_wait_for_req);
/**
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 64e013f..da504f7 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -124,13 +124,18 @@ struct mmc_request {
struct mmc_data *data;
struct mmc_command *stop;
- void *done_data; /* completion data */
+ struct completion completion;
void (*done)(struct mmc_request *);/* completion function */
};
struct mmc_host;
struct mmc_card;
+extern void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
+ bool host_is_idle);
+extern void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq);
+extern void mmc_start_req(struct mmc_host *host, struct mmc_request *mrq);
+extern void mmc_wait_for_req_done(struct mmc_request *mrq);
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 30f6fad..b85463b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -88,6 +88,14 @@ struct mmc_host_ops {
*/
int (*enable)(struct mmc_host *host);
int (*disable)(struct mmc_host *host, int lazy);
+ /*
+ * It is optional for the host to implement pre_req and post_req in
+ * order to support double buffering of requests (prepare one
+ * request while another request is active).
+ */
+ void (*post_req)(struct mmc_host *host, struct mmc_request *req);
+ void (*pre_req)(struct mmc_host *host, struct mmc_request *req,
+ bool host_is_idle);
void (*request)(struct mmc_host *host, struct mmc_request *req);
/*
* Avoid calling these three functions too often or in a "fast path",
--
1.7.1
^ permalink raw reply related
* [PATCH] ARM: Thumb-2: Fix out-of-range offset for Thumb-2 in proc-v7.S
From: Uwe Kleine-König @ 2011-01-12 18:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTinH0vJsXPkStC8nSqMjCgyv7YPscf2ORry_FdhG@mail.gmail.com>
On Wed, Jan 12, 2011 at 10:33:33AM -0600, Dave Martin wrote:
> 2011/1/12 Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>:
> > On Tue, Jan 11, 2011 at 06:03:33PM -0600, Dave Martin wrote:
> >> The following patch introduces a pre-increment addressing
> >> offset which is out of range for Thumb-2:
> >> ? ? ARM: pgtable: switch order of Linux vs hardware page tables
> > a commit id would be nice here
> >
> > Uwe
>
> Apologies--- it's commit d30e45eeabefadc6039d7f876a59e5f5f6cb11c6
No need to excuse.
I suggest:
ARM: Thumb-2: Fix out-of-range offset for Thumb-2 in proc-v7.S
Commit
d30e45e (ARM: pgtable: switch order of Linux vs hardware page tables)
introduced a pre-increment addressing offset which is out of range for
Thumb-2. Thumb-2 only permits offsets <256. So split the intruction in
two for Thumb-2.
Signed-off-by: ...
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply
* [FYI 0/4] arm: mmci: example implementation of double buffering
From: Per Forlin @ 2011-01-12 18:19 UTC (permalink / raw)
To: linux-arm-kernel
The mmci host driver implementation for double buffering is not intended
nor ready for mainline yet. It is only an example of how to implement
pre_req() and post_req(). The reason for this is that the basic DMA
support for MMCI is not complete yet.
This patch series depends on
"[PATCH 0/5] mmc: add double buffering for mmc block requests"
Linus Walleij (1):
mmci: add PrimeCell generic DMA to MMCI/PL180
Per Forlin (1):
ARM: mmci: add support for double buffering
Ulf Hansson (2):
mmci: fixup broken_blockend variant patch
MMCI: Corrections for DMA
drivers/mmc/host/mmci.c | 538 ++++++++++++++++++++++++++++++++++++++-------
drivers/mmc/host/mmci.h | 21 ++-
include/linux/amba/mmci.h | 16 ++
3 files changed, 496 insertions(+), 79 deletions(-)
^ permalink raw reply
* [FYI 1/4] mmci: fixup broken_blockend variant patch
From: Per Forlin @ 2011-01-12 18:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294856383-14187-1-git-send-email-per.forlin@linaro.org>
From: Ulf Hansson <ulf.hansson@stericsson.com>
NOT to be mainlined, only sets the base for the double
buffer example implementation. The DMA implemenation for
MMCI is under development.
host->last_blockend flag is now set only for the last
MCI_DATABLOCKEND, the previous code would just react to
any blockend, which was buggy. Consolidate Ux500 and U300
bug flags to a single one and use only the MCI_DATAEND irq
on U300 as well, this is faster anyway.
Also make sure the MCI_DATABLOCKENDMASK is set only when
needed, instead of being set always and then masked off.
Tested successfully on Ux500, U300 and ARM RealView
PB1176.
---
drivers/mmc/host/mmci.c | 45 ++++++++++++++++++++++-----------------------
drivers/mmc/host/mmci.h | 4 ++--
2 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 5630228..aafede4 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -48,8 +48,6 @@ static unsigned int fmax = 515633;
* is asserted (likewise for RX)
* @broken_blockend: the MCI_DATABLOCKEND is broken on the hardware
* and will not work at all.
- * @broken_blockend_dma: the MCI_DATABLOCKEND is broken on the hardware when
- * using DMA.
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
*/
@@ -60,7 +58,6 @@ struct variant_data {
unsigned int fifosize;
unsigned int fifohalfsize;
bool broken_blockend;
- bool broken_blockend_dma;
bool sdio;
bool st_clkdiv;
};
@@ -76,7 +73,7 @@ static struct variant_data variant_u300 = {
.fifohalfsize = 8 * 4,
.clkreg_enable = 1 << 13, /* HWFCEN */
.datalength_bits = 16,
- .broken_blockend_dma = true,
+ .broken_blockend = true,
.sdio = true,
};
@@ -199,7 +196,7 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
{
struct variant_data *variant = host->variant;
- unsigned int datactrl, timeout, irqmask;
+ unsigned int datactrl, timeout, irqmask0, irqmask1;
unsigned long long clks;
void __iomem *base;
int blksz_bits;
@@ -210,7 +207,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
host->data = data;
host->size = data->blksz * data->blocks;
host->data_xfered = 0;
- host->blockend = false;
+ host->last_blockend = false;
host->dataend = false;
mmci_init_sg(host, data);
@@ -230,20 +227,20 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
if (data->flags & MMC_DATA_READ) {
datactrl |= MCI_DPSM_DIRECTION;
- irqmask = MCI_RXFIFOHALFFULLMASK;
+ irqmask1 = MCI_RXFIFOHALFFULLMASK;
/*
* If we have less than a FIFOSIZE of bytes to transfer,
* trigger a PIO interrupt as soon as any data is available.
*/
if (host->size < variant->fifosize)
- irqmask |= MCI_RXDATAAVLBLMASK;
+ irqmask1 |= MCI_RXDATAAVLBLMASK;
} else {
/*
* We don't actually need to include "FIFO empty" here
* since its implicit in "FIFO half empty".
*/
- irqmask = MCI_TXFIFOHALFEMPTYMASK;
+ irqmask1 = MCI_TXFIFOHALFEMPTYMASK;
}
/* The ST Micro variants has a special bit to enable SDIO */
@@ -252,8 +249,14 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
datactrl |= MCI_ST_DPSM_SDIOEN;
writel(datactrl, base + MMCIDATACTRL);
- writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
- mmci_set_mask1(host, irqmask);
+ irqmask0 = readl(base + MMCIMASK0);
+ if (variant->broken_blockend)
+ irqmask0 &= ~MCI_DATABLOCKENDMASK;
+ else
+ irqmask0 |= MCI_DATABLOCKENDMASK;
+ irqmask0 &= ~MCI_DATAENDMASK;
+ writel(irqmask0, base + MMCIMASK0);
+ mmci_set_mask1(host, irqmask1);
}
static void
@@ -301,7 +304,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
data->error = -EIO;
/* Force-complete the transaction */
- host->blockend = true;
+ host->last_blockend = true;
host->dataend = true;
/*
@@ -337,7 +340,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
*
* In the U300, the IRQs can arrive out-of-order,
* e.g. MCI_DATABLOCKEND sometimes arrives after MCI_DATAEND,
- * so for this case we use the flags "blockend" and
+ * so for this case we use the flags "last_blockend" and
* "dataend" to make sure both IRQs have arrived before
* concluding the transaction. (This does not apply
* to the Ux500 which doesn't fire MCI_DATABLOCKEND
@@ -353,7 +356,8 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
*/
if (!variant->broken_blockend)
host->data_xfered += data->blksz;
- host->blockend = true;
+ if (host->data_xfered == data->blksz * data->blocks)
+ host->last_blockend = true;
}
if (status & MCI_DATAEND)
@@ -364,11 +368,12 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
* on others we must sync with the blockend signal since they can
* appear out-of-order.
*/
- if (host->dataend && (host->blockend || variant->broken_blockend)) {
+ if (host->dataend &&
+ (host->last_blockend || variant->broken_blockend)) {
mmci_stop_data(host);
/* Reset these flags */
- host->blockend = false;
+ host->last_blockend = false;
host->dataend = false;
/*
@@ -770,7 +775,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
struct variant_data *variant = id->data;
struct mmci_host *host;
struct mmc_host *mmc;
- unsigned int mask;
int ret;
/* must have platform data */
@@ -951,12 +955,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
goto irq0_free;
}
- mask = MCI_IRQENABLE;
- /* Don't use the datablockend flag if it's broken */
- if (variant->broken_blockend)
- mask &= ~MCI_DATABLOCKEND;
-
- writel(mask, host->base + MMCIMASK0);
+ writel(MCI_IRQENABLE, host->base + MMCIMASK0);
amba_set_drvdata(dev, mmc);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index df06f01..7ac8c4d 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -137,7 +137,7 @@
#define MCI_IRQENABLE \
(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \
MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
- MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK)
+ MCI_CMDRESPENDMASK|MCI_CMDSENTMASK)
/* These interrupts are directed to IRQ1 when two IRQ lines are available */
#define MCI_IRQ1MASK \
@@ -177,7 +177,7 @@ struct mmci_host {
struct timer_list timer;
unsigned int oldstat;
- bool blockend;
+ bool last_blockend;
bool dataend;
/* pio stuff */
--
1.7.1
^ permalink raw reply related
* [FYI 2/4] mmci: add PrimeCell generic DMA to MMCI/PL180
From: Per Forlin @ 2011-01-12 18:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1294856383-14187-1-git-send-email-per.forlin@linaro.org>
From: Linus Walleij <linus.walleij@stericsson.com>
NOT to be mainlined, only sets the base for the double
buffer example implementation. The DMA implemenation for
MMCI is under development.
This extends the MMCI/PL180 driver with generic DMA engine support
using the PrimeCell DMA engine interface.
---
drivers/mmc/host/mmci.c | 264 +++++++++++++++++++++++++++++++++++++++++++--
drivers/mmc/host/mmci.h | 13 +++
include/linux/amba/mmci.h | 16 +++
3 files changed, 282 insertions(+), 11 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index aafede4..38fcbde 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -2,7 +2,7 @@
* linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver
*
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- * Copyright (C) 2010 ST-Ericsson AB.
+ * Copyright (C) 2010 ST-Ericsson SA
*
* 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
@@ -24,8 +24,10 @@
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/gpio.h>
-#include <linux/amba/mmci.h>
#include <linux/regulator/consumer.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/amba/mmci.h>
#include <asm/div64.h>
#include <asm/io.h>
@@ -41,11 +43,15 @@ static unsigned int fmax = 515633;
* struct variant_data - MMCI variant-specific quirks
* @clkreg: default value for MCICLOCK register
* @clkreg_enable: enable value for MMCICLOCK register
+ * @dmareg_enable: enable value for MMCIDATACTRL register
* @datalength_bits: number of bits in the MMCIDATALENGTH register
* @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
* is asserted (likewise for RX)
* @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
* is asserted (likewise for RX)
+ * @txsize_threshold: Sets DMA burst size to minimal if transfer size is
+ * less or equal to this threshold. This shall be specified in
+ * number of bytes. Set 0 for no burst compensation
* @broken_blockend: the MCI_DATABLOCKEND is broken on the hardware
* and will not work at all.
* @sdio: variant supports SDIO
@@ -54,9 +60,11 @@ static unsigned int fmax = 515633;
struct variant_data {
unsigned int clkreg;
unsigned int clkreg_enable;
+ unsigned int dmareg_enable;
unsigned int datalength_bits;
unsigned int fifosize;
unsigned int fifohalfsize;
+ unsigned int txsize_threshold;
bool broken_blockend;
bool sdio;
bool st_clkdiv;
@@ -80,8 +88,10 @@ static struct variant_data variant_u300 = {
static struct variant_data variant_ux500 = {
.fifosize = 30 * 4,
.fifohalfsize = 8 * 4,
+ .txsize_threshold = 16,
.clkreg = MCI_CLK_ENABLE,
.clkreg_enable = 1 << 14, /* HWFCEN */
+ .dmareg_enable = 1 << 12, /* DMAREQCTRL */
.datalength_bits = 24,
.broken_blockend = true,
.sdio = true,
@@ -193,6 +203,209 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
}
+/*
+ * All the DMA operation mode stuff goes inside this ifdef.
+ * This assumes that you have a generic DMA device interface,
+ * no custom DMA interfaces are supported.
+ */
+#ifdef CONFIG_DMA_ENGINE
+static void __devinit mmci_setup_dma(struct mmci_host *host)
+{
+ struct mmci_platform_data *plat = host->plat;
+ dma_cap_mask_t mask;
+
+ if (!plat || !plat->dma_filter) {
+ dev_err(mmc_dev(host->mmc), "no DMA platform data!\n");
+ return;
+ }
+
+ /* Try to acquire a generic DMA engine slave channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ /*
+ * If only an RX channel is specified, the driver will
+ * attempt to use it bidirectionally, however if it is
+ * is specified but cannot be located, DMA will be disabled.
+ */
+ host->dma_rx_channel = dma_request_channel(mask,
+ plat->dma_filter,
+ plat->dma_rx_param);
+ /* E.g if no DMA hardware is present */
+ if (!host->dma_rx_channel) {
+ dev_err(mmc_dev(host->mmc), "no RX DMA channel!\n");
+ return;
+ }
+ if (plat->dma_tx_param) {
+ host->dma_tx_channel = dma_request_channel(mask,
+ plat->dma_filter,
+ plat->dma_tx_param);
+ if (!host->dma_tx_channel) {
+ dma_release_channel(host->dma_rx_channel);
+ host->dma_rx_channel = NULL;
+ return;
+ }
+ } else {
+ host->dma_tx_channel = host->dma_rx_channel;
+ }
+ host->dma_enable = true;
+ dev_info(mmc_dev(host->mmc), "use DMA channels DMA RX %s, DMA TX %s\n",
+ dma_chan_name(host->dma_rx_channel),
+ dma_chan_name(host->dma_tx_channel));
+}
+
+/*
+ * This is used in __devinit or __devexit so inline it
+ * so it can be discarded.
+ */
+static inline void mmci_disable_dma(struct mmci_host *host)
+{
+ if (host->dma_rx_channel)
+ dma_release_channel(host->dma_rx_channel);
+ if (host->dma_tx_channel)
+ dma_release_channel(host->dma_tx_channel);
+ host->dma_enable = false;
+}
+
+static void mmci_dma_data_end(struct mmci_host *host)
+{
+ struct mmc_data *data = host->data;
+
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ host->dma_on_current_xfer = false;
+}
+
+static void mmci_dma_terminate(struct mmci_host *host)
+{
+ struct mmc_data *data = host->data;
+ struct dma_chan *chan;
+
+ dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+ if (data->flags & MMC_DATA_READ)
+ chan = host->dma_rx_channel;
+ else
+ chan = host->dma_tx_channel;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+}
+
+static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
+{
+ struct variant_data *variant = host->variant;
+ struct dma_slave_config rx_conf = {
+ .src_addr = host->phybase + MMCIFIFO,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .direction = DMA_FROM_DEVICE,
+ .src_maxburst = variant->fifohalfsize >> 2, /* # of words */
+ };
+ struct dma_slave_config tx_conf = {
+ .dst_addr = host->phybase + MMCIFIFO,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ .direction = DMA_TO_DEVICE,
+ .dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
+ };
+ struct mmc_data *data = host->data;
+ enum dma_data_direction direction;
+ struct dma_chan *chan;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sg;
+ dma_cookie_t cookie;
+ int i;
+
+ datactrl |= MCI_DPSM_DMAENABLE;
+ datactrl |= variant->dmareg_enable;
+
+ if (data->flags & MMC_DATA_READ) {
+ if (host->size <= variant->txsize_threshold)
+ rx_conf.src_maxburst = 1;
+
+ direction = DMA_FROM_DEVICE;
+ chan = host->dma_rx_channel;
+ chan->device->device_control(chan, DMA_SLAVE_CONFIG,
+ (unsigned long) &rx_conf);
+ } else {
+ if (host->size <= variant->txsize_threshold)
+ tx_conf.dst_maxburst = 1;
+
+ direction = DMA_TO_DEVICE;
+ chan = host->dma_tx_channel;
+ chan->device->device_control(chan, DMA_SLAVE_CONFIG,
+ (unsigned long) &tx_conf);
+ }
+
+ /* Check for weird stuff in the sg list */
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ dev_vdbg(mmc_dev(host->mmc), "MMCI SGlist %d dir %d: length: %08x\n",
+ i, direction, sg->length);
+ if (sg->offset & 3 || sg->length & 3)
+ return -EINVAL;
+ }
+
+ dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+
+ desc = chan->device->device_prep_slave_sg(chan,
+ data->sg, data->sg_len, direction,
+ DMA_CTRL_ACK);
+ if (!desc)
+ goto unmap_exit;
+
+ host->dma_desc = desc;
+ dev_vdbg(mmc_dev(host->mmc), "Submit MMCI DMA job, sglen %d "
+ "blksz %04x blks %04x flags %08x\n",
+ data->sg_len, data->blksz, data->blocks, data->flags);
+ cookie = desc->tx_submit(desc);
+
+ /* Here overloaded DMA controllers may fail */
+ if (dma_submit_error(cookie))
+ goto unmap_exit;
+
+ host->dma_on_current_xfer = true;
+ chan->device->device_issue_pending(chan);
+
+ /* Trigger the DMA transfer */
+ writel(datactrl, host->base + MMCIDATACTRL);
+ /*
+ * Let the MMCI say when the data is ended and it's time
+ * to fire next DMA request. When that happens, MMCI will
+ * call mmci_data_end()
+ */
+ writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
+ host->base + MMCIMASK0);
+ return 0;
+
+unmap_exit:
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction);
+ return -ENOMEM;
+}
+#else
+/* Blank functions if the DMA engine is not available */
+static inline void mmci_setup_dma(struct mmci_host *host)
+{
+}
+
+static inline void mmci_disable_dma(struct mmci_host *host)
+{
+}
+
+static inline void mmci_dma_data_end(struct mmci_host *host)
+{
+}
+
+static inline void mmci_dma_terminate(struct mmci_host *host)
+{
+}
+
+static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
+{
+ return -ENOSYS;
+}
+#endif
+
static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
{
struct variant_data *variant = host->variant;
@@ -210,8 +423,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
host->last_blockend = false;
host->dataend = false;
- mmci_init_sg(host, data);
-
clks = (unsigned long long)data->timeout_ns * host->cclk;
do_div(clks, 1000000000UL);
@@ -225,13 +436,32 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
BUG_ON(1 << blksz_bits != data->blksz);
datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
- if (data->flags & MMC_DATA_READ) {
+
+ if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION;
+
+ if (host->dma_enable) {
+ int ret;
+
+ /*
+ * Attempt to use DMA operation mode, if this
+ * should fail, fall back to PIO mode
+ */
+ ret = mmci_dma_start_data(host, datactrl);
+ if (!ret)
+ return;
+ }
+
+ /* IRQ mode, map the SG list for CPU reading/writing */
+ mmci_init_sg(host, data);
+
+ if (data->flags & MMC_DATA_READ) {
irqmask1 = MCI_RXFIFOHALFFULLMASK;
/*
- * If we have less than a FIFOSIZE of bytes to transfer,
- * trigger a PIO interrupt as soon as any data is available.
+ * If we have less than a FIFOSIZE of bytes to
+ * transfer, trigger a PIO interrupt as soon as any
+ * data is available.
*/
if (host->size < variant->fifosize)
irqmask1 |= MCI_RXDATAAVLBLMASK;
@@ -309,9 +539,12 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
/*
* We hit an error condition. Ensure that any data
- * partially written to a page is properly coherent.
+ * partially written to a page is properly coherent,
+ * unless we're using DMA.
*/
- if (data->flags & MMC_DATA_READ) {
+ if (host->dma_on_current_xfer)
+ mmci_dma_terminate(host);
+ else if (data->flags & MMC_DATA_READ) {
struct sg_mapping_iter *sg_miter = &host->sg_miter;
unsigned long flags;
@@ -354,7 +587,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
* flag is unreliable: since it can stay high between
* IRQs it will corrupt the transfer counter.
*/
- if (!variant->broken_blockend)
+ if (!variant->broken_blockend && !host->dma_on_current_xfer)
host->data_xfered += data->blksz;
if (host->data_xfered == data->blksz * data->blocks)
host->last_blockend = true;
@@ -370,6 +603,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
*/
if (host->dataend &&
(host->last_blockend || variant->broken_blockend)) {
+ mmci_dma_data_end(host);
mmci_stop_data(host);
/* Reset these flags */
@@ -411,8 +645,11 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
}
if (!cmd->data || cmd->error) {
- if (host->data)
+ if (host->data) {
+ if (host->dma_on_current_xfer)
+ mmci_dma_terminate(host);
mmci_stop_data(host);
+ }
mmci_request_end(host, cmd->mrq);
} else if (!(cmd->data->flags & MMC_DATA_READ)) {
mmci_start_data(host, cmd->data);
@@ -832,6 +1069,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
dev_dbg(mmc_dev(mmc), "eventual mclk rate: %u Hz\n",
host->mclk);
}
+ host->phybase = dev->res.start;
host->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!host->base) {
ret = -ENOMEM;
@@ -942,6 +1180,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
&& host->gpio_cd_irq < 0)
mmc->caps |= MMC_CAP_NEEDS_POLL;
+ mmci_setup_dma(host);
+
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
if (ret)
goto unmap;
@@ -970,6 +1210,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
irq0_free:
free_irq(dev->irq[0], host);
unmap:
+ mmci_disable_dma(host);
if (host->gpio_wp != -ENOSYS)
gpio_free(host->gpio_wp);
err_gpio_wp:
@@ -1008,6 +1249,7 @@ static int __devexit mmci_remove(struct amba_device *dev)
writel(0, host->base + MMCICOMMAND);
writel(0, host->base + MMCIDATACTRL);
+ mmci_disable_dma(host);
free_irq(dev->irq[0], host);
if (!host->singleirq)
free_irq(dev->irq[1], host);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 7ac8c4d..39b7ac7 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -148,8 +148,11 @@
struct clk;
struct variant_data;
+struct dma_chan;
+struct dma_async_tx_descriptor;
struct mmci_host {
+ phys_addr_t phybase;
void __iomem *base;
struct mmc_request *mrq;
struct mmc_command *cmd;
@@ -183,6 +186,16 @@ struct mmci_host {
/* pio stuff */
struct sg_mapping_iter sg_miter;
unsigned int size;
+
struct regulator *vcc;
+
+ /* DMA stuff */
+ bool dma_enable;
+ bool dma_on_current_xfer;
+#ifdef CONFIG_DMA_ENGINE
+ struct dma_chan *dma_rx_channel;
+ struct dma_chan *dma_tx_channel;
+ struct dma_async_tx_descriptor *dma_desc;
+#endif
};
diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h
index f4ee9ac..dbeba68 100644
--- a/include/linux/amba/mmci.h
+++ b/include/linux/amba/mmci.h
@@ -6,6 +6,8 @@
#include <linux/mmc/host.h>
+/* Just some dummy forwarding */
+struct dma_chan;
/**
* struct mmci_platform_data - platform configuration for the MMCI
* (also known as PL180) block.
@@ -27,6 +29,17 @@
* @cd_invert: true if the gpio_cd pin value is active low
* @capabilities: the capabilities of the block as implemented in
* this platform, signify anything MMC_CAP_* from mmc/host.h
+ * @dma_filter: function used to select an apropriate RX and TX
+ * DMA channel to be used for DMA, if and only if you're deploying the
+ * generic DMA engine
+ * @dma_rx_param: parameter passed to the DMA allocation
+ * filter in order to select an apropriate RX channel. If
+ * there is a bidirectional RX+TX channel, then just specify
+ * this and leave dma_tx_param set to NULL
+ * @dma_tx_param: parameter passed to the DMA allocation
+ * filter in order to select an apropriate TX channel. If this
+ * is NULL the driver will attempt to use the RX channel as a
+ * bidirectional channel
*/
struct mmci_platform_data {
unsigned int f_max;
@@ -38,6 +51,9 @@ struct mmci_platform_data {
int gpio_cd;
bool cd_invert;
unsigned long capabilities;
+ bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
+ void *dma_rx_param;
+ void *dma_tx_param;
};
#endif
--
1.7.1
^ 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