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

powerpc/44x: Support OCM(On Chip Memory) for APM821xx SoC and Bluestone board

This patch consists of:
- Add driver for OCM component
- Export OCM Information at /sys/kernel/debug/ppc4xx_ocm/info

Signed-off-by: Vinh Nguyen Huu Tuong <vhtnguyen@apm.com>
Acked-by: Josh Boyer <jwboyer@gmail.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Vinh Nguyen Huu Tuong and committed by
Benjamin Herrenschmidt
c19d8248 0388c79c

+477
+8
arch/powerpc/boot/dts/bluestone.dts
··· 107 107 interrupt-parent = <&UIC0>; 108 108 }; 109 109 110 + OCM: ocm@400040000 { 111 + compatible = "ibm,ocm"; 112 + status = "ok"; 113 + cell-index = <1>; 114 + /* configured in U-Boot */ 115 + reg = <4 0x00040000 0x8000>; /* 32K */ 116 + }; 117 + 110 118 SDR0: sdr { 111 119 compatible = "ibm,sdr-apm821xx"; 112 120 dcr-reg = <0x00e 0x002>;
+45
arch/powerpc/include/asm/ppc4xx_ocm.h
··· 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 + #ifndef __ASM_POWERPC_PPC4XX_OCM_H__ 27 + #define __ASM_POWERPC_PPC4XX_OCM_H__ 28 + 29 + #define PPC4XX_OCM_NON_CACHED 0 30 + #define PPC4XX_OCM_CACHED 1 31 + 32 + #if defined(CONFIG_PPC4xx_OCM) 33 + 34 + void *ppc4xx_ocm_alloc(phys_addr_t *phys, int size, int align, 35 + int flags, const char *owner); 36 + void ppc4xx_ocm_free(const void *virt); 37 + 38 + #else 39 + 40 + #define ppc4xx_ocm_alloc(phys, size, align, flags, owner) NULL 41 + #define ppc4xx_ocm_free(addr) ((void)0) 42 + 43 + #endif /* CONFIG_PPC4xx_OCM */ 44 + 45 + #endif /* __ASM_POWERPC_PPC4XX_OCM_H__ */
+8
arch/powerpc/platforms/44x/Kconfig
··· 252 252 help 253 253 Enable gpiolib support for ppc440 based boards 254 254 255 + config PPC4xx_OCM 256 + bool "PPC4xx On Chip Memory (OCM) support" 257 + depends on 4xx 258 + select PPC_LIB_RHEAP 259 + help 260 + Enable OCM support for PowerPC 4xx platforms with on chip memory, 261 + OCM provides the fast place for memory access to improve performance. 262 + 255 263 # 44x specific CPU modules, selected based on the board above. 256 264 config 440EP 257 265 bool
+1
arch/powerpc/sysdev/Makefile
··· 37 37 obj-$(CONFIG_PPC_I8259) += i8259.o 38 38 obj-$(CONFIG_IPIC) += ipic.o 39 39 obj-$(CONFIG_4xx) += uic.o 40 + obj-$(CONFIG_PPC4xx_OCM) += ppc4xx_ocm.o 40 41 obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o 41 42 obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o 42 43 obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o
+415
arch/powerpc/sysdev/ppc4xx_ocm.c
··· 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 + 37 + struct 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 */ 45 + struct 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 + 56 + struct 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 + 71 + static struct ocm_info *ocm_nodes; 72 + static int ocm_count; 73 + 74 + static 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 + 84 + static 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 + 106 + static 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 + 232 + static 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 + 279 + static int ocm_debugfs_open(struct inode *inode, struct file *file) 280 + { 281 + return single_open(file, ocm_debugfs_show, NULL); 282 + } 283 + 284 + static 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 + 291 + static 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 + 309 + void *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 + 365 + void 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 + 384 + static 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 + 415 + arch_initcall(ppc4xx_ocm_init);