mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
ARM: cache-l2x0: update workaround for PL310 errata 727915
ARM errata 727915 for PL310 has been updated to include a new workaround required for PL310 r2p0 for l2x0_flush_all, which also affects l2x0_clean_all in my testing. For r2p0, clean or flush each set/way individually. For r3p0 or greater, use the debug register for cleaning and flushing. Requires exporting the cache_id, sets and ways detected in the init function for later use. Change-Id: I215055cbe5dc7e4e8184fb2befc4aff672ef0a12 Signed-off-by: Colin Cross <ccross@android.com>
This commit is contained in:
@@ -57,6 +57,7 @@
|
||||
#define L2X0_STNDBY_MODE_EN (1 << 0)
|
||||
|
||||
/* Registers shifts and masks */
|
||||
#define L2X0_CACHE_ID_REV_MASK (0x3f)
|
||||
#define L2X0_CACHE_ID_PART_MASK (0xf << 6)
|
||||
#define L2X0_CACHE_ID_PART_L210 (1 << 6)
|
||||
#define L2X0_CACHE_ID_PART_L310 (3 << 6)
|
||||
@@ -72,6 +73,8 @@
|
||||
#define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT 29
|
||||
#define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT 30
|
||||
|
||||
#define REV_PL310_R2P0 4
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,16 @@ static void __iomem *l2x0_base;
|
||||
static DEFINE_SPINLOCK(l2x0_lock);
|
||||
static uint32_t l2x0_way_mask; /* Bitmask of active ways */
|
||||
static uint32_t l2x0_size;
|
||||
static u32 l2x0_cache_id;
|
||||
static unsigned int l2x0_sets;
|
||||
static unsigned int l2x0_ways;
|
||||
|
||||
static inline bool is_pl310_rev(int rev)
|
||||
{
|
||||
return (l2x0_cache_id &
|
||||
(L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) ==
|
||||
(L2X0_CACHE_ID_PART_L310 | rev);
|
||||
}
|
||||
|
||||
static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
|
||||
{
|
||||
@@ -120,6 +130,23 @@ static void l2x0_cache_sync(void)
|
||||
spin_unlock_irqrestore(&l2x0_lock, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PL310_ERRATA_727915
|
||||
static void l2x0_for_each_set_way(void __iomem *reg)
|
||||
{
|
||||
int set;
|
||||
int way;
|
||||
unsigned long flags;
|
||||
|
||||
for (way = 0; way < l2x0_ways; way++) {
|
||||
spin_lock_irqsave(&l2x0_lock, flags);
|
||||
for (set = 0; set < l2x0_sets; set++)
|
||||
writel_relaxed((way << 28) | (set << 5), reg);
|
||||
cache_sync();
|
||||
spin_unlock_irqrestore(&l2x0_lock, flags);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __l2x0_flush_all(void)
|
||||
{
|
||||
debug_writel(0x03);
|
||||
@@ -133,6 +160,13 @@ static void l2x0_flush_all(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
#ifdef CONFIG_PL310_ERRATA_727915
|
||||
if (is_pl310_rev(REV_PL310_R2P0)) {
|
||||
l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* clean all ways */
|
||||
spin_lock_irqsave(&l2x0_lock, flags);
|
||||
__l2x0_flush_all();
|
||||
@@ -143,11 +177,20 @@ static void l2x0_clean_all(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
#ifdef CONFIG_PL310_ERRATA_727915
|
||||
if (is_pl310_rev(REV_PL310_R2P0)) {
|
||||
l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* clean all ways */
|
||||
spin_lock_irqsave(&l2x0_lock, flags);
|
||||
debug_writel(0x03);
|
||||
writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
|
||||
cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
|
||||
cache_sync();
|
||||
debug_writel(0x00);
|
||||
spin_unlock_irqrestore(&l2x0_lock, flags);
|
||||
}
|
||||
|
||||
@@ -280,47 +323,46 @@ static void l2x0_disable(void)
|
||||
void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
|
||||
{
|
||||
__u32 aux;
|
||||
__u32 cache_id;
|
||||
__u32 way_size = 0;
|
||||
int ways;
|
||||
const char *type;
|
||||
|
||||
l2x0_base = base;
|
||||
|
||||
cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
|
||||
l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
|
||||
aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
|
||||
|
||||
aux &= aux_mask;
|
||||
aux |= aux_val;
|
||||
|
||||
/* Determine the number of ways */
|
||||
switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
|
||||
switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) {
|
||||
case L2X0_CACHE_ID_PART_L310:
|
||||
if (aux & (1 << 16))
|
||||
ways = 16;
|
||||
l2x0_ways = 16;
|
||||
else
|
||||
ways = 8;
|
||||
l2x0_ways = 8;
|
||||
type = "L310";
|
||||
break;
|
||||
case L2X0_CACHE_ID_PART_L210:
|
||||
ways = (aux >> 13) & 0xf;
|
||||
l2x0_ways = (aux >> 13) & 0xf;
|
||||
type = "L210";
|
||||
break;
|
||||
default:
|
||||
/* Assume unknown chips have 8 ways */
|
||||
ways = 8;
|
||||
l2x0_ways = 8;
|
||||
type = "L2x0 series";
|
||||
break;
|
||||
}
|
||||
|
||||
l2x0_way_mask = (1 << ways) - 1;
|
||||
l2x0_way_mask = (1 << l2x0_ways) - 1;
|
||||
|
||||
/*
|
||||
* L2 cache Size = Way size * Number of ways
|
||||
*/
|
||||
way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
|
||||
way_size = 1 << (way_size + 3);
|
||||
l2x0_size = ways * way_size * SZ_1K;
|
||||
way_size = SZ_1K << (way_size + 3);
|
||||
l2x0_size = l2x0_ways * way_size;
|
||||
l2x0_sets = way_size / CACHE_LINE_SIZE;
|
||||
|
||||
/*
|
||||
* Check if l2x0 controller is already enabled.
|
||||
@@ -349,5 +391,5 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
|
||||
|
||||
printk(KERN_INFO "%s cache controller enabled\n", type);
|
||||
printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n",
|
||||
ways, cache_id, aux, l2x0_size);
|
||||
l2x0_ways, l2x0_cache_id, aux, l2x0_size);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user