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