Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.12-rc3 415 lines 9.2 kB view raw
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);