diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-03-16 13:38:08 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-05-29 19:48:39 -0400 |
commit | 8abd259f657d5742f96ffd46ed65feb11c44b1fb (patch) | |
tree | 76bf882d836625f95f150c53382c41401e09f49d /arch/arm/mm/cache-l2x0.c | |
parent | c0fe18ba30a62854490b1ac0f7a02145d84153f5 (diff) |
ARM: l2c: provide generic hook to intercept writes to secure registers
When Linux is running in the non-secure world, any write to a secure
L2C register will generate an abort. Platforms normally have to call
firmware to work around this. Provide a hook for them to intercept
any L2C secure register write.
l2c_write_sec() avoids writes to secure registers which are already set
to the appropriate value, thus avoiding the overhead of needlessly
calling into the secure monitor.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mm/cache-l2x0.c')
-rw-r--r-- | arch/arm/mm/cache-l2x0.c | 42 |
1 files changed, 30 insertions, 12 deletions
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b4d373ab1a5c..369a9d01d94f 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c | |||
@@ -60,13 +60,30 @@ static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask) | |||
60 | } | 60 | } |
61 | 61 | ||
62 | /* | 62 | /* |
63 | * By default, we write directly to secure registers. Platforms must | ||
64 | * override this if they are running non-secure. | ||
65 | */ | ||
66 | static void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg) | ||
67 | { | ||
68 | if (val == readl_relaxed(base + reg)) | ||
69 | return; | ||
70 | if (outer_cache.write_sec) | ||
71 | outer_cache.write_sec(val, reg); | ||
72 | else | ||
73 | writel_relaxed(val, base + reg); | ||
74 | } | ||
75 | |||
76 | /* | ||
63 | * This should only be called when we have a requirement that the | 77 | * This should only be called when we have a requirement that the |
64 | * register be written due to a work-around, as platforms running | 78 | * register be written due to a work-around, as platforms running |
65 | * in non-secure mode may not be able to access this register. | 79 | * in non-secure mode may not be able to access this register. |
66 | */ | 80 | */ |
67 | static inline void l2c_set_debug(void __iomem *base, unsigned long val) | 81 | static inline void l2c_set_debug(void __iomem *base, unsigned long val) |
68 | { | 82 | { |
69 | outer_cache.set_debug(val); | 83 | if (outer_cache.set_debug) |
84 | outer_cache.set_debug(val); | ||
85 | else | ||
86 | l2c_write_sec(val, base, L2X0_DEBUG_CTRL); | ||
70 | } | 87 | } |
71 | 88 | ||
72 | static void __l2c_op_way(void __iomem *reg) | 89 | static void __l2c_op_way(void __iomem *reg) |
@@ -95,9 +112,7 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) | |||
95 | { | 112 | { |
96 | unsigned long flags; | 113 | unsigned long flags; |
97 | 114 | ||
98 | /* Only write the aux register if it needs changing */ | 115 | l2c_write_sec(aux, base, L2X0_AUX_CTRL); |
99 | if (readl_relaxed(base + L2X0_AUX_CTRL) != aux) | ||
100 | writel_relaxed(aux, base + L2X0_AUX_CTRL); | ||
101 | 116 | ||
102 | l2c_unlock(base, num_lock); | 117 | l2c_unlock(base, num_lock); |
103 | 118 | ||
@@ -107,7 +122,7 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) | |||
107 | l2c_wait_mask(base + sync_reg_offset, 1); | 122 | l2c_wait_mask(base + sync_reg_offset, 1); |
108 | local_irq_restore(flags); | 123 | local_irq_restore(flags); |
109 | 124 | ||
110 | writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL); | 125 | l2c_write_sec(L2X0_CTRL_EN, base, L2X0_CTRL); |
111 | } | 126 | } |
112 | 127 | ||
113 | static void l2c_disable(void) | 128 | static void l2c_disable(void) |
@@ -115,7 +130,7 @@ static void l2c_disable(void) | |||
115 | void __iomem *base = l2x0_base; | 130 | void __iomem *base = l2x0_base; |
116 | 131 | ||
117 | outer_cache.flush_all(); | 132 | outer_cache.flush_all(); |
118 | writel_relaxed(0, base + L2X0_CTRL); | 133 | l2c_write_sec(0, base, L2X0_CTRL); |
119 | dsb(st); | 134 | dsb(st); |
120 | } | 135 | } |
121 | 136 | ||
@@ -139,7 +154,7 @@ static inline void cache_sync(void) | |||
139 | #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) | 154 | #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) |
140 | static inline void debug_writel(unsigned long val) | 155 | static inline void debug_writel(unsigned long val) |
141 | { | 156 | { |
142 | if (outer_cache.set_debug) | 157 | if (outer_cache.set_debug || outer_cache.write_sec) |
143 | l2c_set_debug(l2x0_base, val); | 158 | l2c_set_debug(l2x0_base, val); |
144 | } | 159 | } |
145 | #else | 160 | #else |
@@ -182,7 +197,7 @@ static void l2x0_disable(void) | |||
182 | 197 | ||
183 | raw_spin_lock_irqsave(&l2x0_lock, flags); | 198 | raw_spin_lock_irqsave(&l2x0_lock, flags); |
184 | __l2x0_flush_all(); | 199 | __l2x0_flush_all(); |
185 | writel_relaxed(0, l2x0_base + L2X0_CTRL); | 200 | l2c_write_sec(0, l2x0_base, L2X0_CTRL); |
186 | dsb(st); | 201 | dsb(st); |
187 | raw_spin_unlock_irqrestore(&l2x0_lock, flags); | 202 | raw_spin_unlock_irqrestore(&l2x0_lock, flags); |
188 | } | 203 | } |
@@ -599,11 +614,11 @@ static void l2c310_resume(void) | |||
599 | L2X0_CACHE_ID_RTL_MASK; | 614 | L2X0_CACHE_ID_RTL_MASK; |
600 | 615 | ||
601 | if (revision >= L310_CACHE_ID_RTL_R2P0) | 616 | if (revision >= L310_CACHE_ID_RTL_R2P0) |
602 | writel_relaxed(l2x0_saved_regs.prefetch_ctrl, | 617 | l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base, |
603 | base + L2X0_PREFETCH_CTRL); | 618 | L2X0_PREFETCH_CTRL); |
604 | if (revision >= L310_CACHE_ID_RTL_R3P0) | 619 | if (revision >= L310_CACHE_ID_RTL_R3P0) |
605 | writel_relaxed(l2x0_saved_regs.pwr_ctrl, | 620 | l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base, |
606 | base + L2X0_POWER_CTRL); | 621 | L2X0_POWER_CTRL); |
607 | 622 | ||
608 | l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); | 623 | l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); |
609 | } | 624 | } |
@@ -732,8 +747,11 @@ static void __init __l2c_init(const struct l2c_init_data *data, | |||
732 | l2x0_size = ways * (data->way_size_0 << way_size_bits); | 747 | l2x0_size = ways * (data->way_size_0 << way_size_bits); |
733 | 748 | ||
734 | fns = data->outer_cache; | 749 | fns = data->outer_cache; |
750 | fns.write_sec = outer_cache.write_sec; | ||
735 | if (data->fixup) | 751 | if (data->fixup) |
736 | data->fixup(l2x0_base, cache_id, &fns); | 752 | data->fixup(l2x0_base, cache_id, &fns); |
753 | if (fns.write_sec) | ||
754 | fns.set_debug = NULL; | ||
737 | 755 | ||
738 | /* | 756 | /* |
739 | * Check if l2x0 controller is already enabled. If we are booting | 757 | * Check if l2x0 controller is already enabled. If we are booting |