diff options
Diffstat (limited to 'arch/arm/mm/cache-l2x0.c')
-rw-r--r-- | arch/arm/mm/cache-l2x0.c | 129 |
1 files changed, 119 insertions, 10 deletions
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 0d85d221d7b0..3f9b9980478e 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c | |||
@@ -33,6 +33,14 @@ static DEFINE_SPINLOCK(l2x0_lock); | |||
33 | static uint32_t l2x0_way_mask; /* Bitmask of active ways */ | 33 | static uint32_t l2x0_way_mask; /* Bitmask of active ways */ |
34 | static uint32_t l2x0_size; | 34 | static uint32_t l2x0_size; |
35 | 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 | |||
36 | 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) |
37 | { | 45 | { |
38 | /* wait for cache operation by line or way to complete */ | 46 | /* wait for cache operation by line or way to complete */ |
@@ -280,7 +288,7 @@ static void l2x0_disable(void) | |||
280 | spin_unlock_irqrestore(&l2x0_lock, flags); | 288 | spin_unlock_irqrestore(&l2x0_lock, flags); |
281 | } | 289 | } |
282 | 290 | ||
283 | static void __init l2x0_unlock(__u32 cache_id) | 291 | static void l2x0_unlock(__u32 cache_id) |
284 | { | 292 | { |
285 | int lockregs; | 293 | int lockregs; |
286 | int i; | 294 | int i; |
@@ -356,6 +364,8 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) | |||
356 | /* l2x0 controller is disabled */ | 364 | /* l2x0 controller is disabled */ |
357 | writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); | 365 | writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); |
358 | 366 | ||
367 | l2x0_saved_regs.aux_ctrl = aux; | ||
368 | |||
359 | l2x0_inv_all(); | 369 | l2x0_inv_all(); |
360 | 370 | ||
361 | /* enable L2X0 */ | 371 | /* enable L2X0 */ |
@@ -445,33 +455,132 @@ static void __init pl310_of_setup(const struct device_node *np, | |||
445 | } | 455 | } |
446 | } | 456 | } |
447 | 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 | |||
448 | static const struct of_device_id l2x0_ids[] __initconst = { | 544 | static const struct of_device_id l2x0_ids[] __initconst = { |
449 | { .compatible = "arm,pl310-cache", .data = pl310_of_setup }, | 545 | { .compatible = "arm,pl310-cache", .data = (void *)&pl310_data }, |
450 | { .compatible = "arm,l220-cache", .data = l2x0_of_setup }, | 546 | { .compatible = "arm,l220-cache", .data = (void *)&l2x0_data }, |
451 | { .compatible = "arm,l210-cache", .data = l2x0_of_setup }, | 547 | { .compatible = "arm,l210-cache", .data = (void *)&l2x0_data }, |
452 | {} | 548 | {} |
453 | }; | 549 | }; |
454 | 550 | ||
455 | int __init l2x0_of_init(__u32 aux_val, __u32 aux_mask) | 551 | int __init l2x0_of_init(__u32 aux_val, __u32 aux_mask) |
456 | { | 552 | { |
457 | struct device_node *np; | 553 | struct device_node *np; |
458 | void (*l2_setup)(const struct device_node *np, | 554 | struct l2x0_of_data *data; |
459 | __u32 *aux_val, __u32 *aux_mask); | 555 | struct resource res; |
460 | 556 | ||
461 | np = of_find_matching_node(NULL, l2x0_ids); | 557 | np = of_find_matching_node(NULL, l2x0_ids); |
462 | if (!np) | 558 | if (!np) |
463 | return -ENODEV; | 559 | return -ENODEV; |
464 | l2x0_base = of_iomap(np, 0); | 560 | |
561 | if (of_address_to_resource(np, 0, &res)) | ||
562 | return -ENODEV; | ||
563 | |||
564 | l2x0_base = ioremap(res.start, resource_size(&res)); | ||
465 | if (!l2x0_base) | 565 | if (!l2x0_base) |
466 | return -ENOMEM; | 566 | return -ENOMEM; |
467 | 567 | ||
568 | l2x0_saved_regs.phy_base = res.start; | ||
569 | |||
570 | data = of_match_node(l2x0_ids, np)->data; | ||
571 | |||
468 | /* L2 configuration can only be changed if the cache is disabled */ | 572 | /* L2 configuration can only be changed if the cache is disabled */ |
469 | if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { | 573 | if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { |
470 | l2_setup = of_match_node(l2x0_ids, np)->data; | 574 | if (data->setup) |
471 | if (l2_setup) | 575 | data->setup(np, &aux_val, &aux_mask); |
472 | l2_setup(np, &aux_val, &aux_mask); | ||
473 | } | 576 | } |
577 | |||
578 | if (data->save) | ||
579 | data->save(); | ||
580 | |||
474 | l2x0_init(l2x0_base, aux_val, aux_mask); | 581 | l2x0_init(l2x0_base, aux_val, aux_mask); |
582 | |||
583 | outer_cache.resume = data->resume; | ||
475 | return 0; | 584 | return 0; |
476 | } | 585 | } |
477 | #endif | 586 | #endif |