linux/arch/powerpc/sysdev/ppc4xx_ocm.c
<<
>>
Prefs
   1/*
   2 * PowerPC 4xx OCM memory allocation support
   3 *
   4 * (C) Copyright 2009, Applied Micro Circuits Corporation
   5 * Victor Gallardo (vgallardo@amcc.com)
   6 *
   7 * See file CREDITS for list of people who contributed to this
   8 * project.
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public License as
  12 * published by the Free Software Foundation; either version 2 of
  13 * the License, or (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  23 * MA 02111-1307 USA
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/dma-mapping.h>
  28#include <linux/of.h>
  29#include <asm/rheap.h>
  30#include <asm/ppc4xx_ocm.h>
  31#include <linux/slab.h>
  32#include <linux/debugfs.h>
  33
  34#define OCM_DISABLED    0
  35#define OCM_ENABLED             1
  36
  37struct ocm_block {
  38        struct list_head        list;
  39        void __iomem            *addr;
  40        int                                     size;
  41        const char                      *owner;
  42};
  43
  44/* non-cached or cached region */
  45struct ocm_region {
  46        phys_addr_t                     phys;
  47        void __iomem            *virt;
  48
  49        int                                     memtotal;
  50        int                                     memfree;
  51
  52        rh_info_t                       *rh;
  53        struct list_head        list;
  54};
  55
  56struct ocm_info {
  57        int                                     index;
  58        int                                     status;
  59        int                                     ready;
  60
  61        phys_addr_t                     phys;
  62
  63        int                                     alignment;
  64        int                                     memtotal;
  65        int                                     cache_size;
  66
  67        struct ocm_region       nc;     /* non-cached region */
  68        struct ocm_region       c;      /* cached region */
  69};
  70
  71static struct ocm_info *ocm_nodes;
  72static int ocm_count;
  73
  74static struct ocm_info *ocm_get_node(unsigned int index)
  75{
  76        if (index >= ocm_count) {
  77                printk(KERN_ERR "PPC4XX OCM: invalid index");
  78                return NULL;
  79        }
  80
  81        return &ocm_nodes[index];
  82}
  83
  84static int ocm_free_region(struct ocm_region *ocm_reg, const void *addr)
  85{
  86        struct ocm_block *blk, *tmp;
  87        unsigned long offset;
  88
  89        if (!ocm_reg->virt)
  90                return 0;
  91
  92        list_for_each_entry_safe(blk, tmp, &ocm_reg->list, list) {
  93                if (blk->addr == addr) {
  94                        offset = addr - ocm_reg->virt;
  95                        ocm_reg->memfree += blk->size;
  96                        rh_free(ocm_reg->rh, offset);
  97                        list_del(&blk->list);
  98                        kfree(blk);
  99                        return 1;
 100                }
 101        }
 102
 103        return 0;
 104}
 105
 106static void __init ocm_init_node(int count, struct device_node *node)
 107{
 108        struct ocm_info *ocm;
 109
 110        const unsigned int *cell_index;
 111        const unsigned int *cache_size;
 112        int len;
 113
 114        struct resource rsrc;
 115        int ioflags;
 116
 117        ocm = ocm_get_node(count);
 118
 119        cell_index = of_get_property(node, "cell-index", &len);
 120        if (!cell_index) {
 121                printk(KERN_ERR "PPC4XX OCM: missing cell-index property");
 122                return;
 123        }
 124        ocm->index = *cell_index;
 125
 126        if (of_device_is_available(node))
 127                ocm->status = OCM_ENABLED;
 128
 129        cache_size = of_get_property(node, "cached-region-size", &len);
 130        if (cache_size)
 131                ocm->cache_size = *cache_size;
 132
 133        if (of_address_to_resource(node, 0, &rsrc)) {
 134                printk(KERN_ERR "PPC4XX OCM%d: could not get resource address\n",
 135                        ocm->index);
 136                return;
 137        }
 138
 139        ocm->phys = rsrc.start;
 140        ocm->memtotal = (rsrc.end - rsrc.start + 1);
 141
 142        printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (%s)\n",
 143                ocm->index, ocm->memtotal,
 144                (ocm->status == OCM_DISABLED) ? "disabled" : "enabled");
 145
 146        if (ocm->status == OCM_DISABLED)
 147                return;
 148
 149        /* request region */
 150
 151        if (!request_mem_region(ocm->phys, ocm->memtotal, "ppc4xx_ocm")) {
 152                printk(KERN_ERR "PPC4XX OCM%d: could not request region\n",
 153                        ocm->index);
 154                return;
 155        }
 156
 157        /* Configure non-cached and cached regions */
 158
 159        ocm->nc.phys = ocm->phys;
 160        ocm->nc.memtotal = ocm->memtotal - ocm->cache_size;
 161        ocm->nc.memfree = ocm->nc.memtotal;
 162
 163        ocm->c.phys = ocm->phys + ocm->nc.memtotal;
 164        ocm->c.memtotal = ocm->cache_size;
 165        ocm->c.memfree = ocm->c.memtotal;
 166
 167        if (ocm->nc.memtotal == 0)
 168                ocm->nc.phys = 0;
 169
 170        if (ocm->c.memtotal == 0)
 171                ocm->c.phys = 0;
 172
 173        printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (non-cached)\n",
 174                ocm->index, ocm->nc.memtotal);
 175
 176        printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (cached)\n",
 177                ocm->index, ocm->c.memtotal);
 178
 179        /* ioremap the non-cached region */
 180        if (ocm->nc.memtotal) {
 181                ioflags = _PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_EXEC;
 182                ocm->nc.virt = __ioremap(ocm->nc.phys, ocm->nc.memtotal,
 183                                          ioflags);
 184
 185                if (!ocm->nc.virt) {
 186                        printk(KERN_ERR
 187                               "PPC4XX OCM%d: failed to ioremap non-cached memory\n",
 188                               ocm->index);
 189                        ocm->nc.memfree = 0;
 190                        return;
 191                }
 192        }
 193
 194        /* ioremap the cached region */
 195
 196        if (ocm->c.memtotal) {
 197                ioflags = _PAGE_EXEC;
 198                ocm->c.virt = __ioremap(ocm->c.phys, ocm->c.memtotal,
 199                                         ioflags);
 200
 201                if (!ocm->c.virt) {
 202                        printk(KERN_ERR
 203                               "PPC4XX OCM%d: failed to ioremap cached memory\n",
 204                               ocm->index);
 205                        ocm->c.memfree = 0;
 206                        return;
 207                }
 208        }
 209
 210        /* Create Remote Heaps */
 211
 212        ocm->alignment = 4; /* default 4 byte alignment */
 213
 214        if (ocm->nc.virt) {
 215                ocm->nc.rh = rh_create(ocm->alignment);
 216                rh_attach_region(ocm->nc.rh, 0, ocm->nc.memtotal);
 217        }
 218
 219        if (ocm->c.virt) {
 220                ocm->c.rh = rh_create(ocm->alignment);
 221                rh_attach_region(ocm->c.rh, 0, ocm->c.memtotal);
 222        }
 223
 224        INIT_LIST_HEAD(&ocm->nc.list);
 225        INIT_LIST_HEAD(&ocm->c.list);
 226
 227        ocm->ready = 1;
 228
 229        return;
 230}
 231
 232static int ocm_debugfs_show(struct seq_file *m, void *v)
 233{
 234        struct ocm_block *blk, *tmp;
 235        unsigned int i;
 236
 237        for (i = 0; i < ocm_count; i++) {
 238                struct ocm_info *ocm = ocm_get_node(i);
 239
 240                if (!ocm || !ocm->ready)
 241                        continue;
 242
 243                seq_printf(m, "PPC4XX OCM   : %d\n", ocm->index);
 244                seq_printf(m, "PhysAddr     : 0x%llx\n", ocm->phys);
 245                seq_printf(m, "MemTotal     : %d Bytes\n", ocm->memtotal);
 246                seq_printf(m, "MemTotal(NC) : %d Bytes\n", ocm->nc.memtotal);
 247                seq_printf(m, "MemTotal(C)  : %d Bytes\n", ocm->c.memtotal);
 248
 249                seq_printf(m, "\n");
 250
 251                seq_printf(m, "NC.PhysAddr  : 0x%llx\n", ocm->nc.phys);
 252                seq_printf(m, "NC.VirtAddr  : 0x%p\n", ocm->nc.virt);
 253                seq_printf(m, "NC.MemTotal  : %d Bytes\n", ocm->nc.memtotal);
 254                seq_printf(m, "NC.MemFree   : %d Bytes\n", ocm->nc.memfree);
 255
 256                list_for_each_entry_safe(blk, tmp, &ocm->nc.list, list) {
 257                        seq_printf(m, "NC.MemUsed   : %d Bytes (%s)\n",
 258                                                        blk->size, blk->owner);
 259                }
 260
 261                seq_printf(m, "\n");
 262
 263                seq_printf(m, "C.PhysAddr   : 0x%llx\n", ocm->c.phys);
 264                seq_printf(m, "C.VirtAddr   : 0x%p\n", ocm->c.virt);
 265                seq_printf(m, "C.MemTotal   : %d Bytes\n", ocm->c.memtotal);
 266                seq_printf(m, "C.MemFree    : %d Bytes\n", ocm->c.memfree);
 267
 268                list_for_each_entry_safe(blk, tmp, &ocm->c.list, list) {
 269                        seq_printf(m, "C.MemUsed    : %d Bytes (%s)\n",
 270                                                blk->size, blk->owner);
 271                }
 272
 273                seq_printf(m, "\n");
 274        }
 275
 276        return 0;
 277}
 278
 279static int ocm_debugfs_open(struct inode *inode, struct file *file)
 280{
 281        return single_open(file, ocm_debugfs_show, NULL);
 282}
 283
 284static const struct file_operations ocm_debugfs_fops = {
 285        .open = ocm_debugfs_open,
 286        .read = seq_read,
 287        .llseek = seq_lseek,
 288        .release = single_release,
 289};
 290
 291static int ocm_debugfs_init(void)
 292{
 293        struct dentry *junk;
 294
 295        junk = debugfs_create_dir("ppc4xx_ocm", 0);
 296        if (!junk) {
 297                printk(KERN_ALERT "debugfs ppc4xx ocm: failed to create dir\n");
 298                return -1;
 299        }
 300
 301        if (debugfs_create_file("info", 0644, junk, NULL, &ocm_debugfs_fops)) {
 302                printk(KERN_ALERT "debugfs ppc4xx ocm: failed to create file\n");
 303                return -1;
 304        }
 305
 306        return 0;
 307}
 308
 309void *ppc4xx_ocm_alloc(phys_addr_t *phys, int size, int align,
 310                        int flags, const char *owner)
 311{
 312        void __iomem *addr = NULL;
 313        unsigned long offset;
 314        struct ocm_info *ocm;
 315        struct ocm_region *ocm_reg;
 316        struct ocm_block *ocm_blk;
 317        int i;
 318
 319        for (i = 0; i < ocm_count; i++) {
 320                ocm = ocm_get_node(i);
 321
 322                if (!ocm || !ocm->ready)
 323                        continue;
 324
 325                if (flags == PPC4XX_OCM_NON_CACHED)
 326                        ocm_reg = &ocm->nc;
 327                else
 328                        ocm_reg = &ocm->c;
 329
 330                if (!ocm_reg->virt)
 331                        continue;
 332
 333                if (align < ocm->alignment)
 334                        align = ocm->alignment;
 335
 336                offset = rh_alloc_align(ocm_reg->rh, size, align, NULL);
 337
 338                if (IS_ERR_VALUE(offset))
 339                        continue;
 340
 341                ocm_blk = kzalloc(sizeof(struct ocm_block *), GFP_KERNEL);
 342                if (!ocm_blk) {
 343                        printk(KERN_ERR "PPC4XX OCM: could not allocate ocm block");
 344                        rh_free(ocm_reg->rh, offset);
 345                        break;
 346                }
 347
 348                *phys = ocm_reg->phys + offset;
 349                addr = ocm_reg->virt + offset;
 350                size = ALIGN(size, align);
 351
 352                ocm_blk->addr = addr;
 353                ocm_blk->size = size;
 354                ocm_blk->owner = owner;
 355                list_add_tail(&ocm_blk->list, &ocm_reg->list);
 356
 357                ocm_reg->memfree -= size;
 358
 359                break;
 360        }
 361
 362        return addr;
 363}
 364
 365void ppc4xx_ocm_free(const void *addr)
 366{
 367        int i;
 368
 369        if (!addr)
 370                return;
 371
 372        for (i = 0; i < ocm_count; i++) {
 373                struct ocm_info *ocm = ocm_get_node(i);
 374
 375                if (!ocm || !ocm->ready)
 376                        continue;
 377
 378                if (ocm_free_region(&ocm->nc, addr) ||
 379                        ocm_free_region(&ocm->c, addr))
 380                        return;
 381        }
 382}
 383
 384static int __init ppc4xx_ocm_init(void)
 385{
 386        struct device_node *np;
 387        int count;
 388
 389        count = 0;
 390        for_each_compatible_node(np, NULL, "ibm,ocm")
 391                count++;
 392
 393        if (!count)
 394                return 0;
 395
 396        ocm_nodes = kzalloc((count * sizeof(struct ocm_info)), GFP_KERNEL);
 397        if (!ocm_nodes) {
 398                printk(KERN_ERR "PPC4XX OCM: failed to allocate OCM nodes!\n");
 399                return -ENOMEM;
 400        }
 401
 402        ocm_count = count;
 403        count = 0;
 404
 405        for_each_compatible_node(np, NULL, "ibm,ocm") {
 406                ocm_init_node(count, np);
 407                count++;
 408        }
 409
 410        ocm_debugfs_init();
 411
 412        return 0;
 413}
 414
 415arch_initcall(ppc4xx_ocm_init);
 416