Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8abd259f authored by Russell King's avatar Russell King
Browse files

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: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent c0fe18ba
Loading
Loading
Loading
Loading
+4 −1
Original line number Original line Diff line number Diff line
@@ -32,8 +32,11 @@ struct outer_cache_fns {
#ifdef CONFIG_OUTER_CACHE_SYNC
#ifdef CONFIG_OUTER_CACHE_SYNC
	void (*sync)(void);
	void (*sync)(void);
#endif
#endif
	void (*set_debug)(unsigned long);
	void (*resume)(void);
	void (*resume)(void);

	/* This is an ARM L2C thing */
	void (*set_debug)(unsigned long);
	void (*write_sec)(unsigned long, unsigned);
};
};


extern struct outer_cache_fns outer_cache;
extern struct outer_cache_fns outer_cache;
+30 −12
Original line number Original line Diff line number Diff line
@@ -59,6 +59,20 @@ static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask)
		cpu_relax();
		cpu_relax();
}
}


/*
 * By default, we write directly to secure registers.  Platforms must
 * override this if they are running non-secure.
 */
static void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg)
{
	if (val == readl_relaxed(base + reg))
		return;
	if (outer_cache.write_sec)
		outer_cache.write_sec(val, reg);
	else
		writel_relaxed(val, base + reg);
}

/*
/*
 * This should only be called when we have a requirement that the
 * This should only be called when we have a requirement that the
 * register be written due to a work-around, as platforms running
 * register be written due to a work-around, as platforms running
@@ -66,7 +80,10 @@ static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask)
 */
 */
static inline void l2c_set_debug(void __iomem *base, unsigned long val)
static inline void l2c_set_debug(void __iomem *base, unsigned long val)
{
{
	if (outer_cache.set_debug)
		outer_cache.set_debug(val);
		outer_cache.set_debug(val);
	else
		l2c_write_sec(val, base, L2X0_DEBUG_CTRL);
}
}


static void __l2c_op_way(void __iomem *reg)
static void __l2c_op_way(void __iomem *reg)
@@ -95,9 +112,7 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock)
{
{
	unsigned long flags;
	unsigned long flags;


	/* Only write the aux register if it needs changing */
	l2c_write_sec(aux, base, L2X0_AUX_CTRL);
	if (readl_relaxed(base + L2X0_AUX_CTRL) != aux)
		writel_relaxed(aux, base + L2X0_AUX_CTRL);


	l2c_unlock(base, num_lock);
	l2c_unlock(base, num_lock);


@@ -107,7 +122,7 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock)
	l2c_wait_mask(base + sync_reg_offset, 1);
	l2c_wait_mask(base + sync_reg_offset, 1);
	local_irq_restore(flags);
	local_irq_restore(flags);


	writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL);
	l2c_write_sec(L2X0_CTRL_EN, base, L2X0_CTRL);
}
}


static void l2c_disable(void)
static void l2c_disable(void)
@@ -115,7 +130,7 @@ static void l2c_disable(void)
	void __iomem *base = l2x0_base;
	void __iomem *base = l2x0_base;


	outer_cache.flush_all();
	outer_cache.flush_all();
	writel_relaxed(0, base + L2X0_CTRL);
	l2c_write_sec(0, base, L2X0_CTRL);
	dsb(st);
	dsb(st);
}
}


@@ -139,7 +154,7 @@ static inline void cache_sync(void)
#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
static inline void debug_writel(unsigned long val)
static inline void debug_writel(unsigned long val)
{
{
	if (outer_cache.set_debug)
	if (outer_cache.set_debug || outer_cache.write_sec)
		l2c_set_debug(l2x0_base, val);
		l2c_set_debug(l2x0_base, val);
}
}
#else
#else
@@ -182,7 +197,7 @@ static void l2x0_disable(void)


	raw_spin_lock_irqsave(&l2x0_lock, flags);
	raw_spin_lock_irqsave(&l2x0_lock, flags);
	__l2x0_flush_all();
	__l2x0_flush_all();
	writel_relaxed(0, l2x0_base + L2X0_CTRL);
	l2c_write_sec(0, l2x0_base, L2X0_CTRL);
	dsb(st);
	dsb(st);
	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
}
}
@@ -599,11 +614,11 @@ static void l2c310_resume(void)
				L2X0_CACHE_ID_RTL_MASK;
				L2X0_CACHE_ID_RTL_MASK;


		if (revision >= L310_CACHE_ID_RTL_R2P0)
		if (revision >= L310_CACHE_ID_RTL_R2P0)
			writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
			l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base,
				       base + L2X0_PREFETCH_CTRL);
				      L2X0_PREFETCH_CTRL);
		if (revision >= L310_CACHE_ID_RTL_R3P0)
		if (revision >= L310_CACHE_ID_RTL_R3P0)
			writel_relaxed(l2x0_saved_regs.pwr_ctrl,
			l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base,
				       base + L2X0_POWER_CTRL);
				      L2X0_POWER_CTRL);


		l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8);
		l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8);
	}
	}
@@ -732,8 +747,11 @@ static void __init __l2c_init(const struct l2c_init_data *data,
	l2x0_size = ways * (data->way_size_0 << way_size_bits);
	l2x0_size = ways * (data->way_size_0 << way_size_bits);


	fns = data->outer_cache;
	fns = data->outer_cache;
	fns.write_sec = outer_cache.write_sec;
	if (data->fixup)
	if (data->fixup)
		data->fixup(l2x0_base, cache_id, &fns);
		data->fixup(l2x0_base, cache_id, &fns);
	if (fns.write_sec)
		fns.set_debug = NULL;


	/*
	/*
	 * Check if l2x0 controller is already enabled.  If we are booting
	 * Check if l2x0 controller is already enabled.  If we are booting