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