diff options
author | Arnd Bergmann <arnd@arndb.de> | 2011-11-01 21:46:10 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2011-11-01 21:46:10 -0400 |
commit | 884897e6a10a86de0e611a76f4bf3fd0068a672c (patch) | |
tree | 019689fb9d129404af1efba4df10975bc9e0a57a /arch/arm/mm | |
parent | c72dbae971400e466ad9ff16c920cd6d9d8c55a1 (diff) | |
parent | 8b61f37440388ebbd2a894178fe098f2e70ab392 (diff) |
Merge branch 'highbank/soc' into next/soc
Conflicts:
arch/arm/mach-mxs/include/mach/gpio.h
arch/arm/mach-omap2/board-generic.c
arch/arm/plat-mxc/include/mach/gpio.h
Diffstat (limited to 'arch/arm/mm')
-rw-r--r-- | arch/arm/mm/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mm/cache-l2x0.c | 216 |
2 files changed, 215 insertions, 3 deletions
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 88633fe01a5d..7d5fff7b3632 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig | |||
@@ -822,7 +822,7 @@ config CACHE_L2X0 | |||
822 | REALVIEW_EB_A9MP || SOC_IMX35 || SOC_IMX31 || MACH_REALVIEW_PBX || \ | 822 | REALVIEW_EB_A9MP || SOC_IMX35 || SOC_IMX31 || MACH_REALVIEW_PBX || \ |
823 | ARCH_NOMADIK || ARCH_OMAP4 || ARCH_EXYNOS4 || ARCH_TEGRA || \ | 823 | ARCH_NOMADIK || ARCH_OMAP4 || ARCH_EXYNOS4 || ARCH_TEGRA || \ |
824 | ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE || \ | 824 | ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE || \ |
825 | ARCH_PRIMA2 || ARCH_ZYNQ || ARCH_CNS3XXX | 825 | ARCH_PRIMA2 || ARCH_ZYNQ || ARCH_CNS3XXX || ARCH_HIGHBANK |
826 | default y | 826 | default y |
827 | select OUTER_CACHE | 827 | select OUTER_CACHE |
828 | select OUTER_CACHE_SYNC | 828 | select OUTER_CACHE_SYNC |
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 9ecfdb511951..3f9b9980478e 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c | |||
@@ -16,9 +16,12 @@ | |||
16 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | */ | 18 | */ |
19 | #include <linux/err.h> | ||
19 | #include <linux/init.h> | 20 | #include <linux/init.h> |
20 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
21 | #include <linux/io.h> | 22 | #include <linux/io.h> |
23 | #include <linux/of.h> | ||
24 | #include <linux/of_address.h> | ||
22 | 25 | ||
23 | #include <asm/cacheflush.h> | 26 | #include <asm/cacheflush.h> |
24 | #include <asm/hardware/cache-l2x0.h> | 27 | #include <asm/hardware/cache-l2x0.h> |
@@ -30,11 +33,19 @@ static DEFINE_SPINLOCK(l2x0_lock); | |||
30 | static uint32_t l2x0_way_mask; /* Bitmask of active ways */ | 33 | static uint32_t l2x0_way_mask; /* Bitmask of active ways */ |
31 | static uint32_t l2x0_size; | 34 | static uint32_t l2x0_size; |
32 | 35 | ||
36 | struct l2x0_regs l2x0_saved_regs; | ||
37 | |||
38 | struct l2x0_of_data { | ||
39 | void (*setup)(const struct device_node *, __u32 *, __u32 *); | ||
40 | void (*save)(void); | ||
41 | void (*resume)(void); | ||
42 | }; | ||
43 | |||
33 | static inline void cache_wait_way(void __iomem *reg, unsigned long mask) | 44 | static inline void cache_wait_way(void __iomem *reg, unsigned long mask) |
34 | { | 45 | { |
35 | /* wait for cache operation by line or way to complete */ | 46 | /* wait for cache operation by line or way to complete */ |
36 | while (readl_relaxed(reg) & mask) | 47 | while (readl_relaxed(reg) & mask) |
37 | ; | 48 | cpu_relax(); |
38 | } | 49 | } |
39 | 50 | ||
40 | #ifdef CONFIG_CACHE_PL310 | 51 | #ifdef CONFIG_CACHE_PL310 |
@@ -277,7 +288,7 @@ static void l2x0_disable(void) | |||
277 | spin_unlock_irqrestore(&l2x0_lock, flags); | 288 | spin_unlock_irqrestore(&l2x0_lock, flags); |
278 | } | 289 | } |
279 | 290 | ||
280 | static void __init l2x0_unlock(__u32 cache_id) | 291 | static void l2x0_unlock(__u32 cache_id) |
281 | { | 292 | { |
282 | int lockregs; | 293 | int lockregs; |
283 | int i; | 294 | int i; |
@@ -353,6 +364,8 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) | |||
353 | /* l2x0 controller is disabled */ | 364 | /* l2x0 controller is disabled */ |
354 | writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); | 365 | writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); |
355 | 366 | ||
367 | l2x0_saved_regs.aux_ctrl = aux; | ||
368 | |||
356 | l2x0_inv_all(); | 369 | l2x0_inv_all(); |
357 | 370 | ||
358 | /* enable L2X0 */ | 371 | /* enable L2X0 */ |
@@ -372,3 +385,202 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) | |||
372 | printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n", | 385 | printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n", |
373 | ways, cache_id, aux, l2x0_size); | 386 | ways, cache_id, aux, l2x0_size); |
374 | } | 387 | } |
388 | |||
389 | #ifdef CONFIG_OF | ||
390 | static void __init l2x0_of_setup(const struct device_node *np, | ||
391 | __u32 *aux_val, __u32 *aux_mask) | ||
392 | { | ||
393 | u32 data[2] = { 0, 0 }; | ||
394 | u32 tag = 0; | ||
395 | u32 dirty = 0; | ||
396 | u32 val = 0, mask = 0; | ||
397 | |||
398 | of_property_read_u32(np, "arm,tag-latency", &tag); | ||
399 | if (tag) { | ||
400 | mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK; | ||
401 | val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT; | ||
402 | } | ||
403 | |||
404 | of_property_read_u32_array(np, "arm,data-latency", | ||
405 | data, ARRAY_SIZE(data)); | ||
406 | if (data[0] && data[1]) { | ||
407 | mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK | | ||
408 | L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK; | ||
409 | val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) | | ||
410 | ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT); | ||
411 | } | ||
412 | |||
413 | of_property_read_u32(np, "arm,dirty-latency", &dirty); | ||
414 | if (dirty) { | ||
415 | mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK; | ||
416 | val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT; | ||
417 | } | ||
418 | |||
419 | *aux_val &= ~mask; | ||
420 | *aux_val |= val; | ||
421 | *aux_mask &= ~mask; | ||
422 | } | ||
423 | |||
424 | static void __init pl310_of_setup(const struct device_node *np, | ||
425 | __u32 *aux_val, __u32 *aux_mask) | ||
426 | { | ||
427 | u32 data[3] = { 0, 0, 0 }; | ||
428 | u32 tag[3] = { 0, 0, 0 }; | ||
429 | u32 filter[2] = { 0, 0 }; | ||
430 | |||
431 | of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); | ||
432 | if (tag[0] && tag[1] && tag[2]) | ||
433 | writel_relaxed( | ||
434 | ((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | | ||
435 | ((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | | ||
436 | ((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), | ||
437 | l2x0_base + L2X0_TAG_LATENCY_CTRL); | ||
438 | |||
439 | of_property_read_u32_array(np, "arm,data-latency", | ||
440 | data, ARRAY_SIZE(data)); | ||
441 | if (data[0] && data[1] && data[2]) | ||
442 | writel_relaxed( | ||
443 | ((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | | ||
444 | ((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | | ||
445 | ((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), | ||
446 | l2x0_base + L2X0_DATA_LATENCY_CTRL); | ||
447 | |||
448 | of_property_read_u32_array(np, "arm,filter-ranges", | ||
449 | filter, ARRAY_SIZE(filter)); | ||
450 | if (filter[1]) { | ||
451 | writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M), | ||
452 | l2x0_base + L2X0_ADDR_FILTER_END); | ||
453 | writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN, | ||
454 | l2x0_base + L2X0_ADDR_FILTER_START); | ||
455 | } | ||
456 | } | ||
457 | |||
458 | static void __init pl310_save(void) | ||
459 | { | ||
460 | u32 l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & | ||
461 | L2X0_CACHE_ID_RTL_MASK; | ||
462 | |||
463 | l2x0_saved_regs.tag_latency = readl_relaxed(l2x0_base + | ||
464 | L2X0_TAG_LATENCY_CTRL); | ||
465 | l2x0_saved_regs.data_latency = readl_relaxed(l2x0_base + | ||
466 | L2X0_DATA_LATENCY_CTRL); | ||
467 | l2x0_saved_regs.filter_end = readl_relaxed(l2x0_base + | ||
468 | L2X0_ADDR_FILTER_END); | ||
469 | l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base + | ||
470 | L2X0_ADDR_FILTER_START); | ||
471 | |||
472 | if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) { | ||
473 | /* | ||
474 | * From r2p0, there is Prefetch offset/control register | ||
475 | */ | ||
476 | l2x0_saved_regs.prefetch_ctrl = readl_relaxed(l2x0_base + | ||
477 | L2X0_PREFETCH_CTRL); | ||
478 | /* | ||
479 | * From r3p0, there is Power control register | ||
480 | */ | ||
481 | if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0) | ||
482 | l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base + | ||
483 | L2X0_POWER_CTRL); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void l2x0_resume(void) | ||
488 | { | ||
489 | if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { | ||
490 | /* restore aux ctrl and enable l2 */ | ||
491 | l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID)); | ||
492 | |||
493 | writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base + | ||
494 | L2X0_AUX_CTRL); | ||
495 | |||
496 | l2x0_inv_all(); | ||
497 | |||
498 | writel_relaxed(1, l2x0_base + L2X0_CTRL); | ||
499 | } | ||
500 | } | ||
501 | |||
502 | static void pl310_resume(void) | ||
503 | { | ||
504 | u32 l2x0_revision; | ||
505 | |||
506 | if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { | ||
507 | /* restore pl310 setup */ | ||
508 | writel_relaxed(l2x0_saved_regs.tag_latency, | ||
509 | l2x0_base + L2X0_TAG_LATENCY_CTRL); | ||
510 | writel_relaxed(l2x0_saved_regs.data_latency, | ||
511 | l2x0_base + L2X0_DATA_LATENCY_CTRL); | ||
512 | writel_relaxed(l2x0_saved_regs.filter_end, | ||
513 | l2x0_base + L2X0_ADDR_FILTER_END); | ||
514 | writel_relaxed(l2x0_saved_regs.filter_start, | ||
515 | l2x0_base + L2X0_ADDR_FILTER_START); | ||
516 | |||
517 | l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & | ||
518 | L2X0_CACHE_ID_RTL_MASK; | ||
519 | |||
520 | if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) { | ||
521 | writel_relaxed(l2x0_saved_regs.prefetch_ctrl, | ||
522 | l2x0_base + L2X0_PREFETCH_CTRL); | ||
523 | if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0) | ||
524 | writel_relaxed(l2x0_saved_regs.pwr_ctrl, | ||
525 | l2x0_base + L2X0_POWER_CTRL); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | l2x0_resume(); | ||
530 | } | ||
531 | |||
532 | static const struct l2x0_of_data pl310_data = { | ||
533 | pl310_of_setup, | ||
534 | pl310_save, | ||
535 | pl310_resume, | ||
536 | }; | ||
537 | |||
538 | static const struct l2x0_of_data l2x0_data = { | ||
539 | l2x0_of_setup, | ||
540 | NULL, | ||
541 | l2x0_resume, | ||
542 | }; | ||
543 | |||
544 | static const struct of_device_id l2x0_ids[] __initconst = { | ||
545 | { .compatible = "arm,pl310-cache", .data = (void *)&pl310_data }, | ||
546 | { .compatible = "arm,l220-cache", .data = (void *)&l2x0_data }, | ||
547 | { .compatible = "arm,l210-cache", .data = (void *)&l2x0_data }, | ||
548 | {} | ||
549 | }; | ||
550 | |||
551 | int __init l2x0_of_init(__u32 aux_val, __u32 aux_mask) | ||
552 | { | ||
553 | struct device_node *np; | ||
554 | struct l2x0_of_data *data; | ||
555 | struct resource res; | ||
556 | |||
557 | np = of_find_matching_node(NULL, l2x0_ids); | ||
558 | if (!np) | ||
559 | return -ENODEV; | ||
560 | |||
561 | if (of_address_to_resource(np, 0, &res)) | ||
562 | return -ENODEV; | ||
563 | |||
564 | l2x0_base = ioremap(res.start, resource_size(&res)); | ||
565 | if (!l2x0_base) | ||
566 | return -ENOMEM; | ||
567 | |||
568 | l2x0_saved_regs.phy_base = res.start; | ||
569 | |||
570 | data = of_match_node(l2x0_ids, np)->data; | ||
571 | |||
572 | /* L2 configuration can only be changed if the cache is disabled */ | ||
573 | if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { | ||
574 | if (data->setup) | ||
575 | data->setup(np, &aux_val, &aux_mask); | ||
576 | } | ||
577 | |||
578 | if (data->save) | ||
579 | data->save(); | ||
580 | |||
581 | l2x0_init(l2x0_base, aux_val, aux_mask); | ||
582 | |||
583 | outer_cache.resume = data->resume; | ||
584 | return 0; | ||
585 | } | ||
586 | #endif | ||