diff options
author | James Hogan <james.hogan@imgtec.com> | 2012-10-09 05:54:17 -0400 |
---|---|---|
committer | James Hogan <james.hogan@imgtec.com> | 2013-03-02 15:09:21 -0500 |
commit | c438b58e65462cfff172b396d03d6bc45c971fca (patch) | |
tree | 3d8ea3cfbe75f4e6b41f5f41f36c889df5b0f7d8 /arch | |
parent | bbc17704d5d3a8e4325dae74c2a2e77218c26057 (diff) |
metag: TCM support
Add some TCM support
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/metag/include/asm/mmzone.h | 42 | ||||
-rw-r--r-- | arch/metag/include/asm/sparsemem.h | 13 | ||||
-rw-r--r-- | arch/metag/include/asm/tcm.h | 30 | ||||
-rw-r--r-- | arch/metag/kernel/tcm.c | 151 | ||||
-rw-r--r-- | arch/metag/mm/numa.c | 81 |
5 files changed, 317 insertions, 0 deletions
diff --git a/arch/metag/include/asm/mmzone.h b/arch/metag/include/asm/mmzone.h new file mode 100644 index 000000000000..9c88a9c65f59 --- /dev/null +++ b/arch/metag/include/asm/mmzone.h | |||
@@ -0,0 +1,42 @@ | |||
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 */ | ||
diff --git a/arch/metag/include/asm/sparsemem.h b/arch/metag/include/asm/sparsemem.h new file mode 100644 index 000000000000..03fe255d697a --- /dev/null +++ b/arch/metag/include/asm/sparsemem.h | |||
@@ -0,0 +1,13 @@ | |||
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 */ | ||
diff --git a/arch/metag/include/asm/tcm.h b/arch/metag/include/asm/tcm.h new file mode 100644 index 000000000000..7711c317b1d2 --- /dev/null +++ b/arch/metag/include/asm/tcm.h | |||
@@ -0,0 +1,30 @@ | |||
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 | ||
diff --git a/arch/metag/kernel/tcm.c b/arch/metag/kernel/tcm.c new file mode 100644 index 000000000000..5d102b31ce84 --- /dev/null +++ b/arch/metag/kernel/tcm.c | |||
@@ -0,0 +1,151 @@ | |||
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 | } | ||
diff --git a/arch/metag/mm/numa.c b/arch/metag/mm/numa.c new file mode 100644 index 000000000000..9ae578c9b620 --- /dev/null +++ b/arch/metag/mm/numa.c | |||
@@ -0,0 +1,81 @@ | |||
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 | } | ||