diff options
author | Vinh Nguyen Huu Tuong <vhtnguyen@apm.com> | 2012-07-02 18:52:30 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-01-09 23:08:37 -0500 |
commit | c19d82486216b1c674208f76fed6b9eb4ee45ad3 (patch) | |
tree | ddaa0f230e5be8b84e349c1aed873410e190c00a /arch/powerpc/sysdev | |
parent | 0388c79c99ccb43f711af57d2e14fcd6a5f45a06 (diff) |
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>
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r-- | arch/powerpc/sysdev/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_ocm.c | 415 |
2 files changed, 416 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index a57600b3a4e3..f44f09f3d527 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile | |||
@@ -37,6 +37,7 @@ obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o | |||
37 | obj-$(CONFIG_PPC_I8259) += i8259.o | 37 | obj-$(CONFIG_PPC_I8259) += i8259.o |
38 | obj-$(CONFIG_IPIC) += ipic.o | 38 | obj-$(CONFIG_IPIC) += ipic.o |
39 | obj-$(CONFIG_4xx) += uic.o | 39 | obj-$(CONFIG_4xx) += uic.o |
40 | obj-$(CONFIG_PPC4xx_OCM) += ppc4xx_ocm.o | ||
40 | obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o | 41 | obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o |
41 | obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o | 42 | obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o |
42 | obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o | 43 | obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o |
diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c new file mode 100644 index 000000000000..1b15f93479c3 --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c | |||
@@ -0,0 +1,415 @@ | |||
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); | ||