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

metag: TCM support

Add some TCM support

Signed-off-by: James Hogan <james.hogan@imgtec.com>

+317
+42
arch/metag/include/asm/mmzone.h
··· 1 + #ifndef __ASM_METAG_MMZONE_H 2 + #define __ASM_METAG_MMZONE_H 3 + 4 + #ifdef CONFIG_NEED_MULTIPLE_NODES 5 + #include <linux/numa.h> 6 + 7 + extern struct pglist_data *node_data[]; 8 + #define NODE_DATA(nid) (node_data[nid]) 9 + 10 + static inline int pfn_to_nid(unsigned long pfn) 11 + { 12 + int nid; 13 + 14 + for (nid = 0; nid < MAX_NUMNODES; nid++) 15 + if (pfn >= node_start_pfn(nid) && pfn <= node_end_pfn(nid)) 16 + break; 17 + 18 + return nid; 19 + } 20 + 21 + static inline struct pglist_data *pfn_to_pgdat(unsigned long pfn) 22 + { 23 + return NODE_DATA(pfn_to_nid(pfn)); 24 + } 25 + 26 + /* arch/metag/mm/numa.c */ 27 + void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end); 28 + #else 29 + static inline void 30 + setup_bootmem_node(int nid, unsigned long start, unsigned long end) 31 + { 32 + } 33 + #endif /* CONFIG_NEED_MULTIPLE_NODES */ 34 + 35 + #ifdef CONFIG_NUMA 36 + /* SoC specific mem init */ 37 + void __init soc_mem_setup(void); 38 + #else 39 + static inline void __init soc_mem_setup(void) {}; 40 + #endif 41 + 42 + #endif /* __ASM_METAG_MMZONE_H */
+13
arch/metag/include/asm/sparsemem.h
··· 1 + #ifndef __ASM_METAG_SPARSEMEM_H 2 + #define __ASM_METAG_SPARSEMEM_H 3 + 4 + /* 5 + * SECTION_SIZE_BITS 2^N: how big each section will be 6 + * MAX_PHYSADDR_BITS 2^N: how much physical address space we have 7 + * MAX_PHYSMEM_BITS 2^N: how much memory we can have in that space 8 + */ 9 + #define SECTION_SIZE_BITS 26 10 + #define MAX_PHYSADDR_BITS 32 11 + #define MAX_PHYSMEM_BITS 32 12 + 13 + #endif /* __ASM_METAG_SPARSEMEM_H */
+30
arch/metag/include/asm/tcm.h
··· 1 + #ifndef __ASM_TCM_H__ 2 + #define __ASM_TCM_H__ 3 + 4 + #include <linux/ioport.h> 5 + #include <linux/list.h> 6 + 7 + struct tcm_allocation { 8 + struct list_head list; 9 + unsigned int tag; 10 + unsigned long addr; 11 + unsigned long size; 12 + }; 13 + 14 + /* 15 + * TCM memory region descriptor. 16 + */ 17 + struct tcm_region { 18 + unsigned int tag; 19 + struct resource res; 20 + }; 21 + 22 + #define TCM_INVALID_TAG 0xffffffff 23 + 24 + unsigned long tcm_alloc(unsigned int tag, size_t len); 25 + void tcm_free(unsigned int tag, unsigned long addr, size_t len); 26 + unsigned int tcm_lookup_tag(unsigned long p); 27 + 28 + int tcm_add_region(struct tcm_region *reg); 29 + 30 + #endif
+151
arch/metag/kernel/tcm.c
··· 1 + /* 2 + * Copyright (C) 2010 Imagination Technologies Ltd. 3 + */ 4 + 5 + #include <linux/init.h> 6 + #include <linux/kernel.h> 7 + #include <linux/spinlock.h> 8 + #include <linux/stddef.h> 9 + #include <linux/genalloc.h> 10 + #include <linux/string.h> 11 + #include <linux/list.h> 12 + #include <linux/slab.h> 13 + #include <asm/page.h> 14 + #include <asm/tcm.h> 15 + 16 + struct tcm_pool { 17 + struct list_head list; 18 + unsigned int tag; 19 + unsigned long start; 20 + unsigned long end; 21 + struct gen_pool *pool; 22 + }; 23 + 24 + static LIST_HEAD(pool_list); 25 + 26 + static struct tcm_pool *find_pool(unsigned int tag) 27 + { 28 + struct list_head *lh; 29 + struct tcm_pool *pool; 30 + 31 + list_for_each(lh, &pool_list) { 32 + pool = list_entry(lh, struct tcm_pool, list); 33 + if (pool->tag == tag) 34 + return pool; 35 + } 36 + 37 + return NULL; 38 + } 39 + 40 + /** 41 + * tcm_alloc - allocate memory from a TCM pool 42 + * @tag: tag of the pool to allocate memory from 43 + * @len: number of bytes to be allocated 44 + * 45 + * Allocate the requested number of bytes from the pool matching 46 + * the specified tag. Returns the address of the allocated memory 47 + * or zero on failure. 48 + */ 49 + unsigned long tcm_alloc(unsigned int tag, size_t len) 50 + { 51 + unsigned long vaddr; 52 + struct tcm_pool *pool; 53 + 54 + pool = find_pool(tag); 55 + if (!pool) 56 + return 0; 57 + 58 + vaddr = gen_pool_alloc(pool->pool, len); 59 + if (!vaddr) 60 + return 0; 61 + 62 + return vaddr; 63 + } 64 + 65 + /** 66 + * tcm_free - free a block of memory to a TCM pool 67 + * @tag: tag of the pool to free memory to 68 + * @addr: address of the memory to be freed 69 + * @len: number of bytes to be freed 70 + * 71 + * Free the requested number of bytes at a specific address to the 72 + * pool matching the specified tag. 73 + */ 74 + void tcm_free(unsigned int tag, unsigned long addr, size_t len) 75 + { 76 + struct tcm_pool *pool; 77 + 78 + pool = find_pool(tag); 79 + if (!pool) 80 + return; 81 + gen_pool_free(pool->pool, addr, len); 82 + } 83 + 84 + /** 85 + * tcm_lookup_tag - find the tag matching an address 86 + * @p: memory address to lookup the tag for 87 + * 88 + * Find the tag of the tcm memory region that contains the 89 + * specified address. Returns %TCM_INVALID_TAG if no such 90 + * memory region could be found. 91 + */ 92 + unsigned int tcm_lookup_tag(unsigned long p) 93 + { 94 + struct list_head *lh; 95 + struct tcm_pool *pool; 96 + unsigned long addr = (unsigned long) p; 97 + 98 + list_for_each(lh, &pool_list) { 99 + pool = list_entry(lh, struct tcm_pool, list); 100 + if (addr >= pool->start && addr < pool->end) 101 + return pool->tag; 102 + } 103 + 104 + return TCM_INVALID_TAG; 105 + } 106 + 107 + /** 108 + * tcm_add_region - add a memory region to TCM pool list 109 + * @reg: descriptor of region to be added 110 + * 111 + * Add a region of memory to the TCM pool list. Returns 0 on success. 112 + */ 113 + int __init tcm_add_region(struct tcm_region *reg) 114 + { 115 + struct tcm_pool *pool; 116 + 117 + pool = kmalloc(sizeof(*pool), GFP_KERNEL); 118 + if (!pool) { 119 + pr_err("Failed to alloc memory for TCM pool!\n"); 120 + return -ENOMEM; 121 + } 122 + 123 + pool->tag = reg->tag; 124 + pool->start = reg->res.start; 125 + pool->end = reg->res.end; 126 + 127 + /* 128 + * 2^3 = 8 bytes granularity to allow for 64bit access alignment. 129 + * -1 = NUMA node specifier. 130 + */ 131 + pool->pool = gen_pool_create(3, -1); 132 + 133 + if (!pool->pool) { 134 + pr_err("Failed to create TCM pool!\n"); 135 + kfree(pool); 136 + return -ENOMEM; 137 + } 138 + 139 + if (gen_pool_add(pool->pool, reg->res.start, 140 + reg->res.end - reg->res.start + 1, -1)) { 141 + pr_err("Failed to add memory to TCM pool!\n"); 142 + return -ENOMEM; 143 + } 144 + pr_info("Added %s TCM pool (%08x bytes @ %08x)\n", 145 + reg->res.name, reg->res.end - reg->res.start + 1, 146 + reg->res.start); 147 + 148 + list_add_tail(&pool->list, &pool_list); 149 + 150 + return 0; 151 + }
+81
arch/metag/mm/numa.c
··· 1 + /* 2 + * Multiple memory node support for Meta machines 3 + * 4 + * Copyright (C) 2007 Paul Mundt 5 + * Copyright (C) 2010 Imagination Technologies Ltd. 6 + * 7 + * This file is subject to the terms and conditions of the GNU General Public 8 + * License. See the file "COPYING" in the main directory of this archive 9 + * for more details. 10 + */ 11 + #include <linux/export.h> 12 + #include <linux/bootmem.h> 13 + #include <linux/memblock.h> 14 + #include <linux/mm.h> 15 + #include <linux/numa.h> 16 + #include <linux/pfn.h> 17 + #include <asm/sections.h> 18 + 19 + struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; 20 + EXPORT_SYMBOL_GPL(node_data); 21 + 22 + extern char _heap_start[]; 23 + 24 + /* 25 + * On Meta machines the conventional approach is to stash system RAM 26 + * in node 0, and other memory blocks in to node 1 and up, ordered by 27 + * latency. Each node's pgdat is node-local at the beginning of the node, 28 + * immediately followed by the node mem map. 29 + */ 30 + void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) 31 + { 32 + unsigned long bootmap_pages, bootmem_paddr; 33 + unsigned long start_pfn, end_pfn; 34 + unsigned long pgdat_paddr; 35 + 36 + /* Don't allow bogus node assignment */ 37 + BUG_ON(nid > MAX_NUMNODES || nid <= 0); 38 + 39 + start_pfn = start >> PAGE_SHIFT; 40 + end_pfn = end >> PAGE_SHIFT; 41 + 42 + memblock_add(start, end - start); 43 + 44 + memblock_set_node(PFN_PHYS(start_pfn), 45 + PFN_PHYS(end_pfn - start_pfn), nid); 46 + 47 + /* Node-local pgdat */ 48 + pgdat_paddr = memblock_alloc_base(sizeof(struct pglist_data), 49 + SMP_CACHE_BYTES, end); 50 + NODE_DATA(nid) = __va(pgdat_paddr); 51 + memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); 52 + 53 + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; 54 + NODE_DATA(nid)->node_start_pfn = start_pfn; 55 + NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; 56 + 57 + /* Node-local bootmap */ 58 + bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); 59 + bootmem_paddr = memblock_alloc_base(bootmap_pages << PAGE_SHIFT, 60 + PAGE_SIZE, end); 61 + init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT, 62 + start_pfn, end_pfn); 63 + 64 + free_bootmem_with_active_regions(nid, end_pfn); 65 + 66 + /* Reserve the pgdat and bootmap space with the bootmem allocator */ 67 + reserve_bootmem_node(NODE_DATA(nid), pgdat_paddr & PAGE_MASK, 68 + sizeof(struct pglist_data), BOOTMEM_DEFAULT); 69 + reserve_bootmem_node(NODE_DATA(nid), bootmem_paddr, 70 + bootmap_pages << PAGE_SHIFT, BOOTMEM_DEFAULT); 71 + 72 + /* It's up */ 73 + node_set_online(nid); 74 + 75 + /* Kick sparsemem */ 76 + sparse_memory_present_with_active_regions(nid); 77 + } 78 + 79 + void __init __weak soc_mem_setup(void) 80 + { 81 + }