linux-bk/arch/m68k/mm/kmap.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/m68k/mm/kmap.c
   3 *
   4 *  Copyright (C) 1997 Roman Hodek
   5 *
   6 *  10/01/99 cleaned up the code and changing to the same interface
   7 *           used by other architectures                /Roman Zippel
   8 */
   9
  10#include <linux/config.h>
  11#include <linux/mm.h>
  12#include <linux/kernel.h>
  13#include <linux/string.h>
  14#include <linux/types.h>
  15#include <linux/slab.h>
  16#include <linux/vmalloc.h>
  17
  18#include <asm/setup.h>
  19#include <asm/segment.h>
  20#include <asm/page.h>
  21#include <asm/pgalloc.h>
  22#include <asm/io.h>
  23#include <asm/system.h>
  24
  25#undef DEBUG
  26
  27#define PTRTREESIZE     (256*1024)
  28
  29/*
  30 * For 040/060 we can use the virtual memory area like other architectures,
  31 * but for 020/030 we want to use early termination page descriptor and we
  32 * can't mix this with normal page descriptors, so we have to copy that code
  33 * (mm/vmalloc.c) and return appriorate aligned addresses.
  34 */
  35
  36#ifdef CPU_M68040_OR_M68060_ONLY
  37
  38#define IO_SIZE         PAGE_SIZE
  39
  40static inline struct vm_struct *get_io_area(unsigned long size)
  41{
  42        return get_vm_area(size, VM_IOREMAP);
  43}
  44
  45
  46static inline void free_io_area(void *addr)
  47{
  48        vfree((void *)(PAGE_MASK & (unsigned long)addr));
  49}
  50
  51#else
  52
  53#define IO_SIZE         (256*1024)
  54
  55static struct vm_struct *iolist;
  56
  57static struct vm_struct *get_io_area(unsigned long size)
  58{
  59        unsigned long addr;
  60        struct vm_struct **p, *tmp, *area;
  61
  62        area = (struct vm_struct *)kmalloc(sizeof(*area), GFP_KERNEL);
  63        if (!area)
  64                return NULL;
  65        addr = KMAP_START;
  66        for (p = &iolist; (tmp = *p) ; p = &tmp->next) {
  67                if (size + addr < (unsigned long)tmp->addr)
  68                        break;
  69                if (addr > KMAP_END-size)
  70                        return NULL;
  71                addr = tmp->size + (unsigned long)tmp->addr;
  72        }
  73        area->addr = (void *)addr;
  74        area->size = size + IO_SIZE;
  75        area->next = *p;
  76        *p = area;
  77        return area;
  78}
  79
  80static inline void free_io_area(void *addr)
  81{
  82        struct vm_struct **p, *tmp;
  83
  84        if (!addr)
  85                return;
  86        addr = (void *)((unsigned long)addr & -IO_SIZE);
  87        for (p = &iolist ; (tmp = *p) ; p = &tmp->next) {
  88                if (tmp->addr == addr) {
  89                        *p = tmp->next;
  90                        __iounmap(tmp->addr, tmp->size);
  91                        kfree(tmp);
  92                        return;
  93                }
  94        }
  95}
  96
  97#endif
  98
  99/*
 100 * Map some physical address range into the kernel address space. The
 101 * code is copied and adapted from map_chunk().
 102 */
 103/* Rewritten by Andreas Schwab to remove all races. */
 104
 105void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag)
 106{
 107        struct vm_struct *area;
 108        unsigned long virtaddr, retaddr;
 109        long offset;
 110        pgd_t *pgd_dir;
 111        pmd_t *pmd_dir;
 112        pte_t *pte_dir;
 113
 114        /*
 115         * Don't allow mappings that wrap..
 116         */
 117        if (!size || size > physaddr + size)
 118                return NULL;
 119
 120#ifdef CONFIG_AMIGA
 121        if (MACH_IS_AMIGA) {
 122                if ((physaddr >= 0x40000000) && (physaddr + size < 0x60000000)
 123                    && (cacheflag == IOMAP_NOCACHE_SER))
 124                        return (void *)physaddr;
 125        }
 126#endif
 127
 128#ifdef DEBUG
 129        printk("ioremap: 0x%lx,0x%lx(%d) - ", physaddr, size, cacheflag);
 130#endif
 131        /*
 132         * Mappings have to be aligned
 133         */
 134        offset = physaddr & (IO_SIZE - 1);
 135        physaddr &= -IO_SIZE;
 136        size = (size + offset + IO_SIZE - 1) & -IO_SIZE;
 137
 138        /*
 139         * Ok, go for it..
 140         */
 141        area = get_io_area(size);
 142        if (!area)
 143                return NULL;
 144
 145        virtaddr = (unsigned long)area->addr;
 146        retaddr = virtaddr + offset;
 147#ifdef DEBUG
 148        printk("0x%lx,0x%lx,0x%lx", physaddr, virtaddr, retaddr);
 149#endif
 150
 151        /*
 152         * add cache and table flags to physical address
 153         */
 154        if (CPU_IS_040_OR_060) {
 155                physaddr |= (_PAGE_PRESENT | _PAGE_GLOBAL040 |
 156                             _PAGE_ACCESSED | _PAGE_DIRTY);
 157                switch (cacheflag) {
 158                case IOMAP_FULL_CACHING:
 159                        physaddr |= _PAGE_CACHE040;
 160                        break;
 161                case IOMAP_NOCACHE_SER:
 162                default:
 163                        physaddr |= _PAGE_NOCACHE_S;
 164                        break;
 165                case IOMAP_NOCACHE_NONSER:
 166                        physaddr |= _PAGE_NOCACHE;
 167                        break;
 168                case IOMAP_WRITETHROUGH:
 169                        physaddr |= _PAGE_CACHE040W;
 170                        break;
 171                }
 172        } else {
 173                physaddr |= (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY);
 174                switch (cacheflag) {
 175                case IOMAP_NOCACHE_SER:
 176                case IOMAP_NOCACHE_NONSER:
 177                default:
 178                        physaddr |= _PAGE_NOCACHE030;
 179                        break;
 180                case IOMAP_FULL_CACHING:
 181                case IOMAP_WRITETHROUGH:
 182                        break;
 183                }
 184        }
 185
 186        while ((long)size > 0) {
 187#ifdef DEBUG
 188                if (!(virtaddr & (PTRTREESIZE-1)))
 189                        printk ("\npa=%#lx va=%#lx ", physaddr, virtaddr);
 190#endif
 191                pgd_dir = pgd_offset_k(virtaddr);
 192                pmd_dir = pmd_alloc(&init_mm, pgd_dir, virtaddr);
 193                if (!pmd_dir) {
 194                        printk("ioremap: no mem for pmd_dir\n");
 195                        return NULL;
 196                }
 197
 198                if (CPU_IS_020_OR_030) {
 199                        pmd_dir->pmd[(virtaddr/PTRTREESIZE) & 15] = physaddr;
 200                        physaddr += PTRTREESIZE;
 201                        virtaddr += PTRTREESIZE;
 202                        size -= PTRTREESIZE;
 203                } else {
 204                        pte_dir = pte_alloc_kernel(&init_mm, pmd_dir, virtaddr);
 205                        if (!pte_dir) {
 206                                printk("ioremap: no mem for pte_dir\n");
 207                                return NULL;
 208                        }
 209
 210                        pte_val(*pte_dir) = physaddr;
 211                        virtaddr += PAGE_SIZE;
 212                        physaddr += PAGE_SIZE;
 213                        size -= PAGE_SIZE;
 214                }
 215        }
 216#ifdef DEBUG
 217        printk("\n");
 218#endif
 219        flush_tlb_all();
 220
 221        return (void *)retaddr;
 222}
 223
 224/*
 225 * Unmap a ioremap()ed region again
 226 */
 227void iounmap(void *addr)
 228{
 229#ifdef CONFIG_AMIGA
 230        if ((!MACH_IS_AMIGA) ||
 231            (((unsigned long)addr < 0x40000000) ||
 232             ((unsigned long)addr > 0x60000000)))
 233                        free_io_area(addr);
 234#else
 235        free_io_area(addr);
 236#endif
 237}
 238
 239/*
 240 * __iounmap unmaps nearly everything, so be careful
 241 * it doesn't free currently pointer/page tables anymore but it
 242 * wans't used anyway and might be added later.
 243 */
 244void __iounmap(void *addr, unsigned long size)
 245{
 246        unsigned long virtaddr = (unsigned long)addr;
 247        pgd_t *pgd_dir;
 248        pmd_t *pmd_dir;
 249        pte_t *pte_dir;
 250
 251        while ((long)size > 0) {
 252                pgd_dir = pgd_offset_k(virtaddr);
 253                if (pgd_bad(*pgd_dir)) {
 254                        printk("iounmap: bad pgd(%08lx)\n", pgd_val(*pgd_dir));
 255                        pgd_clear(pgd_dir);
 256                        return;
 257                }
 258                pmd_dir = pmd_offset(pgd_dir, virtaddr);
 259
 260                if (CPU_IS_020_OR_030) {
 261                        int pmd_off = (virtaddr/PTRTREESIZE) & 15;
 262
 263                        if ((pmd_dir->pmd[pmd_off] & _DESCTYPE_MASK) == _PAGE_PRESENT) {
 264                                pmd_dir->pmd[pmd_off] = 0;
 265                                virtaddr += PTRTREESIZE;
 266                                size -= PTRTREESIZE;
 267                                continue;
 268                        }
 269                }
 270
 271                if (pmd_bad(*pmd_dir)) {
 272                        printk("iounmap: bad pmd (%08lx)\n", pmd_val(*pmd_dir));
 273                        pmd_clear(pmd_dir);
 274                        return;
 275                }
 276                pte_dir = pte_offset_kernel(pmd_dir, virtaddr);
 277
 278                pte_val(*pte_dir) = 0;
 279                virtaddr += PAGE_SIZE;
 280                size -= PAGE_SIZE;
 281        }
 282
 283        flush_tlb_all();
 284}
 285
 286/*
 287 * Set new cache mode for some kernel address space.
 288 * The caller must push data for that range itself, if such data may already
 289 * be in the cache.
 290 */
 291void kernel_set_cachemode(void *addr, unsigned long size, int cmode)
 292{
 293        unsigned long virtaddr = (unsigned long)addr;
 294        pgd_t *pgd_dir;
 295        pmd_t *pmd_dir;
 296        pte_t *pte_dir;
 297
 298        if (CPU_IS_040_OR_060) {
 299                switch (cmode) {
 300                case IOMAP_FULL_CACHING:
 301                        cmode = _PAGE_CACHE040;
 302                        break;
 303                case IOMAP_NOCACHE_SER:
 304                default:
 305                        cmode = _PAGE_NOCACHE_S;
 306                        break;
 307                case IOMAP_NOCACHE_NONSER:
 308                        cmode = _PAGE_NOCACHE;
 309                        break;
 310                case IOMAP_WRITETHROUGH:
 311                        cmode = _PAGE_CACHE040W;
 312                        break;
 313                }
 314        } else {
 315                switch (cmode) {
 316                case IOMAP_NOCACHE_SER:
 317                case IOMAP_NOCACHE_NONSER:
 318                default:
 319                        cmode = _PAGE_NOCACHE030;
 320                        break;
 321                case IOMAP_FULL_CACHING:
 322                case IOMAP_WRITETHROUGH:
 323                        cmode = 0;
 324                }
 325        }
 326
 327        while ((long)size > 0) {
 328                pgd_dir = pgd_offset_k(virtaddr);
 329                if (pgd_bad(*pgd_dir)) {
 330                        printk("iocachemode: bad pgd(%08lx)\n", pgd_val(*pgd_dir));
 331                        pgd_clear(pgd_dir);
 332                        return;
 333                }
 334                pmd_dir = pmd_offset(pgd_dir, virtaddr);
 335
 336                if (CPU_IS_020_OR_030) {
 337                        int pmd_off = (virtaddr/PTRTREESIZE) & 15;
 338
 339                        if ((pmd_dir->pmd[pmd_off] & _DESCTYPE_MASK) == _PAGE_PRESENT) {
 340                                pmd_dir->pmd[pmd_off] = (pmd_dir->pmd[pmd_off] &
 341                                                         _CACHEMASK040) | cmode;
 342                                virtaddr += PTRTREESIZE;
 343                                size -= PTRTREESIZE;
 344                                continue;
 345                        }
 346                }
 347
 348                if (pmd_bad(*pmd_dir)) {
 349                        printk("iocachemode: bad pmd (%08lx)\n", pmd_val(*pmd_dir));
 350                        pmd_clear(pmd_dir);
 351                        return;
 352                }
 353                pte_dir = pte_offset_kernel(pmd_dir, virtaddr);
 354
 355                pte_val(*pte_dir) = (pte_val(*pte_dir) & _CACHEMASK040) | cmode;
 356                virtaddr += PAGE_SIZE;
 357                size -= PAGE_SIZE;
 358        }
 359
 360        flush_tlb_all();
 361}
 362
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.