* [PATCH] ARM: OMAP2: use early init hook
From: Tony Lindgren @ 2011-02-14 23:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110124201731.GC13589@atomide.com>
* Tony Lindgren <tony@atomide.com> [110124 12:16]:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [110123 09:47]:
> > On Sun, Jan 23, 2011 at 05:13:44PM +0000, Russell King - ARM Linux wrote:
> > > Move non-mapping and non-irq initialization code out of .map_io and
> > > .init_irq respectively into the new init_early hook.
> > >
> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > > ---
> > > I think I've updated this patch correctly for the new platforms merged
> > > into mainline.
> >
> > Grr, this breaks on OMAP because some of this stuff wants to do ioremap()
> > and therefore wants the kmem allocators initialized. That's rather
> > annoying...
>
> So far tested on zoom3 only, but the following gets it booting
> on top of your patch.
>
> Want to take this one into your series?
Russell, I'll queue your patch for the upcoming merge window into
devel-early-init branch along with my changes.
Will also post one more clean-up patch.
Regards,
Tony
^ permalink raw reply
* [PATCH RESEND v6 0/4] Introduce support for TI816X processor series
From: Kevin Hilman @ 2011-02-14 23:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297695791-26940-1-git-send-email-hemantp@ti.com>
Hemant Pedanekar <hemantp@ti.com> writes:
> (Resending v6 with cc: linux-arm-kernel)
>
> This patch set adds support for TI816X processor series. This series includes
> DM8168, C6A816x and AM389x devices.
[...]
Other than the minor comment/confirmation on PATCH 2/4, this series
Reviewed-by: Kevin Hilman <khilman@ti.com>
^ permalink raw reply
* [PATCH RESEND v6 2/4] TI816X: Update common OMAP machine specific sources
From: Kevin Hilman @ 2011-02-14 23:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297695819-27027-1-git-send-email-hemantp@ti.com>
Hemant Pedanekar <hemantp@ti.com> writes:
[...]
> @@ -402,7 +422,7 @@ void __init omap2_init_common_devices(struct omap_sdrc_params *sdrc_cs0,
>
> omap_hwmod_late_init();
>
> - if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
> + if (omap3_has_sdrc()) {
> omap2_sdrc_init(sdrc_cs0, sdrc_cs1);
> _omap2_init_reprogram_sdrc();
> }
At first glance, this one doesn't look quite right. Will
omap3_has_sdrc() be true on 24xx? Please confirm.
Kevin
^ permalink raw reply
* [PATCH v4 0/5] ARM: omap[34]: Thumb-2 compatibility fixes
From: Kevin Hilman @ 2011-02-14 23:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110214153726.GA20144@arm.com>
Hi Dave,
Dave Martin <dave.martin@linaro.org> writes:
> On Mon, Feb 14, 2011 at 10:00:23AM -0500, Nicolas Pitre wrote:
>> On Mon, 14 Feb 2011, Dave Martin wrote:
>>
>> > @@ -289,8 +297,20 @@ clean_l2:
>> > * - should be faster and will change with kernel
>> > * - 'might' have to copy address, load and jump to it
>> > */
>> > +#ifdef CONFIG_THUMB2_KERNEL
>> > + /* kernel is non-interworking : must do this from Thumb */
>> > + adr r1, . + 1
>> > + bx r1
>> > + .thumb
>> > +#endif
>> > ldr r1, kernel_flush
>>
>> Didn't you mean this instead:
>>
>> /* kernel is non-interworking : must do this from Thumb */
>> adr r1, 1f + 1
>> bx r1
>> .thumb
>> 1: ldr r1, kernel_flush
>> ...
>
> Note that this is intended as an experimental hack, not a real patch
> (apologies if I didn't make that clear...)
>
> Well, actually I meant "add r1, pc, #1" ... which means I was too
> busy trying to be clever... oops!
>
> That is of course exactly equivalent to your code...
>
>>
>> ?
>>
>> > blx r1
>> > +#ifdef CONFIG_THUMB2_KERNEL
>> > + .align
>> > + bx pc
>> > + nop
>> > + .arm
>>
>> Also here, the .align has the potential to introduce a zero halfword in
>> the instruction stream before the bx. What about:
>>
>> adr r3, 1f
>> bx r3
>> .align
>> .arm
>> 1: ...
>
> .align inserts a 16-bit nop when misaligned in Thumb in a text section,
> and a word-aligned bx pc is a specific architecturally allowed way
> to do an inline switch to ARM. The linker uses this trick for PLT
> veneers etc.
>
> A nicer fix for doing this sort of call from low-level code which
> might be ARM is to convert arch/arm/mm/*-v7.S to use "bx lr" to return.
>
> Generally, we can do this for all arches >= v5, without any
> incompatibility. However, since the need for it will be rare and it
> will generate patch noise for not much real benefit,
> I haven't proposed this.
>
> Updated patch below.
I tested the updated patch on top of your "dirty" branch I tested with
last week, and now see off-mode working just fine.
Kevin
> Cheers
> ---Dave
>
> diff --git a/arch/arm/mach-omap2/sleep34xx.S b/arch/arm/mach-omap2/sleep34xx.S
> index a204c78..6ae8a92 100644
> --- a/arch/arm/mach-omap2/sleep34xx.S
> +++ b/arch/arm/mach-omap2/sleep34xx.S
> @@ -32,6 +32,14 @@
> #include "sdrc.h"
> #include "control.h"
>
> +#undef ARM
> +#undef THUMB
> +#undef BSYM
> +#define ARM(x...) x
> +#define THUMB(x...)
> +#define BSYM(x) (x)
> + .arm
> +
> /*
> * Registers access definitions
> */
> @@ -289,8 +297,20 @@ clean_l2:
> * - should be faster and will change with kernel
> * - 'might' have to copy address, load and jump to it
> */
> - ldr r1, kernel_flush
> +#ifdef CONFIG_THUMB2_KERNEL
> + /* kernel is non-interworking : must do this from Thumb */
> + adr r1, 1f + 1
> + bx r1
> + .thumb
> +#endif
> +1: ldr r1, kernel_flush
> blx r1
> +#ifdef CONFIG_THUMB2_KERNEL
> + .align
> + bx pc
> + nop
> + .arm
> +#endif
>
> omap3_do_wfi:
> ldr r4, sdrc_power @ read the SDRC_POWER register
> diff --git a/arch/arm/mach-omap2/sram34xx.S b/arch/arm/mach-omap2/sram34xx.S
> index 829d235..64faab8 100644
> --- a/arch/arm/mach-omap2/sram34xx.S
> +++ b/arch/arm/mach-omap2/sram34xx.S
> @@ -34,6 +34,14 @@
> #include "sdrc.h"
> #include "cm2xxx_3xxx.h"
>
> +#undef ARM
> +#undef THUMB
> +#undef BSYM
> +#define ARM(x...) x
> +#define THUMB(x...)
> +#define BSYM(x) (x)
> + .arm
> +
> .text
>
> /* r1 parameters */
^ permalink raw reply
* [RFC] Inter-processor Mailboxes Drivers
From: Blanchard, Hollis @ 2011-02-14 23:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTin+ZSNHX2iv=XwWCgxy330UJwhkf1T=4BMXZ22s@mail.gmail.com>
On 02/13/2011 01:24 PM, Linus Walleij wrote:
>> > 3. Userspace interfaces for accessing the mailboxes. A
>> > '/dev/mailbox1', '/dev/mailbox2', etc... mapping, for example.
> What kind of business does userspace have with directly using
> mailboxes? Enlighten me so I get it... in our system these are
> used by protocols, such as net/caif/* thru drivers/net/caif/*, and
> we have similar kernelspace functionality for Phonet.
>
> CAIF and Phonet on the other hand, have custom openings
> down to the thing that exists on the other end of the mailbox.
> Most of these systems tend to talk some funny protocol that
> is often better handled by the kernel than by any userspace.
>
> So is this for the situation when you have no intermediate
> protocol between your userpace and the other CPU's
> subsystem? Or are you thinking about handling that
> protocol in userspace? That is generally not such a good idea
> for efficiency reasons.
OpenMCAPI (http://openmcapi.org) implements the MCAPI specification,
which is a simple application-level communication API that uses shared
memory. The API could be layered over any protocol, but was more or less
designed for simple shared-memory systems, e.g. fixed topology, no
retransmission, etc.
Currently, we implement almost all of this as a shared library, plus a
very small kernel driver. The only requirements on the kernel are to
allow userspace to map the shared memory area, and provide an IPI
mechanism (and allow the process to sleep while waiting). Applications
sync with each other using normal atomic memory operations.
We're now trying to optimize the transfer of scalars on platforms that
provide mailboxes (beyond simple IPIs), which is why we're looking at
defining a user-facing API to such hardware.
I'll add that we haven't done serious optimization yet, but the numbers
we do have seem reasonable. What are the "efficiency" issues you're
worried about?
Hollis Blanchard
Mentor Graphics, Embedded Systems Division
^ permalink raw reply
* [PATCH v2] ARM: vfp: Always save VFP state in vfp_pm_suspend
From: Colin Cross @ 2011-02-14 22:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTik_Jey_PtRmr530FVckA6RXHESeX+CyoJC=ZTkR@mail.gmail.com>
vfp_pm_suspend should save the VFP state any time there is
a last_VFP_context. If it only saves when the VFP is enabled,
the state can get lost when, on a UP system:
Thread 1 uses the VFP
Context switch occurs to thread 2, VFP is disabled but the
VFP context is not saved to allow lazy save and restore
Thread 2 initiates suspend
vfp_pm_suspend is called with the VFP disabled, but the
context has not been saved.
Modify vfp_pm_suspend to save the VFP context whenever
last_VFP_context is set.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Colin Cross <ccross@android.com>
---
arch/arm/vfp/vfpmodule.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 66bf8d1..7231d18 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -415,13 +415,13 @@ static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state)
struct thread_info *ti = current_thread_info();
u32 fpexc = fmrx(FPEXC);
- /* if vfp is on, then save state for resumption */
- if (fpexc & FPEXC_EN) {
+ /* save state for resume */
+ if (last_VFP_context[ti->cpu]) {
printk(KERN_DEBUG "%s: saving vfp state\n", __func__);
- vfp_save_state(&ti->vfpstate, fpexc);
-
- /* disable, just in case */
- fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
+ fmxr(FPEXC, fpexc | FPEXC_EN);
+ vfp_save_state(last_VFP_context[ti->cpu], fpexc);
+ last_VFP_context[ti->cpu] = NULL;
+ fmxr(FPEXC, fpexc & ~FPEXC_EN);
}
/* clear any information we had about last context state */
--
1.7.3.1
^ permalink raw reply related
* [PATCHv2] arm: mach-omap2: smartreflex: fix another memory leak
From: Kevin Hilman @ 2011-02-14 22:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297087684-19967-1-git-send-email-aaro.koskinen@nokia.com>
Aaro Koskinen <aaro.koskinen@nokia.com> writes:
> Temporary strings with volt_* file names should be released after the
> debugfs entries are created. While at it, also simplify the string
> allocation, and use just snprintf() to create the name.
>
> The patch eliminates kmemleak reports with the following stack trace
> (multiple objects depending on HW):
>
> unreferenced object 0xcedbc5a0 (size 64):
> comm "swapper", pid 1, jiffies 4294929375 (age 423.734s)
> hex dump (first 32 bytes):
> 76 6f 6c 74 5f 39 37 35 30 30 30 00 00 00 00 00 volt_975000.....
> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
> backtrace:
> [<c012fee0>] create_object+0x104/0x208
> [<c012dbc8>] kmem_cache_alloc_trace+0xf0/0x17c
> [<c0013f64>] omap_sr_probe+0x314/0x420
> [<c02a1724>] platform_drv_probe+0x18/0x1c
> [<c02a088c>] driver_probe_device+0xc8/0x188
> [<c02a09b4>] __driver_attach+0x68/0x8c
> [<c02a00ac>] bus_for_each_dev+0x44/0x74
> [<c029f9e0>] bus_add_driver+0xa0/0x228
> [<c02a0cac>] driver_register+0xa8/0x130
> [<c02a1b2c>] platform_driver_probe+0x18/0x8c
> [<c0013c1c>] sr_init+0x40/0x74
> [<c005a554>] do_one_initcall+0xc8/0x1a0
> [<c00084f4>] kernel_init+0x150/0x218
> [<c0065d64>] kernel_thread_exit+0x0/0x8
> [<ffffffff>] 0xffffffff
>
> Signed-off-by: Aaro Koskinen <aaro.koskinen@nokia.com>
> ---
>
> v2: Get rid of kmalloc().
Replied to the wrong patch earlier.
Just to be clear, I'm queuing v2 for the 2.6.38-rc cycle.
Kevin
> arch/arm/mach-omap2/smartreflex.c | 15 +++------------
> 1 files changed, 3 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c
> index 77ecebf..e54db84 100644
> --- a/arch/arm/mach-omap2/smartreflex.c
> +++ b/arch/arm/mach-omap2/smartreflex.c
> @@ -927,19 +927,10 @@ static int __init omap_sr_probe(struct platform_device *pdev)
> }
>
> for (i = 0; i < sr_info->nvalue_count; i++) {
> - char *name;
> - char volt_name[32];
> -
> - name = kzalloc(NVALUE_NAME_LEN + 1, GFP_KERNEL);
> - if (!name) {
> - dev_err(&pdev->dev, "%s: Unable to allocate memory"
> - " for n-value directory name\n", __func__);
> - return -ENOMEM;
> - }
> + char name[NVALUE_NAME_LEN + 1];
>
> - strcpy(name, "volt_");
> - sprintf(volt_name, "%d", volt_data[i].volt_nominal);
> - strcat(name, volt_name);
> + snprintf(name, sizeof(name), "volt_%d",
> + volt_data[i].volt_nominal);
> (void) debugfs_create_x32(name, S_IRUGO | S_IWUGO, nvalue_dir,
> &(sr_info->nvalue_table[i].nvalue));
> }
^ permalink raw reply
* [PATCH] arm: mach-omap2: smartreflex: fix another memory leak
From: Kevin Hilman @ 2011-02-14 22:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297083471-6341-1-git-send-email-aaro.koskinen@nokia.com>
Aaro Koskinen <aaro.koskinen@nokia.com> writes:
> Temporary strings with volt_* file names should be released after the
> debugfs entries are created.
>
> The patch eliminates kmemleak reports with the following stack trace
> (multiple objects depending on HW):
>
> unreferenced object 0xcedbc5a0 (size 64):
> comm "swapper", pid 1, jiffies 4294929375 (age 423.734s)
> hex dump (first 32 bytes):
> 76 6f 6c 74 5f 39 37 35 30 30 30 00 00 00 00 00 volt_975000.....
> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
> backtrace:
> [<c012fee0>] create_object+0x104/0x208
> [<c012dbc8>] kmem_cache_alloc_trace+0xf0/0x17c
> [<c0013f64>] omap_sr_probe+0x314/0x420
> [<c02a1724>] platform_drv_probe+0x18/0x1c
> [<c02a088c>] driver_probe_device+0xc8/0x188
> [<c02a09b4>] __driver_attach+0x68/0x8c
> [<c02a00ac>] bus_for_each_dev+0x44/0x74
> [<c029f9e0>] bus_add_driver+0xa0/0x228
> [<c02a0cac>] driver_register+0xa8/0x130
> [<c02a1b2c>] platform_driver_probe+0x18/0x8c
> [<c0013c1c>] sr_init+0x40/0x74
> [<c005a554>] do_one_initcall+0xc8/0x1a0
> [<c00084f4>] kernel_init+0x150/0x218
> [<c0065d64>] kernel_thread_exit+0x0/0x8
> [<ffffffff>] 0xffffffff
>
> Signed-off-by: Aaro Koskinen <aaro.koskinen@nokia.com>
Thanks, queuing for 2.6.38-rc cycle.
Kevin
> ---
> arch/arm/mach-omap2/smartreflex.c | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c
> index 77ecebf..61aebed 100644
> --- a/arch/arm/mach-omap2/smartreflex.c
> +++ b/arch/arm/mach-omap2/smartreflex.c
> @@ -942,6 +942,7 @@ static int __init omap_sr_probe(struct platform_device *pdev)
> strcat(name, volt_name);
> (void) debugfs_create_x32(name, S_IRUGO | S_IWUGO, nvalue_dir,
> &(sr_info->nvalue_table[i].nvalue));
> + kfree(name);
> }
>
> return ret;
^ permalink raw reply
* MMC quirks relating to performance/lifetime.
From: Andrei Warkentin @ 2011-02-14 22:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201102142122.54766.arnd@arndb.de>
On Mon, Feb 14, 2011 at 2:22 PM, Arnd Bergmann <arnd@arndb.de> wrote:
>>
>> As I mentioned, I am checking with T right now on whether we can use
>> suggestion (1) or
>> suggestion (2) or if they need to be combined. The documentation we
>> got was open to interpretation and the patch created from that did
>> both.
>> You mentioned that writing in reverse is not a good idea. Could you
>> elaborate why? I would guess because you're always causing a write
>> into a different AU (on these Toshiba cards), causing extra GC on
>> every write?
>
> Probably both the reliable write and writing small blocks in reverse
> order will cause any card to do something that is different from
> what it does on normal 64kb (or larger) aligned accesses.
>
> There are multiple ways how this could be implemented:
>
> 1. Have one exception cache for all "special" blocks. This would normally
> ? be for FAT32 subdirectory updates, which always write to the same
> ? few blocks. This means you can do small writes efficiently anywhere
> ? on the card, but only up to a (small) fixed number of block addresses.
> ? If you overflow the table, the card still needs to go through an
> ? extra PE for each new entry you write, in order to free up an entry.
>
> 2. Have a small number of AUs that can be in a special mode with efficient
> ? small writes but inefficient large writes. This means that when you
> ? alternate between small and large writes in the same AU, it has to go
> ? through a PE on every switch. Similarly, if you do small writes to
> ? more than the maximum number of AUs that can be held in this mode, you
> ? get the same effect. This number can be as small as one, because that
> ? is what FAT32 requires.
>
> In both cases, you don't actually have a solution for the problem, you just
> make it less likely for specific workloads.
Aha, ok. By the way, I did find out that either suggestion works. So
I'll pull out the reversing portion of the patch. No need to
overcomplicate :).
^ permalink raw reply
* [PATCH v2] OMAP: PM: DMA: Enable runtime pm
From: Kevin Hilman @ 2011-02-14 22:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297082286-8449-1-git-send-email-manjugk@ti.com>
"G, Manjunath Kondaiah" <manjugk@ti.com> writes:
> From: Manjunath G Kondaiah <manjugk@ti.com>
>
> Enable runtime pm and use pm_runtime_get_sync and pm_runtime_put_autosuspend
> for OMAP DMA driver.
>
> The DMA driver uses auto suspend feature of runtime pm framework through
> which the clock gets disabled automatically if there is no activity for
> more than one second.
>
> Testing:
> Compile: omap1_defconfig and omap2plus_defconfig
> Boot: OMAP1710(H3), OMAP2420(H4), OMAP3630(Zoom3), OMAP4(Blaze)
The normal DMA tests should also be run on these platforms. Based on
the above, I can't tell any DMA tests were run. Based on my tests,
this isn't working for chained xfers.
Using the runtime PM sysfs interface, you can check the runtime status
of the device:
# cat /sys/devices/platform/omap/omap_dma_system.0/power/runtime_status
It should show 'active' during transfer, and after timeout expires it
will show 'suspended'.
Doing some tests using my dmatest module:
git://gitorious.org/omap-test/dmatest.git
I noticed that it gets stuck in 'active' and never gets suspended when I
used DMA channel linking (load module using 'linking=1' as load-time option)
I'm not sure exactly why, but I will guess that the reason is that there
is an imbalance in get/put calls when using chaining, since 'get' is
only called once upon omap_start_dma() but 'put' is called for every
channel in the callback.
> On zoom3 core retention is tested with following steps:
> echo 1 > /debug/pm_debug/sleep_while_idle
> echo 1 > /debug/pm_debug/enable_off_mode
> echo 5 > /sys/devices/platform/omap/omap_uart.0/sleep_timeout
> echo 5 > /sys/devices/platform/omap/omap_uart.1/sleep_timeout
> echo 5 > /sys/devices/platform/omap/omap_uart.2/sleep_timeout
> echo 5 > /sys/devices/platform/omap/omap_uart.3/sleep_timeout
>
> It is observed that(on pm branch), core retention count gets increasing if the
> board is left idle for more than 5 seconds. However, it doesnot enter off mode
> (even without DMA runtime changes).
What silicon rev is on your Zoom3? Mainline kernels now disable core
off-mode for 3630 revs < ES2.1 due to erratum i583.
If this happens, you should see something like this on the console:
Core OFF disabled due to errata i583
Kevin
> Signed-off-by: G, Manjunath Kondaiah <manjugk@ti.com>
> ---
> arch/arm/plat-omap/dma.c | 27 +++++++++++++++++++++++++++
> 1 files changed, 27 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
> index 2ec3b5d..6bfe25c 100644
> --- a/arch/arm/plat-omap/dma.c
> +++ b/arch/arm/plat-omap/dma.c
> @@ -35,6 +35,7 @@
> #include <linux/io.h>
> #include <linux/slab.h>
> #include <linux/delay.h>
> +#include <linux/pm_runtime.h>
>
> #include <asm/system.h>
> #include <mach/hardware.h>
> @@ -59,6 +60,7 @@ enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED };
>
> static struct omap_system_dma_plat_info *p;
> static struct omap_dma_dev_attr *d;
> +static struct device *dev;
>
> static int enable_1510_mode;
> static u32 errata;
> @@ -676,6 +678,7 @@ int omap_request_dma(int dev_id, const char *dev_name,
> unsigned long flags;
> struct omap_dma_lch *chan;
>
> + pm_runtime_get_sync(dev);
> spin_lock_irqsave(&dma_chan_lock, flags);
> for (ch = 0; ch < dma_chan_count; ch++) {
> if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
> @@ -686,6 +689,7 @@ int omap_request_dma(int dev_id, const char *dev_name,
> }
> if (free_ch == -1) {
> spin_unlock_irqrestore(&dma_chan_lock, flags);
> + pm_runtime_put_autosuspend(dev);
> return -EBUSY;
> }
> chan = dma_chan + free_ch;
> @@ -743,6 +747,7 @@ int omap_request_dma(int dev_id, const char *dev_name,
> }
>
> *dma_ch_out = free_ch;
> + pm_runtime_put_autosuspend(dev);
>
> return 0;
> }
> @@ -871,6 +876,8 @@ void omap_start_dma(int lch)
> {
> u32 l;
>
> + pm_runtime_get_sync(dev);
> +
> /*
> * The CPC/CDAC register needs to be initialized to zero
> * before starting dma transfer.
> @@ -1805,6 +1812,8 @@ static int omap1_dma_handle_ch(int ch)
> if (likely(dma_chan[ch].callback != NULL))
> dma_chan[ch].callback(ch, csr, dma_chan[ch].data);
>
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> return 1;
> }
>
> @@ -1899,6 +1908,8 @@ static int omap2_dma_handle_ch(int ch)
> if (likely(dma_chan[ch].callback != NULL))
> dma_chan[ch].callback(ch, status, dma_chan[ch].data);
>
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> return 0;
> }
>
> @@ -1978,6 +1989,7 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> + dev = &pdev->dev;
> d = p->dma_attr;
> errata = p->errata;
>
> @@ -1999,6 +2011,11 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev)
> }
> }
>
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_set_autosuspend_delay(dev, 1000);
> + pm_runtime_enable(dev);
> + pm_runtime_get_sync(dev);
> +
> spin_lock_init(&dma_chan_lock);
> for (ch = 0; ch < dma_chan_count; ch++) {
> omap_clear_dma(ch);
> @@ -2064,6 +2081,16 @@ static int __devinit omap_system_dma_probe(struct platform_device *pdev)
> dma_chan[1].dev_id = 1;
> }
> p->show_dma_caps();
> +
> + /*
> + * Note: If dma channels are reserved through boot paramters,
> + * then dma device is always enabled.
> + */
> + if (omap_dma_reserve_channels)
> + pm_runtime_get(dev);
> +
> + pm_runtime_put_autosuspend(dev);
> +
> return 0;
>
> exit_dma_irq_fail:
^ permalink raw reply
* [PATCH 1/2] OMAP2420: mailbox: fix IVA vs DSP IRQ numbering
From: Tony Lindgren @ 2011-02-14 21:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297454203-28298-1-git-send-email-khilman@ti.com>
* Kevin Hilman <khilman@ti.com> [110211 11:55]:
> The IRQ numbering for the IVA and DSP mailboxes was switched due
> to the wrong ordering in the OMAP2 mbox list. Switch the ordering
> so DSP is first and matches all the other SoCs.
>
> Tested on OMAP2420/n810.
I'll queue both as fixes for the -rc cycle.
Tony
^ permalink raw reply
* [PATCH 5/5] omap4: Remove 'FIXME: omap44xx_sram_init not implemented'
From: Tony Lindgren @ 2011-02-14 21:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297510187-31547-6-git-send-email-santosh.shilimkar@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com> [110212 03:28]:
> The omap44xx_sram_init() implements functionality to push some
> code on SRAM whenever the code can't be executed from external
> memory. The low power and DVFS code can be executed from
> external DDR itself thanks to OMAP4 memory controller hardware
> support. So on OMAP4, sram_push kind of functionality isn't needed.
>
> Hence remove the FIXME warning added for implementing sram push
> feature on OMAP4.
Will take this one too.
Tony
^ permalink raw reply
* [PATCH 4/5] omap2plus: omap4: Set NR_CPU to 2 instead of default 4
From: Tony Lindgren @ 2011-02-14 21:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297510187-31547-5-git-send-email-santosh.shilimkar@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com> [110212 03:28]:
> The omap2plus_defconfig picks default NR_CPU value as 4 which isn't
> correct for OMAP4430. Available CPUs are ony 2, so fix the same.
I'll queue this for the upcoming merge window.
Tony
^ permalink raw reply
* [PATCH 2/5] omap4: Enable ARM local timers with OMAP4430 es1.0 exception
From: Tony Lindgren @ 2011-02-14 21:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297510187-31547-3-git-send-email-santosh.shilimkar@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com> [110212 03:28]:
> On OMAP4430 ES1.0 the local timers are gated by security. Enable the
> CONFIG_LOCAL_TIMERS for omap2plus build and handle the OMAP4430 es1.0
> exception case.
>
> This patch has dependency on the first patch in this series.
> ARM: smp: Select local timers vs dummy timer support runtime
>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: Tony Lindgren <tony@atomide.com>
> ---
> arch/arm/mach-omap2/Kconfig | 1 +
> arch/arm/mach-omap2/timer-mpu.c | 4 ++++
> 2 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 1a2cf62..f285dd7 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -44,6 +44,7 @@ config ARCH_OMAP4
> depends on ARCH_OMAP2PLUS
> select CPU_V7
> select ARM_GIC
> + select LOCAL_TIMERS
> select PL310_ERRATA_588369
> select ARM_ERRATA_720789
> select ARCH_HAS_OPP
> diff --git a/arch/arm/mach-omap2/timer-mpu.c b/arch/arm/mach-omap2/timer-mpu.c
> index 09c73dc..31c0ac4 100644
> --- a/arch/arm/mach-omap2/timer-mpu.c
> +++ b/arch/arm/mach-omap2/timer-mpu.c
> @@ -28,6 +28,10 @@
> */
> int __cpuinit local_timer_setup(struct clock_event_device *evt)
> {
> + /* Local timers are not supprted on OMAP4430 ES1.0 */
> + if (omap_rev() == OMAP4430_REV_ES1_0)
> + return -ENXIO;
> +
> evt->irq = OMAP44XX_IRQ_LOCALTIMER;
> twd_timer_setup(evt);
> return 0;
> --
> 1.6.0.4
>
^ permalink raw reply
* [PATCH 3/5] ARM: l2x0: Errata fix for flush by Way operation can cause data corruption
From: Andrei Warkentin @ 2011-02-14 21:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTinfx3eEgvPpeUAAxcrDOtQ+G_PkkoQH4V9E+N=7@mail.gmail.com>
On Mon, Feb 14, 2011 at 1:33 PM, Andrei Warkentin <andreiw@motorola.com> wrote:
>
> Fair enough, but you're doing it right now :-). I believe the smarter
> approach would be to start abstracting all accesses to secure-only
> resources (like the DCR reg). This would be your "hypervisor"
> interface. Then provide an implementation for your TI secure monitor.
> Obviously over time :).
>
Santosh,
Maybe this can influence you in some ways. I have this old patch
sitting around, although it does #ifdef around the TI stuff, simply
because I wasn't really interested in moving it out.
I group the errata by revs so it's simpler to see what you need and
what you don't. Obviously there aren't that many, but it does provide
a pattern to follow...
Thanks,
A
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-ARM-PL310-Cleanup-errata-handling-for-cache-controll.patch
Type: text/x-diff
Size: 9798 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110214/842681d4/attachment-0001.bin>
^ permalink raw reply
* MMC quirks relating to performance/lifetime.
From: Arnd Bergmann @ 2011-02-14 20:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTin=bkzvdQXoWUKRfX8Xs=4bXnxWf88TUG+VvjPD@mail.gmail.com>
On Monday 14 February 2011 20:29:59 Andrei Warkentin wrote:
> On Sun, Feb 13, 2011 at 11:39 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>
> Ah sorry, I had to look that one up myself, I thought it was the local
> jargon associated with the problem space :-). Program/Erase cycle.
Ok, makes sense.
> >> So T suggested for random data to better go into buffer A. How? Two suggestions.
> >> 1) Split smaller accesses into 8KB and write with reliable write.
> >> 2) Split smaller accesses into 8KB and write in reverse.
> >>
> >> The patch does both and I am verifying if that is really necessary. I
> >> need to go see the mmc spec and what it says about reliable write.
> >
> > I should add this to my test tool once I can reproduce it. If it turns
> > out that other media do the same, we can also trigger the same behavior
> > for those.
> >
>
> As I mentioned, I am checking with T right now on whether we can use
> suggestion (1) or
> suggestion (2) or if they need to be combined. The documentation we
> got was open to interpretation and the patch created from that did
> both.
> You mentioned that writing in reverse is not a good idea. Could you
> elaborate why? I would guess because you're always causing a write
> into a different AU (on these Toshiba cards), causing extra GC on
> every write?
Probably both the reliable write and writing small blocks in reverse
order will cause any card to do something that is different from
what it does on normal 64kb (or larger) aligned accesses.
There are multiple ways how this could be implemented:
1. Have one exception cache for all "special" blocks. This would normally
be for FAT32 subdirectory updates, which always write to the same
few blocks. This means you can do small writes efficiently anywhere
on the card, but only up to a (small) fixed number of block addresses.
If you overflow the table, the card still needs to go through an
extra PE for each new entry you write, in order to free up an entry.
2. Have a small number of AUs that can be in a special mode with efficient
small writes but inefficient large writes. This means that when you
alternate between small and large writes in the same AU, it has to go
through a PE on every switch. Similarly, if you do small writes to
more than the maximum number of AUs that can be held in this mode, you
get the same effect. This number can be as small as one, because that
is what FAT32 requires.
In both cases, you don't actually have a solution for the problem, you just
make it less likely for specific workloads.
Arnd
^ permalink raw reply
* [PATCH v4 0/5] ARM: omap[34]: Thumb-2 compatibility fixes
From: Nicolas Pitre @ 2011-02-14 20:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110214153726.GA20144@arm.com>
On Mon, 14 Feb 2011, Dave Martin wrote:
> On Mon, Feb 14, 2011 at 10:00:23AM -0500, Nicolas Pitre wrote:
> > > +#ifdef CONFIG_THUMB2_KERNEL
> > > + .align
> > > + bx pc
> > > + nop
> > > + .arm
> >
> > Also here, the .align has the potential to introduce a zero halfword in
> > the instruction stream before the bx. What about:
> >
> > adr r3, 1f
> > bx r3
> > .align
> > .arm
> > 1: ...
>
> .align inserts a 16-bit nop when misaligned in Thumb in a text section,
Ah, OK then. I didn't know about that. In ARM mode the all-zero bits
decode to an instruction that sort of does nothing, but this isn't an
eleguant nop, and that's what .align (used to?) insert as padding.
> and a word-aligned bx pc is a specific architecturally allowed way
> to do an inline switch to ARM. The linker uses this trick for PLT
> veneers etc.
Yep. It's just that with my suggestion the bx wasn't necessarily
aligned.
> A nicer fix for doing this sort of call from low-level code which
> might be ARM is to convert arch/arm/mm/*-v7.S to use "bx lr" to return.
>
> Generally, we can do this for all arches >= v5, without any
> incompatibility. However, since the need for it will be rare and it
> will generate patch noise for not much real benefit,
> I haven't proposed this.
This can be done in those places where this might be needed without
having to convert them all.
Nicolas
^ permalink raw reply
* [PATCH] Ensure predictable endian state on signal handler entry
From: Russell King - ARM Linux @ 2011-02-14 20:02 UTC (permalink / raw)
To: linux-arm-kernel
Ensure a predictable endian state when entering signal handlers. This
avoids programs which use SETEND to momentarily switch their endian
state from having their signal handlers entered with an unpredictable
endian state.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
--
arch/arm/kernel/signal.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 907d5a6..abaf844 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -474,7 +474,9 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka,
unsigned long handler = (unsigned long)ka->sa.sa_handler;
unsigned long retcode;
int thumb = 0;
- unsigned long cpsr = regs->ARM_cpsr & ~PSR_f;
+ unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT);
+
+ cpsr |= PSR_ENDSTATE;
/*
* Maybe we need to deliver a 32-bit signal to a 26-bit task.
^ permalink raw reply related
* [PATCH 3/5] ARM: l2x0: Errata fix for flush by Way operation can cause data corruption
From: Andrei Warkentin @ 2011-02-14 19:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <33573d5cfc91cf45dc58ee861cccc2ae@mail.gmail.com>
On Sun, Feb 13, 2011 at 11:08 PM, Santosh Shilimkar
<santosh.shilimkar@ti.com> wrote:
>> -----Original Message-----
>> From: Andrei Warkentin [mailto:andreiw at motorola.com]
>> Sent: Sunday, February 13, 2011 4:48 AM
>> To: Santosh Shilimkar
>> Cc: linux-omap at vger.kernel.org; Kevin Hilman; tony at atomide.com;
>> linux-arm-kernel at lists.infradead.org; Catalin Marinas
>> Subject: Re: [PATCH 3/5] ARM: l2x0: Errata fix for flush by Way
>> operation can cause data corruption
>>
>> On Sat, Feb 12, 2011 at 11:59 AM, Santosh Shilimkar
>> <santosh.shilimkar@ti.com> wrote:
>> >> -----Original Message-----
>> >> From: Andrei Warkentin [mailto:andreiw at motorola.com]
>> >> Sent: Saturday, February 12, 2011 11:20 PM
>> >> To: Santosh Shilimkar
>> >> Cc: linux-omap at vger.kernel.org; khilman at ti.com; tony at atomide.com;
>> >> linux-arm-kernel at lists.infradead.org; Catalin Marinas
>> >> Subject: Re: [PATCH 3/5] ARM: l2x0: Errata fix for flush by Way
>> >> operation can cause data corruption
>> >>
>> > [....]
>> >
>> >>
>> >> Can these PL310 errata fixes be made more generic? PL310 is
>> present
>> >> in
>> >> non-OMAP platforms too, which lack the TI hypervisor. And these
>> >> platforms might have the same PL310 rev, and suffer the same
>> >> glitches.
>> >> While ideally there is some kind of hypervisor_ops to modify the
>> >> protected register, at the very least there should be the generic
>> >> debug_write handling the ?"I ?can write all PL310 regs" case. If
>> >> you're interested I have a patch someplace that tried to do this,
>> >> hopefully I can still find it.
>> >
>> > They are kind of generic. If you look at it, the only change
>> > Which is arch specific is the implementation of "debug_writel"
>> function.
>> > Today this code is not in generic PL310 code, but
>> > OMAP specific.
>> >
>> > May be we can make this as exported function pointer, which
>> > arch's can populate.
>> >
>> > Will that work for you ?
>> >
>> > Regards,
>> > Santosh
>> >
>>
>> Ie something like the following.... what do you think???
>>
>> #define L2X0_DCR (0xF40)
>>
>> static void debug_writel(unsigned long val)
>> {
>> #ifdef CONFIG_ARCH_OMAP4
>> ? ? ? ?omap_smc1(0x100, val);
>> #else
>> ? ? ? ?writel_relaxed(val, l2x0_base + L2X0_DCR);
>> #endif
>> }
>> ...
> I understood that from first comment. But I am not in favor
> of polluting common ARM files with SOC specific #ifdeffery.
> We have gone over this when first errata support
> was added for PL310
>
> I have a better way to handle this scenario.
> Expect an updated patch for this.
>
> Regards,
> Santosh
>
Fair enough, but you're doing it right now :-). I believe the smarter
approach would be to start abstracting all accesses to secure-only
resources (like the DCR reg). This would be your "hypervisor"
interface. Then provide an implementation for your TI secure monitor.
Obviously over time :).
^ permalink raw reply
* MMC quirks relating to performance/lifetime.
From: Andrei Warkentin @ 2011-02-14 19:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <201102131839.08517.arnd@arndb.de>
On Sun, Feb 13, 2011 at 11:39 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> I don't think it needs to be boot-time, it can easily be run-time
> tuneable using sysfs, where you can configure it using an init script
> or some other logic from user space.
True, definitely expose the controls through sysfs.
>
> Yes.
>
> What is "PE" here?
>
Ah sorry, I had to look that one up myself, I thought it was the local
jargon associated with the problem space :-). Program/Erase cycle.
>> So T suggested for random data to better go into buffer A. How? Two suggestions.
>> 1) Split smaller accesses into 8KB and write with reliable write.
>> 2) Split smaller accesses into 8KB and write in reverse.
>>
>> The patch does both and I am verifying if that is really necessary. I
>> need to go see the mmc spec and what it says about reliable write.
>
> I should add this to my test tool once I can reproduce it. If it turns
> out that other media do the same, we can also trigger the same behavior
> for those.
>
As I mentioned, I am checking with T right now on whether we can use
suggestion (1) or
suggestion (2) or if they need to be combined. The documentation we
got was open to interpretation and the patch created from that did
both.
You mentioned that writing in reverse is not a good idea. Could you
elaborate why? I would guess because you're always causing a write
into a different AU (on these Toshiba cards), causing extra GC on
every write?
>> Basically, whatever behavior you choose is going to be wrong some set
>> of cards. Which is why tuning it probably only makes sense for eMMC
>> parts, and should be a set of runtime/compile-time quirks. What do you
>> think?
>
> Your explanation makes sense, but I'd definitely favor a run-time solution
> over compile-time or boot-time, because it would be much more flexible.
> We should also be able to find some optimizations that are universally
> good so we can always use them.
>
Then that's the angle I will pursue. It is the most flexible and then
you don't have to pollute the block driver with little workarounds for
soon-to-be-obsolete hardware. Hopefully I'll have something for
re-review soon.
Thanks Again!
^ permalink raw reply
* [PATCH] ARM: vfp: Always save VFP state in vfp_pm_suspend
From: Colin Cross @ 2011-02-14 18:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297683742.30092.11.camel@e102109-lin.cambridge.arm.com>
On Mon, Feb 14, 2011 at 3:42 AM, Catalin Marinas
<catalin.marinas@arm.com> wrote:
> On Sun, 2011-02-13 at 23:13 +0000, Colin Cross wrote:
>> vfp_pm_suspend should save the VFP state any time there is
>> a last_VFP_context. ?If it only saves when the VFP is enabled,
>> the state can get lost when, on a UP system:
>> ? ?Thread 1 uses the VFP
>> ? ?Context switch occurs to thread 2, VFP is disabled but the
>> ? ? ? VFP context is not saved to allow lazy save and restore
>> ? ?Thread 2 initiates suspend
>> ? ?vfp_pm_suspend is called with the VFP disabled, but the
>> ? ? ? context has not been saved.
>
> At this point is it guaranteed that the thread won't migrate to another
> CPU? If not, we should use get/put_cpu.
Yes, VFP suspend is implemented with a sysdev, which is suspended
after disable_nonboot_cpus.
>> --- a/arch/arm/vfp/vfpmodule.c
>> +++ b/arch/arm/vfp/vfpmodule.c
>> @@ -415,13 +415,12 @@ static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state)
>> ? ? ? ? struct thread_info *ti = current_thread_info();
>> ? ? ? ? u32 fpexc = fmrx(FPEXC);
>>
>> - ? ? ? /* if vfp is on, then save state for resumption */
>> - ? ? ? if (fpexc & FPEXC_EN) {
>> + ? ? ? /* save state for resume */
>> + ? ? ? if (last_VFP_context[ti->cpu]) {
>> ? ? ? ? ? ? ? ? printk(KERN_DEBUG "%s: saving vfp state\n", __func__);
>> - ? ? ? ? ? ? ? vfp_save_state(&ti->vfpstate, fpexc);
>> -
>> - ? ? ? ? ? ? ? /* disable, just in case */
>> - ? ? ? ? ? ? ? fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
>> + ? ? ? ? ? ? ? fmxr(FPEXC, fpexc | FPEXC_EN);
>> + ? ? ? ? ? ? ? vfp_save_state(last_VFP_context[ti->cpu], fpexc);
>> + ? ? ? ? ? ? ? fmxr(FPEXC, fpexc & ~FPEXC_EN);
>> ? ? ? ? }
>
> We may want to set the last_VFP_context to NULL so that after resuming
> (to the same thread) we force the VFP reload from the vfpstate
> structure. The vfp_support_entry code ignores the reloading if the
> last_VFP_context is the same as vfpstate.
Right, will fix.
^ permalink raw reply
* [PATCH v10 18/18] OMAP2, 3: DSS2: Get DSS IRQ from platform device
From: Semwal, Sumit @ 2011-02-14 18:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1297692573.2951.16.camel@deskari>
Hi,
On Mon, Feb 14, 2011 at 7:39 PM, Tomi Valkeinen <tomi.valkeinen@ti.com> wrote:
> Hi,
>
> On Mon, 2011-01-24 at 11:52 +0530, ext Sumit Semwal wrote:
>> From: Senthilvadivu Guruswamy <svadivu@ti.com>
>>
>> DSS IRQ number can be obtained from platform_get_irq(). ?This API in turn
>> picks the right IRQ number belonging to HW IP from the hwmod database.
>> So hardcoding of IRQ number could be removed.
>>
>> Reviewed-by: Paul Walmsley <paul@pwsan.com>
>> Reviewed-by: Kevin Hilman <khilman@ti.com>
>> Tested-by: Kevin Hilman <khilman@ti.com>
>> Signed-off-by: Senthilvadivu Guruswamy <svadivu@ti.com>
>> Signed-off-by: Sumit Semwal <sumit.semwal@ti.com>
>> ---
>> ?drivers/video/omap2/dss/dss.c | ? 21 ++++++++++++++-------
>> ?1 files changed, 14 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
>> index 4d7a816..f9390b4 100644
>> --- a/drivers/video/omap2/dss/dss.c
>> +++ b/drivers/video/omap2/dss/dss.c
>> @@ -563,7 +563,7 @@ void dss_set_dac_pwrdn_bgz(bool enable)
>>
>> ?static int dss_init(bool skip_init)
>> ?{
>> - ? ? int r;
>> + ? ? int r, dss_irq;
>> ? ? ? u32 rev;
>> ? ? ? struct resource *dss_mem;
>>
>> @@ -609,11 +609,18 @@ static int dss_init(bool skip_init)
>> ? ? ? REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); ? ? ?/* venc clock mode = normal */
>> ?#endif
>>
>> - ? ? r = request_irq(INT_24XX_DSS_IRQ,
>> - ? ? ? ? ? ? ? ? ? ? cpu_is_omap24xx()
>> - ? ? ? ? ? ? ? ? ? ? ? dss_irq_handler_omap2
>> - ? ? ? ? ? ? ? ? ? ? : dss_irq_handler_omap3,
>> - ? ? ? ? ? ? ? ? ? ? 0, "OMAP DSS", NULL);
>> + ? ? dss_irq = platform_get_irq(dss.pdev, 0);
>> + ? ? if (dss_irq < 0) {
>> + ? ? ? ? ? ? DSSERR("omap2 dss: platform_get_irq failed\n");
>> + ? ? ? ? ? ? r = -ENODEV;
>> + ? ? ? ? ? ? goto fail1;
>> + ? ? }
>> +
>> + ? ? r = request_irq(dss_irq,
>> + ? ? ? ? ? ? cpu_is_omap24xx()
>> + ? ? ? ? ? ? ? dss_irq_handler_omap2
>> + ? ? ? ? ? ? : dss_irq_handler_omap3,
>> + ? ? ? ? ? ? 0, "OMAP DSS", NULL);
>>
>> ? ? ? if (r < 0) {
>> ? ? ? ? ? ? ? DSSERR("omap2 dss: request_irq failed\n");
>> @@ -641,7 +648,7 @@ static int dss_init(bool skip_init)
>> ? ? ? return 0;
>>
>> ?fail2:
>> - ? ? free_irq(INT_24XX_DSS_IRQ, NULL);
>> + ? ? free_irq(dss_irq, NULL);
>
> You change this free_irq, as you should, but you missed another a few
> lines later.
>
> Archit fixes it in his "OMAP: DSS2: Common IRQ handler for all OMAPs"
> patch silently, but I think it should be fixed here already, as the
> patches haven't been pulled to anywhere yet.
>
> Perhaps you could use the same method as Archit used, create a new field
> "irq" in the dss struct which can be used in the dss_exit's free_irq
> call.
Yes, you're right; I guess since it was already in your tree by the
time we noticed this oversight, we assumed Archit could submit a new
one. I can send you an updated one tomorrow for this.
Regards,
~Sumit.
>
> ?Tomi
>
>
>
^ permalink raw reply
* [PATCH 2/4] arm: configs: update mmp2 defconfig for brownstone and SoC Selection
From: Nicolas Pitre @ 2011-02-14 18:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <04A50A49-CBBC-4E5D-B19E-391B4DF77DF3@marvell.com>
On Sun, 13 Feb 2011, Philip Rakity wrote:
>
> took old mmp2_defconfig --
> did make menuconfig
> changed to CPU_MMP2
> added brownstone as supported board
>
> Signed-off-by: Philip Rakity <prakity@marvell.com>
> ---
> arch/arm/configs/mmp2_defconfig | 1207 ++++++++++++++++++++++++++++++++++++++-
> 1 files changed, 1203 insertions(+), 4 deletions(-)
That's not good.
You should do a "make savedefconfig" which will produce a ./defconfig
file which is a reduced version of your config. Then use that instead
of the .config file to update your default config.
Nicolas
^ permalink raw reply
* [PATCH 2/2] mach-mmp: Support for SD/MMC clock adjustment in brownstone
From: Philip Rakity @ 2011-02-14 17:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110214154601.GA31103@n2100.arm.linux.org.uk>
The two patches that use this change are enclosed -- submitted to linux-mmc mailing list.
MMP2 / PXA168 / PXA910 have private registers that allow us to fine grain adjust the
SD clock internal delay and method of feedback. RESET_ALL clears the private
registers so they need resetting. The values needed for fine grain adjustment
depend on the board design. Sometimes no adjustment is needed; sometimes
other values are needed. Doing SDR50 (100 mbps) and DDR (Dual Data Rate) makes
it sometimes necessary to set these values.
One change not enclosed was the modification to sdhci.c to allow pre and post callbacks
when a reset was done. It added call backs into the platform specific driver.
Philip
====
[PATCH 2/2] sdhci: sdhci-pxa.c: Add post reset processing for chip specific registers
RESET_ALL resets chips private registers.
Reset to values specified in board file.
depends on board specific mach-mmp/plat-pxa sdhci.h definitions
Signed-off-by: Philip Rakity <prakity@marvell.com>
---
drivers/mmc/host/sdhci-pxa.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 42 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
index 215517f..0e64d66 100644
--- a/drivers/mmc/host/sdhci-pxa.c
+++ b/drivers/mmc/host/sdhci-pxa.c
@@ -29,6 +29,13 @@
#define DRIVER_NAME "sdhci-pxa"
+#define SD_CLOCK_AND_BURST_SIZE_SETUP 0x10A
+#define SDCLK_DELAY_MASK 0x1F
+#define SDCLK_SEL_MASK 0x1
+#define SDCLK_DELAY_SHIFT 9
+#define SDCLK_SEL_SHIFT 8
+
+
struct sdhci_pxa {
struct sdhci_host *host;
struct sdhci_pxa_platdata *pdata;
@@ -53,7 +60,42 @@ static void enable_clock(struct sdhci_host *host)
}
}
+static void set_clock_and_burst_size(struct sdhci_host *host)
+{
+ u16 tmp;
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ pr_debug("%s:%s: adjust = %d\n",
+ __func__, mmc_hostname(host->mmc), pxa->pdata->adjust_clocks);
+
+ if (pxa->pdata->adjust_clocks) {
+ tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X, "
+ "delay = %d, sel = %d\n",
+ __func__, mmc_hostname(host->mmc), tmp,
+ pxa->pdata->clk_delay, pxa->pdata->clk_select);
+ tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+ tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+ tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
+ SDCLK_DELAY_SHIFT;
+ tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
+ SDCLK_SEL_SHIFT;
+ writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST_SIZE_SETUP = %04X\n",
+ __func__, mmc_hostname(host->mmc), tmp);
+ }
+}
+
+static void platform_reset_exit(struct sdhci_host *host, u8 mask)
+{
+ if (mask == SDHCI_RESET_ALL) {
+ /* reset private registers */
+ set_clock_and_burst_size(host);
+ }
+}
+
static struct sdhci_ops sdhci_pxa_ops = {
+ .platform_reset_exit = platform_reset_exit,
};
/*****************************************************************************\
--
1.7.0.4
========
[PATCH] sdhci: Add support PXA168, PXA910, and MMP2 controllers
======
Select pxa controller based on CPU model: MMP2, PXA168, PXA910
Three new SoC specific files added:
sdhci-pxa168.c
sdhci-pxa910.c
sdhci-mmp2.c
These files control the platform specific behavior of the SD controller.
MMP2 and the PXAxxx controllers use different hardware registers
to control platform specific behavior. Platform flags come from
arch/mach-mmp/ files for board design (brownstone, jasper, aspenite etc)
settings (8 bit capable slot / card permanent).
quirks for SD/SoC specific behaviro defined in specific platform files
(sdhci-pxa168.c, sdhci-pxa910.c, sdhci-mmp2.c)
The correct SD controller is now shown Kconfig.
sdhci-pxa.c changed to act as a shim for the platform specific code.
Only generic operations are handled in sdhci-pxa.c. All
platform operations are passed to the platform code.
reset_enter() and reset_exit() used for silicon control.
Signed-off-by: Philip Rakity <prakity@marvell.com>
---
drivers/mmc/host/Kconfig | 42 +++++-
drivers/mmc/host/Makefile | 5 +-
drivers/mmc/host/sdhci-mmp2.c | 268 ++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-pxa.c | 154 +++---------------
drivers/mmc/host/sdhci-pxa.h | 65 ++++++++
drivers/mmc/host/sdhci-pxa168.c | 344 +++++++++++++++++++++++++++++++++++++++
drivers/mmc/host/sdhci-pxa910.c | 273 +++++++++++++++++++++++++++++++
7 files changed, 1012 insertions(+), 139 deletions(-)
create mode 100644 drivers/mmc/host/sdhci-mmp2.c
create mode 100644 drivers/mmc/host/sdhci-pxa.h
create mode 100644 drivers/mmc/host/sdhci-pxa168.c
create mode 100644 drivers/mmc/host/sdhci-pxa910.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 21c21d2..577e7ed 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -178,18 +178,52 @@ config MMC_SDHCI_S3C
If unsure, say N.
+
+config MMC_SDHCI_PXA_CORE
+ tristate
+ help
+ This is silent Kconfig symbol that is selected by the drivers that
+ need PXA driver base support
+
+
config MMC_SDHCI_PXA
- tristate "Marvell PXA168/PXA910/MMP2 SD Host Controller support"
- depends on ARCH_PXA || ARCH_MMP
+ tristate "Marvell MMP2 SD Host Controller support"
+ depends on CPU_MMP2
+ select MMC_SDHCI
+ select MMC_SDHCI_PXA_CORE
+ help
+ This selects the Marvell(R) MMP2 SD Host Controller.
+ If you have a MMP2 platform with SD Host Controller
+ and a card slot, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_SDHCI_PXA9xx
+ tristate "Marvell PXA9xx SD Host Controller support"
+ depends on CPU_PXA910
select MMC_SDHCI
+ select MMC_SDHCI_PXA_CORE
+ help
+ This selects the Marvell(R) PXA910 SD Host Controller.
+ If you have a PXA910 platform with SD Host Controller
+ and a card slot, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_SDHCI_PXA168
+ tristate "Marvell PXA168 SD Host Controller support"
+ depends on CPU_PXA168
+ select MMC_SDHCI
+ select MMC_SDHCI_PXA_CORE
select MMC_SDHCI_IO_ACCESSORS
help
- This selects the Marvell(R) PXA168/PXA910/MMP2 SD Host Controller.
- If you have a PXA168/PXA910/MMP2 platform with SD Host Controller
+ This selects the Marvell(R) PXA168 SD Host Controller.
+ If you have a PXA168 platform with SD Host Controller
and a card slot, say Y or M here.
If unsure, say N.
+
config MMC_SDHCI_SPEAR
tristate "SDHCI support on ST SPEAr platform"
depends on MMC_SDHCI && PLAT_SPEAR
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e834fb2..2d61850 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -8,7 +8,10 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
-obj-$(CONFIG_MMC_SDHCI_PXA) += sdhci-pxa.o
+obj-$(CONFIG_MMC_SDHCI_PXA_CORE) += sdhci-pxa.o
+obj-$(CONFIG_MMC_SDHCI_PXA) += sdhci-mmp2.o
+obj-$(CONFIG_MMC_SDHCI_PXA168) += sdhci-pxa168.o
+obj-$(CONFIG_MMC_SDHCI_PXA9xx) += sdhci-pxa910.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
diff --git a/drivers/mmc/host/sdhci-mmp2.c b/drivers/mmc/host/sdhci-mmp2.c
new file mode 100644
index 0000000..fbe5e28
--- /dev/null
+++ b/drivers/mmc/host/sdhci-mmp2.c
@@ -0,0 +1,268 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ * Philip Rakity <prakity@marvell.com>
+ * Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <plat/sdhci.h>
+#include "sdhci-pxa.h"
+#include "sdhci.h"
+
+#define DRIVER_NAME "sdhci-mmp2"
+
+#define SD_CFG_FIFO_PARAM 0x100
+#define SDCFG_GEN_PAD_CLK_ON (1<<6)
+
+#define SD_CLOCK_AND_BURST_SIZE_SETUP 0x10A
+#define SDCLK_DELAY_MASK 0x1F
+#define SDCLK_SEL_MASK 0x1
+#define SDCLK_DELAY_SHIFT 9
+#define SDCLK_SEL_SHIFT 8
+
+#define SD_CE_ATA_2 0x10E
+#define SDCE_MISC_INT (1<<2)
+#define SDCE_MISC_INT_EN (1<<1)
+
+#define DISABLE_CLOCK_GATING 0
+
+
+static int platform_mmp2_probe(struct sdhci_host *host);
+
+/*
+ * MMC spec calls for the host to send 74 clocks to the card
+ * during initialization, right after voltage stabilization.
+ * the pxa168 controller has no easy way to generate those clocks.
+ * create the clocks manually right here.
+ */
+static void generate_initial_74_clocks(struct sdhci_host *host, u8 power_mode)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+ u16 tmp;
+ int count;
+
+ if (pxa->power_mode == MMC_POWER_UP
+ && power_mode == MMC_POWER_ON) {
+
+ pr_debug("%s:%s ENTER: slot->power_mode = %d,"
+ "ios->power_mode = %d\n",
+ __func__,
+ mmc_hostname(host->mmc),
+ pxa->power_mode,
+ power_mode);
+
+ /* set we want notice of when 74 clocks are sent */
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ tmp |= SDCE_MISC_INT_EN;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+
+ /* start sending the 74 clocks */
+ tmp = readw(host->ioaddr + SD_CFG_FIFO_PARAM);
+ tmp |= SDCFG_GEN_PAD_CLK_ON;
+ writew(tmp, host->ioaddr + SD_CFG_FIFO_PARAM);
+
+ /* slowest speed is about 100KHz or 10usec per clock */
+ udelay(740);
+ count = 0;
+#define MAX_WAIT_COUNT 5
+ while (count++ < MAX_WAIT_COUNT) {
+ if ((readw(host->ioaddr + SD_CE_ATA_2)
+ & SDCE_MISC_INT) == 0)
+ break;
+ udelay(10);
+ }
+
+ if (count == MAX_WAIT_COUNT)
+ printk(KERN_WARNING"%s: %s: 74 clock interrupt "
+ "not cleared\n",
+ __func__, mmc_hostname(host->mmc));
+ /* clear the interrupt bit if posted */
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ tmp |= SDCE_MISC_INT;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+ }
+ pxa->power_mode = power_mode;
+}
+
+static void set_clock_and_burst_size(struct sdhci_host *host)
+{
+ u16 tmp;
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ pr_debug("%s:%s: adjust = %d\n",
+ __func__, mmc_hostname(host->mmc), pxa->pdata->adjust_clocks);
+
+ if (pxa->pdata->adjust_clocks) {
+ tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X, "
+ "delay = %d, sel = %d\n",
+ __func__, mmc_hostname(host->mmc), tmp,
+ pxa->pdata->clk_delay, pxa->pdata->clk_select);
+ tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+ tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+ tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
+ SDCLK_DELAY_SHIFT;
+ tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
+ SDCLK_SEL_SHIFT;
+ writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST_SIZE_SETUP = %04X\n",
+ __func__, mmc_hostname(host->mmc), tmp);
+ }
+}
+
+static void programFIFO(struct sdhci_host *host, int enable)
+{
+ unsigned short tmp;
+
+ tmp = readw(host->ioaddr + SDHCI_HOST_CONTROL_2);
+
+ if (enable)
+ tmp |= SDCTRL_2_ASYNC_INT_EN;
+ else
+ tmp &= ~SDCTRL_2_ASYNC_INT_EN;
+
+ writew(tmp, host->ioaddr + SDHCI_HOST_CONTROL_2);
+}
+
+static void platform_reset_exit(struct sdhci_host *host, u8 mask)
+{
+ if (mask == SDHCI_RESET_ALL) {
+ /* reset private registers */
+ programFIFO(host, DISABLE_CLOCK_GATING);
+ set_clock_and_burst_size(host);
+ }
+}
+
+
+#ifdef CONFIG_MMC_CLKGATE
+static void platform_hw_clk_gate(struct sdhci_host *host)
+{
+ int enable;
+
+ enable = host->mmc->clk_gated;
+ programFIFO(host, enable);
+ pr_debug("%s:%s: enable = %d\n",
+ __func__, mmc_hostname(host->mmc), enable);
+}
+#endif
+
+static unsigned int get_f_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ pr_debug("%s:%s f_max = %d\n",
+ __func__, mmc_hostname(host->mmc),
+ pxa->pdata->max_speed);
+
+ return pxa->pdata->max_speed;
+}
+
+static unsigned int set_signaling_voltage(struct sdhci_host *host,
+ unsigned int uhs)
+{
+ u16 con;
+
+ pr_debug("%s:%s\n", __func__, mmc_hostname(host->mmc));
+ /*
+ * Set V18_EN -- DDR does not work without this.
+ * does not change signaling voltage
+ */
+ con = readw(host->ioaddr + SDHCI_HOST_CONTROL_2);
+ if (uhs != MMC_SDR_MODE)
+ con |= SDCTRL_2_SDH_V18_EN;
+ else
+ con &= ~SDCTRL_2_SDH_V18_EN;
+ writew(con, host->ioaddr + SDHCI_HOST_CONTROL_2);
+ return 0;
+}
+
+struct sdhci_pxa_data sdhci_platform_data = {
+ .ops = {
+ .platform_reset_exit = platform_reset_exit,
+ .platform_send_init_74_clocks = generate_initial_74_clocks,
+ .set_signaling_voltage = set_signaling_voltage,
+ .get_f_max_clock = NULL,
+#ifdef CONFIG_MMC_CLKGATE
+ .platform_hw_clk_gate = platform_hw_clk_gate,
+#endif
+ },
+#ifdef CONFIG_MMC_CLKGATE
+ .mmc_caps = MMC_CAP_HW_CLOCK_GATING | MMC_CAP_BUS_WIDTH_TEST,
+#else
+ .mmc_caps = MMC_CAP_BUS_WIDTH_TEST,
+#endif
+ .platform_probe = platform_mmp2_probe,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE
+ | SDHCI_QUIRK_32BIT_ADMA_SIZE
+ | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+};
+EXPORT_SYMBOL_GPL(sdhci_platform_data);
+
+static int platform_mmp2_probe(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+ struct sdhci_pxa_platdata *pdata = pxa->pdata;
+ struct sdhci_ops *p_ops;
+
+ p_ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+ if (!p_ops) {
+ printk(KERN_ERR "no memory");
+ return -ENOMEM;
+ }
+
+ /*
+ * we cannot directly copy our operations into host->ops
+ * since it is read only. So we do this indirectly.
+ */
+ memcpy((void *)p_ops, (void *)&sdhci_platform_data.ops,
+ sizeof(struct sdhci_ops));
+
+ if (pxa->pdata->max_speed)
+ p_ops->get_f_max_clock = get_f_max_clock;
+
+ host->quirks |= sdhci_platform_data.quirks;
+ host->mmc->caps |= sdhci_platform_data.mmc_caps;
+
+ /* If slot design supports 8 bit data, indicate this to MMC. */
+ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+
+ if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+
+ pr_debug("%s:%s: host->quirks = %08X, mmc->caps = %08lX\n",
+ __func__, mmc_hostname(host->mmc),
+ host->quirks, host->mmc->caps);
+
+ memcpy((void *)host->ops, (void *)p_ops, sizeof(struct sdhci_ops));
+ kfree(p_ops);
+ return 0;
+}
diff --git a/drivers/mmc/host/sdhci-pxa.c b/drivers/mmc/host/sdhci-pxa.c
index e736727..55774cb 100644
--- a/drivers/mmc/host/sdhci-pxa.c
+++ b/drivers/mmc/host/sdhci-pxa.c
@@ -18,33 +18,21 @@
* Refer to sdhci-s3c.c.
*/
+#include <linux/module.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/err.h>
+#include <linux/slab.h>
#include <plat/sdhci.h>
+#include "sdhci-pxa.h"
+#include <mach/cputype.h>
#include "sdhci.h"
#define DRIVER_NAME "sdhci-pxa"
-#define SD_CLOCK_AND_BURST_SIZE_SETUP 0x10A
-#define SDCLK_DELAY_MASK 0x1F
-#define SDCLK_SEL_MASK 0x1
-#define SDCLK_DELAY_SHIFT 9
-#define SDCLK_SEL_SHIFT 8
-
-#define DISABLE_CLOCK_GATING 0
-
-struct sdhci_pxa {
- struct sdhci_host *host;
- struct sdhci_pxa_platdata *pdata;
- struct clk *clk;
- struct resource *res;
-
- u8 clk_enable;
-};
/*****************************************************************************\
* *
@@ -61,97 +49,6 @@ static void enable_clock(struct sdhci_host *host)
}
}
-static void set_clock_and_burst_size(struct sdhci_host *host)
-{
- u16 tmp;
- struct sdhci_pxa *pxa = sdhci_priv(host);
-
- pr_debug("%s:%s: adjust = %d\n",
- __func__, mmc_hostname(host->mmc), pxa->pdata->adjust_clocks);
-
- if (pxa->pdata->adjust_clocks) {
- tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
- pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X, "
- "delay = %d, sel = %d\n",
- __func__, mmc_hostname(host->mmc), tmp,
- pxa->pdata->clk_delay, pxa->pdata->clk_select);
- tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
- tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
- tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
- SDCLK_DELAY_SHIFT;
- tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
- SDCLK_SEL_SHIFT;
- writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
- pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST_SIZE_SETUP = %04X\n",
- __func__, mmc_hostname(host->mmc), tmp);
- }
-}
-
-static void programFIFO(struct sdhci_host *host, int enable)
-{
- unsigned short tmp;
-
- tmp = readw(host->ioaddr + SDHCI_HOST_CONTROL_2);
-
- if (enable)
- tmp |= SDCTRL_2_ASYNC_INT_EN;
- else
- tmp &= ~SDCTRL_2_ASYNC_INT_EN;
-
- writew(tmp, host->ioaddr + SDHCI_HOST_CONTROL_2);
-}
-
-static void platform_reset_exit(struct sdhci_host *host, u8 mask)
-{
- if (mask == SDHCI_RESET_ALL) {
- /* reset private registers */
- programFIFO(host, DISABLE_CLOCK_GATING);
- set_clock_and_burst_size(host);
- }
-}
-
-static unsigned int get_f_max_clock(struct sdhci_host *host)
-{
- struct sdhci_pxa *pxa = sdhci_priv(host);
-
- return pxa->pdata->max_speed;
-}
-
-static unsigned int set_signaling_voltage(struct sdhci_host *host,
- unsigned int ddr)
-{
- u16 con;
-
- pr_debug("%s:%s\n", __func__, mmc_hostname(host->mmc));
- /*
- * Set V18_EN -- DDR does not work without this.
- * does not change signaling voltage
- */
- con = readw(host->ioaddr + SDHCI_HOST_CONTROL_2);
- con |= SDCTRL_2_SDH_V18_EN;
- writew(con, host->ioaddr + SDHCI_HOST_CONTROL_2);
- return 0;
-}
-
-#ifdef CONFIG_MMC_CLKGATE
-static void platform_hw_clk_gate(struct sdhci_host *host)
-{
- int enable;
-
- enable = host->mmc->clk_gated;
- programFIFO(host, enable);
-}
-#endif
-
-static struct sdhci_ops sdhci_pxa_ops = {
- .platform_reset_exit = platform_reset_exit,
- .set_signaling_voltage = set_signaling_voltage,
- .get_f_max_clock = NULL,
-#ifdef CONFIG_MMC_CLKGATE
- .platform_hw_clk_gate = platform_hw_clk_gate,
-#endif
-};
-
/*****************************************************************************\
* *
* Device probing/removal *
@@ -213,38 +110,23 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
}
host->hw_name = "MMC";
- host->ops = &sdhci_pxa_ops;
- host->irq = irq;
- host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
- host->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE
- | SDHCI_QUIRK_32BIT_ADMA_SIZE
- | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
-
- if (pdata->quirks)
- host->quirks |= pdata->quirks;
-
-#ifdef CONFIG_MMC_CLKGATE
- host->mmc->caps |= MMC_CAP_HW_CLOCK_GATING | MMC_CAP_BUS_WIDTH_TEST;
-#else
- host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST;
-#endif
-
- /* If slot design supports 8 bit data, indicate this to MMC. */
- if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
- host->mmc->caps |= MMC_CAP_8_BIT_DATA;
-
- if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
- host->mmc->caps |= MMC_CAP_NONREMOVABLE;
- host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ host->ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+ if (!host->ops) {
+ dev_err(&pdev->dev, "no memory for host->ops\n");
+ ret = -ENOMEM;
+ goto out;
}
+ host->irq = irq;
+ host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
/* do not rely on u-boot to enable the clocks */
enable_clock(host);
- if (pxa->pdata->max_speed)
- sdhci_pxa_ops.get_f_max_clock = get_f_max_clock;
- else
- sdhci_pxa_ops.get_f_max_clock = NULL;
+ if (sdhci_platform_data.platform_probe) {
+ ret = sdhci_platform_data.platform_probe(host);
+ if (ret)
+ goto out;
+ }
ret = sdhci_add_host(host);
if (ret) {
@@ -258,6 +140,7 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
out:
if (host) {
clk_put(pxa->clk);
+ kfree(host->ops);
if (host->ioaddr)
iounmap(host->ioaddr);
if (pxa->res)
@@ -290,6 +173,7 @@ static int __devexit sdhci_pxa_remove(struct platform_device *pdev)
resource_size(pxa->res));
clk_put(pxa->clk);
+ kfree(host->ops);
sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
}
@@ -297,6 +181,7 @@ static int __devexit sdhci_pxa_remove(struct platform_device *pdev)
return 0;
}
+
#ifdef CONFIG_PM
static int sdhci_pxa_suspend(struct platform_device *dev, pm_message_t state)
{
@@ -348,4 +233,5 @@ module_exit(sdhci_pxa_exit);
MODULE_DESCRIPTION("SDH controller driver for PXA168/PXA910/MMP2");
MODULE_AUTHOR("Zhangfei Gao <zhangfei.gao@marvell.com>");
+MODULE_AUTHOR("Philp Rakity <prakity@marvell.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pxa.h b/drivers/mmc/host/sdhci-pxa.h
new file mode 100644
index 0000000..4161fc1
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxa.h
@@ -0,0 +1,65 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ * Philip Rakity <prakity@marvell.com>
+ * Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+/* Supports:
+ * SDHCI support for MMP2/PXA910/PXA168
+ *
+ * Refer to sdhci-s3c.c.
+ */
+
+#ifndef __SDHCI_PXA_H
+#define __SDHCI_PXA_H
+
+#include <linux/types.h>
+#include "sdhci.h"
+
+struct sdhci_pxa {
+ struct sdhci_host *host;
+ struct sdhci_pxa_platdata *pdata;
+ struct clk *clk;
+ struct resource *res;
+
+ u32 delay_in_ms;
+ u32 delay_in_us;
+ u32 delay_in_ns;
+
+ u8 clk_enable;
+ u8 power_mode;
+};
+
+struct sdhci_pxa_data {
+ struct sdhci_ops ops;
+ unsigned int quirks;
+ unsigned int mmc_caps;
+ int (*platform_probe) (struct sdhci_host *host);
+};
+
+extern struct sdhci_pxa_data sdhci_platform_data;
+extern void sdhci_free_host(struct sdhci_host *host);
+extern void sdhci_remove_host(struct sdhci_host *host, int dead);
+extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
+ size_t priv_size);
+
+#endif /* __SDHCI_PXA_H */
diff --git a/drivers/mmc/host/sdhci-pxa168.c b/drivers/mmc/host/sdhci-pxa168.c
new file mode 100644
index 0000000..e6ae146
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxa168.c
@@ -0,0 +1,344 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ * Philip Rakity <prakity@marvell.com>
+ * Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <plat/sdhci.h>
+#include "sdhci-pxa.h"
+#include "sdhci.h"
+
+#define DRIVER_NAME "sdhci-pxa168"
+
+#define SD_FIFO_PARAM 0xE0
+#define DIS_PAD_SD_CLK_GATE (1<<10) /* Turn on/off Dynamic Clock Gating */
+
+#define SD_CLOCK_AND_BURST_SIZE_SETUP 0xE6
+#define SDCLK_DELAY_MASK 0xF
+#define SDCLK_SEL_MASK 0x3
+#define SDCLK_DELAY_SHIFT 10
+#define SDCLK_SEL_SHIFT 8
+
+#define SD_CE_ATA_2 0xEA
+#define SDCE_MMC_WIDTH (1<<8)
+#define SDCE_MMC_CARD (1<<12)
+
+#define DISABLE_CLOCK_GATING 0
+
+static int platform_pxa168_probe(struct sdhci_host *host);
+
+/*
+ * MMC spec calls for the host to send 74 clocks to the card
+ * during initialization, right after voltage stabilization.
+ * the pxa168 controller has no easy way to generate those clocks.
+ * create the clocks manually right here.
+ */
+#if 0
+static void generate_init_clocks(struct sdhci_host *host, u8 power_mode)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+ struct pfn_cfg *cfg = pxa->pdata->pfn_table;
+ mfp_cfg_t clk_pin;
+ int i;
+
+
+ if (cfg == NULL) {
+ DBG("Cannot generate init clocks!\n");
+ return;
+ }
+
+ if (pxa->power_mode == MMC_POWER_UP
+ && power_mode == MMC_POWER_ON) {
+
+ DBG("%s: ENTER %s: power_mode = %d, ios_.power_mode = %d\n",
+ __func__,
+ mmc_hostname(host->mmc),
+ pxa->power_mode,
+ power_mode);
+ /* CMD/CLK pin to gpio mode. */
+ mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CMD), 1);
+ mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK), 1);
+
+ /* ensure at least 1/2 period stable to prevent runt pulse.*/
+ udelay(3);
+
+ clk_pin = *(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK));
+ if (gpio_request(MFP_PIN(clk_pin), "MMC_CLK")) {
+ printk(KERN_ERR "Cannot obtain MMC_CLK GPIO %ld\n",
+ MFP_PIN(clk_pin));
+ goto err;
+ }
+
+ DBG("Generating init clocks on pins CLK %ld\n",
+ MFP_PIN(clk_pin));
+
+ for (i = 0; i < INIT_CLOCKS; i++) {
+ gpio_direction_output(MFP_PIN(clk_pin), 0); /* low */
+ udelay(3);
+ gpio_direction_output(MFP_PIN(clk_pin), 1); /* high */
+ udelay(3);
+ }
+
+ gpio_free(MFP_PIN(clk_pin));
+ }
+
+err:
+ pxa->power_mode = power_mode;
+
+ /* CMD/CLK pin back MMC mode. */
+ mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CMD), 1);
+ mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CLK), 1);
+}
+#endif
+
+/*
+ * we cannot talk to controller for 8 bus cycles according to sdio spec
+ * at lowest speed this is 100,000 HZ per cycle or 800,000 cycles
+ * which is quite a LONG TIME on a fast cpu -- so delay if needed
+ */
+static void platform_specific_delay(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ mdelay(pxa->delay_in_ms);
+ udelay(pxa->delay_in_us);
+ ndelay(pxa->delay_in_ns);
+}
+
+static void set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ if (clock != 0) {
+ pxa->delay_in_ns = (1000000000/clock);
+
+ /* need to delay 12 clocks for 8787/8786 */
+ /* need to delay 8 clocks for controller -- so just use 12 */
+
+ pxa->delay_in_ns = pxa->delay_in_ns * 12;
+
+ pxa->delay_in_ms = pxa->delay_in_ns / 1000000;
+ pxa->delay_in_ns = pxa->delay_in_ns % 1000000;
+ pxa->delay_in_us = pxa->delay_in_ns / 1000;
+ pxa->delay_in_ns = pxa->delay_in_ns % 1000;
+ } else {
+ pxa->delay_in_ns = 0;
+ pxa->delay_in_us = 0;
+ pxa->delay_in_ms = 0;
+ }
+}
+
+static void set_clock_and_burst_size(struct sdhci_host *host)
+{
+ u16 tmp;
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ if (pxa->pdata->adjust_clocks) {
+ tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X"
+ ", delay = %d, sel = %d\n",
+ __func__, mmc_hostname(host->mmc), tmp,
+ pxa->pdata->clk_delay, pxa->pdata->clk_select);
+ tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+ tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+ tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
+ SDCLK_DELAY_SHIFT;
+ tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
+ SDCLK_SEL_SHIFT;
+ writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST = %04X\n",
+ __func__, mmc_hostname(host->mmc), tmp);
+ }
+}
+
+static void programFIFO(struct sdhci_host *host, int enable)
+{
+ unsigned short tmp;
+
+ tmp = readw(host->ioaddr + SD_FIFO_PARAM);
+ if (enable)
+ tmp &= ~DIS_PAD_SD_CLK_GATE;
+ else
+ tmp |= DIS_PAD_SD_CLK_GATE;
+ writew(tmp, host->ioaddr + SD_FIFO_PARAM);
+}
+
+static void platform_reset_enter(struct sdhci_host *host, u8 mask)
+{
+ /* Before RESET_DATA we need to wait at least 10 sd cycles */
+ if (mask == SDHCI_RESET_DATA)
+ platform_specific_delay(host);
+}
+
+
+static void platform_reset_exit(struct sdhci_host *host, u8 mask)
+{
+ if (mask == SDHCI_RESET_ALL) {
+ /* reset private registers */
+ programFIFO(host, DISABLE_CLOCK_GATING);
+ set_clock_and_burst_size(host);
+ }
+}
+
+
+#ifdef CONFIG_MMC_CLKGATE
+static void platform_hw_clk_gate(struct sdhci_host *host)
+{
+ int enable;
+
+ enable = host->mmc->clk_gated;
+ programFIFO(host, enable);
+ pr_debug("%s:%s: enable = %d\n",
+ __func__, mmc_hostname(host->mmc), enable);
+}
+#endif
+
+static unsigned int get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ pr_debug("%s:%s clk_get_rate = %lu\n",
+ __func__, mmc_hostname(host->mmc),
+ clk_get_rate(pxa->clk));
+
+ return clk_get_rate(pxa->clk);
+}
+
+static unsigned int get_f_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ pr_debug("%s:%s f_max = %d\n",
+ __func__, mmc_hostname(host->mmc),
+ pxa->pdata->max_speed);
+
+ return pxa->pdata->max_speed;
+}
+
+static int platform_supports_8_bit(struct sdhci_host *host, int width)
+{
+ u16 tmp;
+
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ if (width != 8)
+ tmp &= ~(SDCE_MMC_CARD | SDCE_MMC_WIDTH);
+ else
+ tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+ return 0;
+}
+
+static u16 pxa168_readw(struct sdhci_host *host, int reg)
+{
+ u32 temp;
+
+ if (reg == SDHCI_HOST_VERSION) {
+ temp = readl(host->ioaddr + SDHCI_HOST_VERSION - 2) >> 16;
+ return temp & 0xffff;
+ }
+
+ return readw(host->ioaddr + reg);
+}
+
+struct sdhci_pxa_data sdhci_platform_data = {
+ .ops = {
+ .platform_reset_enter = platform_reset_enter,
+ .platform_reset_exit = platform_reset_exit,
+ .get_max_clock = get_max_clock,
+ .set_clock = set_clock,
+ .platform_specific_delay = platform_specific_delay,
+ .platform_send_init_74_clocks = NULL,
+ .get_f_max_clock = NULL,
+ .platform_8bit_width = NULL,
+#ifdef CONFIG_MMC_CLKGATE
+ .platform_hw_clk_gate = platform_hw_clk_gate,
+#endif
+ .read_w = pxa168_readw,
+ },
+#ifdef CONFIG_MMC_CLKGATE
+ .mmc_caps = MMC_CAP_HW_CLOCK_GATING | MMC_CAP_BUS_WIDTH_TEST,
+#else
+ .mmc_caps = MMC_CAP_BUS_WIDTH_TEST,
+#endif
+ .platform_probe = platform_pxa168_probe,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE
+ | SDHCI_QUIRK_32BIT_ADMA_SIZE
+ | SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN
+ | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+
+};
+EXPORT_SYMBOL_GPL(sdhci_platform_data);
+
+static int platform_pxa168_probe(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+ struct sdhci_pxa_platdata *pdata = pxa->pdata;
+ struct sdhci_ops *p_ops;
+
+ p_ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+ if (!p_ops)
+ return -ENOMEM;
+
+ /*
+ * we cannot directly copy our operations into host->ops
+ * since it is read only. So we do this indirectly.
+ */
+ memcpy((void *)p_ops, (void *)&sdhci_platform_data.ops,
+ sizeof(struct sdhci_ops));
+
+ if (pxa->pdata->max_speed)
+ p_ops->get_f_max_clock = get_f_max_clock;
+
+ host->quirks |= sdhci_platform_data.quirks;
+ host->mmc->caps |= sdhci_platform_data.mmc_caps;
+
+ /* If slot design supports 8 bit data, indicate this to MMC. */
+ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) {
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+ p_ops->platform_8bit_width = platform_supports_8_bit;
+ }
+
+ if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+
+ pr_debug("%s:%s: host->quirks = %08X, mmc->caps = %08lX\n",
+ __func__, mmc_hostname(host->mmc),
+ host->quirks, host->mmc->caps);
+
+ memcpy((void *)host->ops, (void *)p_ops, sizeof(struct sdhci_ops));
+ kfree(p_ops);
+ return 0;
+}
diff --git a/drivers/mmc/host/sdhci-pxa910.c b/drivers/mmc/host/sdhci-pxa910.c
new file mode 100644
index 0000000..fcd80a2
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pxa910.c
@@ -0,0 +1,273 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2009, 2010 Marvell International Ltd.
+ * Philip Rakity <prakity@marvell.com>
+ * Mark F. Brown <markb@marvell.com>
+ *
+ * This file is part of GNU program.
+ *
+ * GNU program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNU program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.
+ *
+ * If not, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ *************************************************************************/
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <plat/sdhci.h>
+#include "sdhci-pxa.h"
+#include "sdhci.h"
+
+#define DRIVER_NAME "sdhci-pxa910"
+
+#define SD_FIFO_PARAM 0xE0
+#define DIS_PAD_SD_CLK_GATE (1<<10) /* Turn on/off Dynamic Clock Gating */
+
+#define SD_CLOCK_AND_BURST_SIZE_SETUP 0xE6
+#define SDCLK_DELAY_MASK 0xF
+#define SDCLK_SEL_MASK 0x3
+#define SDCLK_DELAY_SHIFT 10
+#define SDCLK_SEL_SHIFT 8
+
+#define SD_CE_ATA_2 0xEA
+#define SDCE_MMC_WIDTH (1<<8)
+#define SDCE_MMC_CARD (1<<12)
+
+#define DISABLE_CLOCK_GATING 0
+
+static int platform_pxa910_probe(struct sdhci_host *host);
+
+/*
+ * MMC spec calls for the host to send 74 clocks to the card
+ * during initialization, right after voltage stabilization.
+ * the pxa168 controller has no easy way to generate those clocks.
+ * create the clocks manually right here.
+ */
+#if 0
+static void generate_init_clocks(struct sdhci_host *host, u8 power_mode)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+ struct pfn_cfg *cfg = pxa->pdata->pfn_table;
+ mfp_cfg_t clk_pin;
+ int i;
+
+
+ if (cfg == NULL) {
+ DBG("Cannot generate init clocks!\n");
+ return;
+ }
+
+ if (pxa->power_mode == MMC_POWER_UP
+ && power_mode == MMC_POWER_ON) {
+
+ DBG("%s: ENTER %s: power_mode = %d, ios_.power_mode = %d\n",
+ __func__,
+ mmc_hostname(host->mmc),
+ pxa->power_mode,
+ power_mode);
+ /* CMD/CLK pin to gpio mode. */
+ mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CMD), 1);
+ mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK), 1);
+
+ /* ensure at least 1/2 period stable to prevent runt pulse.*/
+ udelay(3);
+
+ clk_pin = *(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK));
+ if (gpio_request(MFP_PIN(clk_pin), "MMC_CLK")) {
+ printk(KERN_ERR "Cannot obtain MMC_CLK GPIO %ld\n",
+ MFP_PIN(clk_pin));
+ goto err;
+ }
+
+ DBG("Generate 74 clocks on pins CLK %ld\n", MFP_PIN(clk_pin));
+
+ for (i = 0; i < INIT_CLOCKS; i++) {
+ gpio_direction_output(MFP_PIN(clk_pin), 0); /* low */
+ udelay(3);
+ gpio_direction_output(MFP_PIN(clk_pin), 1); /* high */
+ udelay(3);
+ }
+
+ gpio_free(MFP_PIN(clk_pin));
+ }
+
+err:
+ pxa->power_mode = power_mode;
+
+ /* CMD/CLK pin back MMC mode. */
+ mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CMD), 1);
+ mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CLK), 1);
+}
+#endif
+
+static void set_clock_and_burst_size(struct sdhci_host *host)
+{
+ u16 tmp;
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ if (pxa->pdata->adjust_clocks) {
+ tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (B) SD_CLOCK_AND_BURST = %04X, "
+ "delay = %d, sel = %d\n",
+ __func__, mmc_hostname(host->mmc), tmp,
+ pxa->pdata->clk_delay, pxa->pdata->clk_select);
+ tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT);
+ tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT);
+ tmp |= (pxa->pdata->clk_delay & SDCLK_DELAY_MASK) <<
+ SDCLK_DELAY_SHIFT;
+ tmp |= (pxa->pdata->clk_select & SDCLK_SEL_MASK) <<
+ SDCLK_SEL_SHIFT;
+ writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP);
+ pr_debug("%s:%s: (A) SD_CLOCK_AND_BURST = %04X\n",
+ __func__, mmc_hostname(host->mmc), tmp);
+ }
+}
+
+static void programFIFO(struct sdhci_host *host, int enable)
+{
+ unsigned short tmp;
+
+ tmp = readw(host->ioaddr + SD_FIFO_PARAM);
+ if (enable)
+ tmp &= ~DIS_PAD_SD_CLK_GATE;
+ else
+ tmp |= DIS_PAD_SD_CLK_GATE;
+ writew(tmp, host->ioaddr + SD_FIFO_PARAM);
+}
+
+static void platform_reset_exit(struct sdhci_host *host, u8 mask)
+{
+ if (mask == SDHCI_RESET_ALL) {
+ programFIFO(host, DISABLE_CLOCK_GATING);
+ set_clock_and_burst_size(host);
+ }
+}
+
+#ifdef CONFIG_MMC_CLKGATE
+static void platform_hw_clk_gate(struct sdhci_host *host)
+{
+ int enable;
+
+ enable = host->mmc->clk_gated;
+ programFIFO(host, enable);
+}
+#endif
+
+static unsigned int get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ pr_debug("%s:%s clk_get_rate = %lu\n",
+ __func__, mmc_hostname(host->mmc),
+ clk_get_rate(pxa->clk));
+
+ return clk_get_rate(pxa->clk);
+}
+
+static unsigned int get_f_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+
+ pr_debug("%s:%s f_max = %d\n",
+ __func__, mmc_hostname(host->mmc),
+ pxa->pdata->max_speed);
+
+ return pxa->pdata->max_speed;
+}
+
+static int platform_supports_8_bit(struct sdhci_host *host, int width)
+{
+ u16 tmp;
+
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+
+ tmp = readw(host->ioaddr + SD_CE_ATA_2);
+ if (width != 8)
+ tmp &= ~(SDCE_MMC_CARD | SDCE_MMC_WIDTH);
+ else
+ tmp |= SDCE_MMC_CARD | SDCE_MMC_WIDTH;
+ writew(tmp, host->ioaddr + SD_CE_ATA_2);
+ return 0;
+}
+
+struct sdhci_pxa_data sdhci_platform_data = {
+ .ops = {
+ .platform_reset_exit = platform_reset_exit,
+ .get_max_clock = get_max_clock,
+ .platform_send_init_74_clocks = NULL,
+ .get_f_max_clock = NULL,
+ .platform_8bit_width = NULL,
+#ifdef CONFIG_MMC_CLKGATE
+ .platform_hw_clk_gate = platform_hw_clk_gate,
+#endif
+ },
+#ifdef CONFIG_MMC_CLKGATE
+ .mmc_caps = MMC_CAP_HW_CLOCK_GATING | MMC_CAP_BUS_WIDTH_TEST,
+#else
+ .mmc_caps = MMC_CAP_BUS_WIDTH_TEST,
+#endif
+ .platform_probe = platform_pxa910_probe,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE
+ | SDHCI_QUIRK_32BIT_ADMA_SIZE
+ | SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN
+ | SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+};
+EXPORT_SYMBOL_GPL(sdhci_platform_data);
+
+static int platform_pxa910_probe(struct sdhci_host *host)
+{
+ struct sdhci_pxa *pxa = sdhci_priv(host);
+ struct sdhci_pxa_platdata *pdata = pxa->pdata;
+ struct sdhci_ops *p_ops;
+
+ /*
+ * we cannot directly copy our operations into host->ops
+ * since it is read only. So we do this indirectly.
+ */
+ p_ops = kmalloc(sizeof(struct sdhci_ops), GFP_KERNEL);
+ if (!p_ops)
+ return -ENOMEM;
+
+ memcpy((void *)p_ops, (void *)&sdhci_platform_data.ops,
+ sizeof(struct sdhci_ops));
+
+ host->quirks |= sdhci_platform_data.quirks;
+ host->mmc->caps |= sdhci_platform_data.mmc_caps;
+
+ /* If slot design supports 8 bit data, indicate this to MMC. */
+ if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) {
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+ p_ops->platform_8bit_width = platform_supports_8_bit;
+ }
+
+ if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
+ host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+
+ if (pxa->pdata->max_speed)
+ p_ops->get_f_max_clock = get_f_max_clock;
+
+ memcpy((void *)host->ops, (void *)p_ops, sizeof(struct sdhci_ops));
+ kfree(p_ops);
+ return 0;
+}
--
1.7.0.4
On Feb 14, 2011, at 7:46 AM, Russell King - ARM Linux wrote:
> On Sun, Feb 13, 2011 at 10:56:22PM -0800, Philip Rakity wrote:
>>
>> Set timing for brownstone for SD/MMC cards to enable detection
>> timing adjustments are needed when speed > 25MHz
>> remove limitation on maximum speed of 25MHz
>>
>> Signed-off-by: Philip Rakity <prakity@marvell.com>
>> ---
>> arch/arm/mach-mmp/brownstone.c | 10 +++++++++-
>> 1 files changed, 9 insertions(+), 1 deletions(-)
>>
>> diff --git a/arch/arm/mach-mmp/brownstone.c b/arch/arm/mach-mmp/brownstone.c
>> index 0e92fd7..35afe60 100644
>> --- a/arch/arm/mach-mmp/brownstone.c
>> +++ b/arch/arm/mach-mmp/brownstone.c
>> @@ -178,14 +178,22 @@ static struct i2c_board_info brownstone_twsi1_info[] = {
>> };
>>
>> static struct sdhci_pxa_platdata mmp2_sdh_platdata_mmc0 = {
>> - .max_speed = 25000000,
>> + .adjust_clocks = 1,
>> + .clk_select = 1,
>> + .clk_delay = 31,
>> };
>>
>> static struct sdhci_pxa_platdata mmp2_sdh_platdata_mmc1 = {
>> + .adjust_clocks = 1,
>> + .clk_select = 1,
>> + .clk_delay = 15,
>> .flags = PXA_FLAG_CARD_PERMANENT,
>> };
>>
>> static struct sdhci_pxa_platdata mmp2_sdh_platdata_mmc2 = {
>> + .adjust_clocks = 1,
>> + .clk_select = 1,
>> + .clk_delay = 31,
>
> You create the new fields in patch 1 and then add values to them in patch 2.
> However, they remain unused, so it's impossible to see what the point of
> creating them was.
^ permalink raw reply related
* [RFC PATCH 2/2] ARMv7: Invalidate the TLB before freeing page tables
From: Catalin Marinas @ 2011-02-14 17:39 UTC (permalink / raw)
To: linux-arm-kernel
Newer processors like Cortex-A15 may cache entries in the higher page
table levels. These cached entries are ASID-tagged and are invalidated
during normal TLB operations.
When a level 2 (pte) page table is removed, the current code sequence
first clears the level 1 (pmd) entry, flushes the cache, frees the level
2 table and then invalidates the TLB. Because of the caching of the
higher page table entries, the processor may speculatively create a TLB
entry after the level 2 page table has been freed but before the TLB
invalidation. If such speculative PTW accesses random data, it could
create a global TLB entry that gets used for subsequent user space
accesses.
The patch ensures that the TLB is invalidated before the page table is
freed (pte_free_tlb). Since pte_free_tlb() does not get a vma structure,
the patch also introduces flush_tlb_user_page() which takes an mm_struct
rather than vma_struct. The original flush_tlb_page() is implemented as
a call to flush_tlb_user_page().
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm/include/asm/tlb.h | 17 +++++++++++++++--
arch/arm/include/asm/tlbflush.h | 16 +++++++++++-----
arch/arm/kernel/smp_tlb.c | 11 ++++++-----
3 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h
index f41a6f5..565403a 100644
--- a/arch/arm/include/asm/tlb.h
+++ b/arch/arm/include/asm/tlb.h
@@ -102,8 +102,21 @@ tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
}
#define tlb_remove_page(tlb,page) free_page_and_swap_cache(page)
-#define pte_free_tlb(tlb, ptep, addr) pte_free((tlb)->mm, ptep)
-#define pmd_free_tlb(tlb, pmdp, addr) pmd_free((tlb)->mm, pmdp)
+
+static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
+ unsigned long addr)
+{
+#if __LINUX_ARM_ARCH__ >= 7
+ flush_tlb_user_page(tlb->mm, addr);
+#endif
+ pte_free(tlb->mm, pte);
+}
+
+static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
+ unsigned long addr)
+{
+ pmd_free(tlb->mm, pmdp);
+}
#define tlb_migrate_finish(mm) do { } while (0)
diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h
index ce7378e..7bd9c52cd 100644
--- a/arch/arm/include/asm/tlbflush.h
+++ b/arch/arm/include/asm/tlbflush.h
@@ -408,17 +408,17 @@ static inline void local_flush_tlb_mm(struct mm_struct *mm)
}
static inline void
-local_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
+local_flush_tlb_user_page(struct mm_struct *mm, unsigned long uaddr)
{
const int zero = 0;
const unsigned int __tlb_flag = __cpu_tlb_flags;
- uaddr = (uaddr & PAGE_MASK) | ASID(vma->vm_mm);
+ uaddr = (uaddr & PAGE_MASK) | ASID(mm);
if (tlb_flag(TLB_WB))
dsb();
- if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) {
+ if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm))) {
if (tlb_flag(TLB_V3_PAGE))
asm("mcr p15, 0, %0, c6, c0, 0" : : "r" (uaddr) : "cc");
if (tlb_flag(TLB_V4_U_PAGE))
@@ -556,19 +556,25 @@ static inline void clean_pmd_entry(pmd_t *pmd)
#ifndef CONFIG_SMP
#define flush_tlb_all local_flush_tlb_all
#define flush_tlb_mm local_flush_tlb_mm
-#define flush_tlb_page local_flush_tlb_page
+#define flush_tlb_user_page local_flush_tlb_user_page
#define flush_tlb_kernel_page local_flush_tlb_kernel_page
#define flush_tlb_range local_flush_tlb_range
#define flush_tlb_kernel_range local_flush_tlb_kernel_range
#else
extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
-extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr);
+extern void flush_tlb_user_page(struct mm_struct *mm, unsigned long uaddr);
extern void flush_tlb_kernel_page(unsigned long kaddr);
extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
#endif
+static inline void
+flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
+{
+ flush_tlb_user_page(vma->vm_mm, uaddr);
+}
+
/*
* If PG_dcache_clean is not set for the page, we need to ensure that any
* cache entries for the kernels virtual memory range are written
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index 7dcb352..8f57f32 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -32,6 +32,7 @@ static void on_each_cpu_mask(void (*func)(void *), void *info, int wait,
*/
struct tlb_args {
struct vm_area_struct *ta_vma;
+ struct mm_struct *ta_mm;
unsigned long ta_start;
unsigned long ta_end;
};
@@ -52,7 +53,7 @@ static inline void ipi_flush_tlb_page(void *arg)
{
struct tlb_args *ta = (struct tlb_args *)arg;
- local_flush_tlb_page(ta->ta_vma, ta->ta_start);
+ local_flush_tlb_user_page(ta->ta_mm, ta->ta_start);
}
static inline void ipi_flush_tlb_kernel_page(void *arg)
@@ -92,15 +93,15 @@ void flush_tlb_mm(struct mm_struct *mm)
local_flush_tlb_mm(mm);
}
-void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
+void flush_tlb_user_page(struct mm_struct *mm, unsigned long uaddr)
{
if (tlb_ops_need_broadcast()) {
struct tlb_args ta;
- ta.ta_vma = vma;
+ ta.ta_mm = mm;
ta.ta_start = uaddr;
- on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, mm_cpumask(vma->vm_mm));
+ on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, mm_cpumask(mm));
} else
- local_flush_tlb_page(vma, uaddr);
+ local_flush_tlb_user_page(mm, uaddr);
}
void flush_tlb_kernel_page(unsigned long kaddr)
^ 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