[PATCH v3] powerpc: add ioremap_early() for mapping IO regions before MMU_init()

Grant Likely grant.likely at secretlab.ca
Wed Jun 17 02:39:34 EST 2009


On Mon, Jun 15, 2009 at 12:55 AM, Benjamin
Herrenschmidt<benh at kernel.crashing.org> wrote:
> On Wed, 2009-05-27 at 12:55 -0600, Grant Likely wrote:
>> From: Grant Likely <grant.likely at secretlab.ca>
>>
>> ioremap_early() is useful for things like mapping SoC internally registers
>> and early debug output because it allows mappings to devices to be setup
>> early in the boot process where they are needed.  It also give a
>> performance boost since BAT mapped registers don't get flushed out of
>> the TLB.
>>
>> Without ioremap_early(), early mappings are set up in an ad-hoc manner
>> and they get lost when the MMU is set up.  Drivers then have to perform
>> hacky fixups to transition over to new mappings.
>>
>> Signed-off-by: Grant Likely <grant.likely at secretlab.ca>
>> ---
>
> Approach looks sane at first glance.
>
> However, I'm reluctant to but that in until we have all MMU types
> covered or we'll have "interesting" surprises.

I considered this and was originally concerned about the same thing.
However, ioremap_early() is special in that caller cannot take for
granted what it does and must understand the side effects.  For
example; on 6xx ioremap_early is always going to carve out a minimum
of 128k from the virtual address space and it is likely that the range
will extend both both before and after the desired address.  Plus,
because of the limited number of BATs there is real likelyhood that
ioremap_early() will fail.  Code calling it must handle the failure
mode gracefully.

IMHO, I think this is only really applicable for platform code that
understands the memory layout.  ie. map the entire IMMR at once, or
mapping local bus device ranges.  On the 5200 I call it early in the
platform setup on the IMMR range, but I don't actually do anything
with the returned value (unless I'm doing udbg).  Then, future
ioremap() calls to that range get to use the BAT mapping
transparently.

On the 440 and the 405 with the small TLB users also need to be
careful so that too many TLB entries don't get pinned to static
allocation and negate the performance improvement of doing the pinning
in the first place.  Again I think it is best restricted to platform
code, and should never cause the system to fail to boot if the mapping
doesn't work.

I do want to implement it for all MMUs (when I have the bandwidth to
do so), but I don't think merging it needs to wait.  If it is merged
as is and someone uses it in the wrong place (ie. non-platform code),
then 405 and 440 will fail to build, and it will get caught quickly.
Alternately I could implement stub ioremap_early() for non-6xx to just
return NULL until it can be implemented.  Callers who don't handle
NULL gracefully are broken, but it won't be known until boot time.

> Also, the CPM patch
> doesn't actually fix the massive bogon in there :-)

Yeah, that was just to get it to build.  I'll look at fix that too.

>> +     /* Be loud and annoying if someone calls this too late.
>> +      * No need to crash the kernel though */
>> +     WARN_ON(mem_init_done);
>> +     if (mem_init_done)
>> +             return NULL;
>
> Can't we write
>
>        if (WARN_ON(mem_init_done))
>                return NULL;
>
> nowadays ?

I'll check.

>> +     /* Make sure request is sane */
>> +     if (size == 0)
>> +             return NULL;
>> +
>> +     /* If the region is already block mapped, then there is nothing
>> +      * to do; just return the mapped address */
>> +     v = p_mapped_by_bats(addr);
>> +     if (v)
>> +             return (void __iomem *)v;
>
> Should we check the size ?

Ugh.  Yes.  good catch.

>> +     /* Align region size */
>> +     for (bl = 128<<10; bl < (256<<20); bl <<= 1) {
>> +             p = _ALIGN_DOWN(addr, bl); /* BATs align on 128k boundaries */
>> +             size = ALIGN(addr - p + size, bl);
>> +             if (bl >= size)
>> +                     break;
>> +     }
>> +
>> +     /* Complain loudly if too much is requested */
>> +     if (bl >= (256<<20)) {
>> +             WARN_ON(1);
>> +             return NULL;
>> +     }
>
> Do we avoid that running into the linear mapping ?

No.  I'll fix.

>> +     /* Allocate the aligned virtual base address.  ALIGN_DOWN is used
>> +      * to ensure no overlaps occur with normal 4k ioremaps. */
>> +     ioremap_bot = _ALIGN_DOWN(ioremap_bot, bl) - size;
>> +
>> +     /* Set up a BAT for this IO region */
>> +     i = loadbat(ioremap_bot, p, size, PAGE_KERNEL_NCG);
>> +     if (i < 0)
>> +             return NULL;
>> +
>> +     return (void __iomem *) (ioremap_bot + (addr - p));
>>  }
>>
>>  /*
>> diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
>> index 8e3dd5a..2c49148 100644
>> --- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
>> +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
>> @@ -146,7 +146,20 @@ static struct of_device_id mpc52xx_cdm_ids[] __initdata = {
>>  void __init
>>  mpc52xx_map_common_devices(void)
>>  {
>> +     const struct of_device_id immr_ids[] = {
>> +             { .compatible = "fsl,mpc5200-immr", },
>> +             { .compatible = "fsl,mpc5200b-immr", },
>> +             { .type = "soc", .compatible = "mpc5200", }, /* lite5200 */
>> +             { .type = "builtin", .compatible = "mpc5200", }, /* efika */
>> +             {}
>> +     };
>>       struct device_node *np;
>> +     struct resource res;
>> +
>> +     /* Pre-map the whole register space using a BAT entry */
>> +     np = of_find_matching_node(NULL, immr_ids);
>> +     if (np && (of_address_to_resource(np, 0, &res) == 0))
>> +             ioremap_early(res.start, res.end - res.start + 1);
>>
>>       /* mpc52xx_wdt is mapped here and used in mpc52xx_restart,
>>        * possibly from a interrupt context. wdt is only implement
>> diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c
>> index e4b6d66..370723e 100644
>> --- a/arch/powerpc/sysdev/cpm_common.c
>> +++ b/arch/powerpc/sysdev/cpm_common.c
>> @@ -56,7 +56,7 @@ void __init udbg_init_cpm(void)
>>  {
>>       if (cpm_udbg_txdesc) {
>>  #ifdef CONFIG_CPM2
>> -             setbat(1, 0xf0000000, 0xf0000000, 1024*1024, PAGE_KERNEL_NCG);
>> +             setbat(0xf0000000, 0xf0000000, 1024*1024, PAGE_KERNEL_NCG);
>>  #endif
>>               udbg_putc = udbg_putc_cpm;
>>       }
>
> That needs to be properly fixed ... maybe using ioremap_early() ? :-)

:-p

> Also, make the initial call ioremap_early_init() just to make things
> clear that one can't just call ioremap(), we are limited to a very
> specific thing here.

ok.

g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.


More information about the Linuxppc-dev mailing list