On Wed, 2009-05-27 at 12:55 -0600, Grant Likely wrote:
> From: Grant Likely <grant.lik...@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.lik...@secretlab.ca>
> ---

My 40x config gives me:

/home/benh/linux-powerpc-test/drivers/video/xilinxfb.c:409: warning:
‘dcr_host.base’ may be used uninitialized in this function

(warning, I think, was already there, so the patch is going into -next
but we may want another one, provided we find a way to shut the idiot up
without horrible hacks since that's just gcc being stupid I believe).

Cheers,
Ben.

> new in v3:
> - Rebased onto Ben's dma_alloc_coherent changes
> - Fixed alignment to match region size
> 
>  arch/powerpc/include/asm/io.h                |    8 +
>  arch/powerpc/kernel/setup_32.c               |    4 
>  arch/powerpc/mm/init_32.c                    |    3 
>  arch/powerpc/mm/mmu_decl.h                   |    7 +
>  arch/powerpc/mm/pgtable_32.c                 |   12 +
>  arch/powerpc/mm/ppc_mmu_32.c                 |  210 
> +++++++++++++++++++++++---
>  arch/powerpc/platforms/52xx/mpc52xx_common.c |   13 ++
>  arch/powerpc/sysdev/cpm_common.c             |    2 
>  8 files changed, 228 insertions(+), 31 deletions(-)
> 
> 
> diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h
> index 001f2f1..10183e2 100644
> --- a/arch/powerpc/include/asm/io.h
> +++ b/arch/powerpc/include/asm/io.h
> @@ -624,6 +624,12 @@ static inline void iosync(void)
>   *
>   * * iounmap undoes such a mapping and can be hooked
>   *
> + * * ioremap_early is for setting up mapping regions during early boot.  
> Useful
> + *   for console devices or mapping an entire region of SoC internal 
> registers.
> + *   ioremap_early becomes usable at machine_init() time.  Care must be taken
> + *   when using this routine because it can consume limited resources like 
> BAT
> + *   registers.
> + *
>   * * __ioremap_at (and the pending __iounmap_at) are low level functions to
>   *   create hand-made mappings for use only by the PCI code and cannot
>   *   currently be hooked. Must be page aligned.
> @@ -647,6 +653,8 @@ extern void __iomem *ioremap_flags(phys_addr_t address, 
> unsigned long size,
>  
>  extern void iounmap(volatile void __iomem *addr);
>  
> +extern void __iomem *ioremap_early(phys_addr_t addr, unsigned long size);
> +
>  extern void __iomem *__ioremap(phys_addr_t, unsigned long size,
>                              unsigned long flags);
>  extern void __iomem *__ioremap_caller(phys_addr_t, unsigned long size,
> diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
> index 9e1ca74..c1c0442 100644
> --- a/arch/powerpc/kernel/setup_32.c
> +++ b/arch/powerpc/kernel/setup_32.c
> @@ -41,6 +41,7 @@
>  #include <asm/mmu_context.h>
>  
>  #include "setup.h"
> +#include "mm/mmu_decl.h"
>  
>  #define DBG(fmt...)
>  
> @@ -118,6 +119,9 @@ notrace unsigned long __init early_init(unsigned long 
> dt_ptr)
>   */
>  notrace void __init machine_init(unsigned long dt_ptr)
>  {
> +     /* Get ready to allocate IO virtual address regions */
> +     ioremap_init();
> +
>       /* Enable early debugging if any specified (see udbg.h) */
>       udbg_early_init();
>  
> diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c
> index 3de6a0d..806c237 100644
> --- a/arch/powerpc/mm/init_32.c
> +++ b/arch/powerpc/mm/init_32.c
> @@ -168,9 +168,6 @@ void __init MMU_init(void)
>               ppc_md.progress("MMU:mapin", 0x301);
>       mapin_ram();
>  
> -     /* Initialize early top-down ioremap allocator */
> -     ioremap_bot = IOREMAP_TOP;
> -
>       /* Map in I/O resources */
>       if (ppc_md.progress)
>               ppc_md.progress("MMU:setio", 0x302);
> diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h
> index d1f9c62..6be30fe 100644
> --- a/arch/powerpc/mm/mmu_decl.h
> +++ b/arch/powerpc/mm/mmu_decl.h
> @@ -86,11 +86,14 @@ struct tlbcam {
>  
>  extern void mapin_ram(void);
>  extern int map_page(unsigned long va, phys_addr_t pa, int flags);
> -extern void setbat(int index, unsigned long virt, phys_addr_t phys,
> -                unsigned int size, int flags);
> +extern int setbat(unsigned long virt, phys_addr_t phys, unsigned int size,
> +               int flags);
> +extern int loadbat(unsigned long virt, phys_addr_t phys, unsigned int size,
> +                int flags);
>  extern void settlbcam(int index, unsigned long virt, phys_addr_t phys,
>                     unsigned int size, int flags, unsigned int pid);
>  extern void invalidate_tlbcam_entry(int index);
> +extern void ioremap_init(void); /* called by machine_init() */
>  
>  extern int __map_without_bats;
>  extern unsigned long ioremap_base;
> diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c
> index 5422169..508fb91 100644
> --- a/arch/powerpc/mm/pgtable_32.c
> +++ b/arch/powerpc/mm/pgtable_32.c
> @@ -51,8 +51,6 @@ extern char etext[], _stext[];
>  #ifdef HAVE_BATS
>  extern phys_addr_t v_mapped_by_bats(unsigned long va);
>  extern unsigned long p_mapped_by_bats(phys_addr_t pa);
> -void setbat(int index, unsigned long virt, phys_addr_t phys,
> -         unsigned int size, int flags);
>  
>  #else /* !HAVE_BATS */
>  #define v_mapped_by_bats(x)  (0UL)
> @@ -126,6 +124,16 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned 
> long address)
>       return ptepage;
>  }
>  
> +/**
> + * ioremap_init - Initialize early top-down ioremap allocator
> + */
> +void __init ioremap_init(void)
> +{
> +     if (ioremap_bot)
> +             return;
> +     ioremap_bot = IOREMAP_TOP;
> +}
> +
>  void __iomem *
>  ioremap(phys_addr_t addr, unsigned long size)
>  {
> diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
> index 2d2a87e..01acd2e 100644
> --- a/arch/powerpc/mm/ppc_mmu_32.c
> +++ b/arch/powerpc/mm/ppc_mmu_32.c
> @@ -72,38 +72,41 @@ unsigned long p_mapped_by_bats(phys_addr_t pa)
>       return 0;
>  }
>  
> +/**
> + * mmu_mapin_ram - Map as much of RAM as possible into kernel space using 
> BATs
> + */
>  unsigned long __init mmu_mapin_ram(void)
>  {
>       unsigned long tot, bl, done;
> -     unsigned long max_size = (256<<20);
> +     int rc;
>  
>       if (__map_without_bats) {
> -             printk(KERN_DEBUG "RAM mapped without BATs\n");
> +             pr_debug("RAM mapped without BATs\n");
>               return 0;
>       }
>  
> -     /* Set up BAT2 and if necessary BAT3 to cover RAM. */
> -
> -     /* Make sure we don't map a block larger than the
> -        smallest alignment of the physical address. */
> +     /* Set up BATs to cover RAM. */
>       tot = total_lowmem;
> -     for (bl = 128<<10; bl < max_size; bl <<= 1) {
> -             if (bl * 2 > tot)
> +     done = 0;
> +     while (done < tot) {
> +             /* determine the smallest block size need to map the region.
> +              * Don't use a BAT mapping if the remaining region is less
> +              * that 128k */
> +             if (tot - done <= 128<<10)
>                       break;
> -     }
> -
> -     setbat(2, PAGE_OFFSET, 0, bl, PAGE_KERNEL_X);
> -     done = (unsigned long)bat_addrs[2].limit - PAGE_OFFSET + 1;
> -     if ((done < tot) && !bat_addrs[3].limit) {
> -             /* use BAT3 to cover a bit more */
> -             tot -= done;
> -             for (bl = 128<<10; bl < max_size; bl <<= 1)
> -                     if (bl * 2 > tot)
> +             for (bl = 128<<10; bl < (256<<20); bl <<= 1)
> +                     if ((bl * 2) > (tot - done))
>                               break;
> -             setbat(3, PAGE_OFFSET+done, done, bl, PAGE_KERNEL_X);
> -             done = (unsigned long)bat_addrs[3].limit - PAGE_OFFSET + 1;
> +
> +             /* Allocate the BAT and recalculate amount of RAM mapped */
> +             rc = setbat(PAGE_OFFSET+done, done, bl, PAGE_KERNEL_X);
> +             if (rc < 0)
> +                     break;
> +             done = (unsigned long)bat_addrs[rc].limit - PAGE_OFFSET + 1;
>       }
>  
> +     if (done == 0)
> +             pr_crit("Weird; No BATs available for RAM.\n");
>       return done;
>  }
>  
> @@ -112,12 +115,29 @@ unsigned long __init mmu_mapin_ram(void)
>   * The parameters are not checked; in particular size must be a power
>   * of 2 between 128k and 256M.
>   */
> -void __init setbat(int index, unsigned long virt, phys_addr_t phys,
> -                unsigned int size, int flags)
> +int __init setbat(unsigned long virt, phys_addr_t phys,
> +               unsigned int size, int flags)
>  {
>       unsigned int bl;
> -     int wimgxpp;
> -     struct ppc_bat *bat = BATS[index];
> +     int wimgxpp, index, nr_bats;
> +     struct ppc_bat *bat;
> +
> +     /* Find a free BAT
> +      *
> +      * Special case; Keep the first entry in reserve for mapping RAM.
> +      * Otherwise the too many other users can prevent RAM from getting
> +      * mapped at all with a BAT.
> +      */
> +     index = (flags == PAGE_KERNEL_X) ? 0 : 1;
> +     nr_bats = mmu_has_feature(MMU_FTR_USE_HIGH_BATS) ? 8 : 4;
> +     for (; index < nr_bats; index++) {
> +             if ((BATS[index][0].batu == 0) && (BATS[index][1].batu == 0))
> +                     break;
> +     }
> +     if (index == nr_bats)
> +             return -1;
> +
> +     bat = BATS[index];
>  
>       if ((flags & _PAGE_NO_CACHE) ||
>           (cpu_has_feature(CPU_FTR_NEED_COHERENT) == 0))
> @@ -156,6 +176,150 @@ void __init setbat(int index, unsigned long virt, 
> phys_addr_t phys,
>       bat_addrs[index].start = virt;
>       bat_addrs[index].limit = virt + ((bl + 1) << 17) - 1;
>       bat_addrs[index].phys = phys;
> +     return index;
> +}
> +
> +/**
> + * loadbat - Set up and configure one of the I/D BAT register pairs.
> + * @virt - virtual address, 128k aligned
> + * @phys - physical address, 128k aligned
> + * @size - size of mapping
> + * @flags - region attribute flags
> + *
> + * Uses setbat() to allocate a BAT pair and immediately writes the
> + * configuration into the BAT registers (instead of waiting for load_up_mmu)
> + */
> +int __init loadbat(unsigned long virt, phys_addr_t phys,
> +                unsigned int size, int flags)
> +{
> +     struct ppc_bat *bat;
> +     int i;
> +
> +     i = setbat(virt, phys, size, flags);
> +     if (i < 0)
> +             return i;
> +     bat = BATS[i];
> +
> +     /* BATs must be set with a switch statement because there is no way
> +      * to paramaterize mtspr/mfspr instructions.
> +      *
> +      * Note: BAT0 is not handled here because early boot code depends
> +      * on BAT0 for mapping first 16M of RAM.  setbat() keeps BAT0 in
> +      * reserve for mapping main memory anyway, so this is okay.
> +      */
> +     switch (i) {
> +     case 1:
> +             mtspr(SPRN_IBAT1U, bat[0].batu);
> +             mtspr(SPRN_IBAT1L, bat[0].batl);
> +             mtspr(SPRN_DBAT1U, bat[1].batu);
> +             mtspr(SPRN_DBAT1L, bat[1].batl);
> +             break;
> +     case 2:
> +             mtspr(SPRN_IBAT2U, bat[0].batu);
> +             mtspr(SPRN_IBAT2L, bat[0].batl);
> +             mtspr(SPRN_DBAT2U, bat[1].batu);
> +             mtspr(SPRN_DBAT2L, bat[1].batl);
> +             break;
> +     case 3:
> +             mtspr(SPRN_IBAT3U, bat[0].batu);
> +             mtspr(SPRN_IBAT3L, bat[0].batl);
> +             mtspr(SPRN_DBAT3U, bat[1].batu);
> +             mtspr(SPRN_DBAT3L, bat[1].batl);
> +             break;
> +     case 4:
> +             mtspr(SPRN_IBAT4U, bat[0].batu);
> +             mtspr(SPRN_IBAT4L, bat[0].batl);
> +             mtspr(SPRN_DBAT4U, bat[1].batu);
> +             mtspr(SPRN_DBAT4L, bat[1].batl);
> +             break;
> +     case 5:
> +             mtspr(SPRN_IBAT5U, bat[0].batu);
> +             mtspr(SPRN_IBAT5L, bat[0].batl);
> +             mtspr(SPRN_DBAT5U, bat[1].batu);
> +             mtspr(SPRN_DBAT5L, bat[1].batl);
> +             break;
> +     case 6:
> +             mtspr(SPRN_IBAT6U, bat[0].batu);
> +             mtspr(SPRN_IBAT6L, bat[0].batl);
> +             mtspr(SPRN_DBAT6U, bat[1].batu);
> +             mtspr(SPRN_DBAT6L, bat[1].batl);
> +             break;
> +     case 7:
> +             mtspr(SPRN_IBAT7U, bat[0].batu);
> +             mtspr(SPRN_IBAT7L, bat[0].batl);
> +             mtspr(SPRN_DBAT7U, bat[1].batu);
> +             mtspr(SPRN_DBAT7L, bat[1].batl);
> +             break;
> +     }
> +
> +     return i;
> +}
> +
> +/**
> + * ioremap_early - Allow large persistant IO regions to be mapped early.
> + * @addr: physical address of region
> + * @size: size of region
> + *
> + * This routine uses setbat() to set up IO ranges before the MMU is
> + * fully configured.
> + *
> + * This routine can be called really early, before MMU_init() is called.  It
> + * is useful for setting up early debug output consoles and frequently
> + * accessed IO regions, like the internally memory mapped registers (IMMR)
> + * in an SoC.  Ranges mapped with this function persist even after MMU_init()
> + * is called and the MMU is turned on 'for real.'
> + *
> + * The region mapped is large (minimum size of 128k) and virtual mapping must
> + * be aligned against this boundary.  Therefore, to avoid fragmentation all
> + * calls to ioremap_early() are best made before any calls to ioremap
> + * for smaller regions.
> + */
> +void __iomem * __init
> +ioremap_early(phys_addr_t addr, unsigned long size)
> +{
> +     unsigned long v, p, bl;
> +     int i;
> +
> +     /* 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;
> +
> +     /* 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;
> +
> +     /* 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;
> +     }
> +
> +     /* 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;
>       }

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to