[PATCH 4/5] grlib/l2c: Write to flush registers using atomic instructions

Martin Åberg maberg at gaisler.com
Tue Jan 16 14:06:29 UTC 2024


All writes to the L2C flush registers performed by the driver are now
done using atomic write instructions. Proposed by GRLIB-TN-0021.

Update #4925.
---
 bsps/shared/grlib/l2c/l2c.c | 64 +++++++++++++++++++++++++++++--------
 1 file changed, 50 insertions(+), 14 deletions(-)

diff --git a/bsps/shared/grlib/l2c/l2c.c b/bsps/shared/grlib/l2c/l2c.c
index d3832cdd51..f989a8eda1 100644
--- a/bsps/shared/grlib/l2c/l2c.c
+++ b/bsps/shared/grlib/l2c/l2c.c
@@ -123,9 +123,6 @@
 #define L2C_MTRR_ACCESSCONTROL_ENABLE L2C_MTRR_AC
 #define L2C_MTRR_ACCESSCONTROL_DISABLE 0
 
-#define REG_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val))
-#define REG_READ(addr) (*(volatile unsigned int *)(addr))
-
 /*
  * L2CACHE FLUSHMEM register fields
  */
@@ -319,6 +316,7 @@ struct l2cache_priv {
 	int mtrr;
 	int ft_support;
 	int split_support;
+	int atomic_flush;
 
 	/* User defined ISR */
 	l2cache_isr_t isr;
@@ -385,6 +383,25 @@ static struct l2cache_priv *l2cachepriv = NULL;
 static char * repl_names[4] = {"LRU","Random","Master-Idx-1","Master-IDx-2"};
 #endif
 
+#define REG_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val))
+#define REG_READ(addr) (*(volatile unsigned int *)(addr))
+
+#if defined(__sparc__)
+static inline uint32_t atomic_swap32(uint32_t *addr, uint32_t val) {
+	__asm__ volatile (
+		"swapa [%1] 1, %0\n" :
+		"+r" (val) : /* output and input */
+		"r" (addr) : /* input */
+		"memory"
+	);
+	return val;
+}
+
+#define REG_WRITE_ATOMIC(addr, val) atomic_swap32((uint32_t *)addr, val)
+#else
+#define REG_WRITE_ATOMIC(addr, val) REG_WRITE(addr, val)
+#endif
+
 /* L2CACHE DRIVER */
 
 struct drvmgr_drv_ops l2cache_ops =
@@ -449,6 +466,8 @@ STATIC int l2cache_init(struct l2cache_priv *priv)
 	priv->mtrr = (status & L2C_STAT_MTRR) >> L2C_STAT_MTRR_BIT;
 	priv->ft_support = (status & L2C_STAT_MP) >> L2C_STAT_MP_BIT;
 
+	priv->atomic_flush = 1;
+
 	/* Probe split support. */
 	int split_old = 0;
 	int split_new = 0;
@@ -619,35 +638,52 @@ UNUSED STATIC INLINE unsigned int l2cache_reg_mtrr_get(int index)
 STATIC INLINE int l2cache_reg_flushmem(unsigned int addr, int options)
 {
 	struct l2cache_priv *priv = l2cachepriv;
+	unsigned int val;
 
 	options = (options & ~(L2C_FLUSH_ADDR));
-	REG_WRITE(&priv->regs->flush_mem_addr, (addr & L2C_FLUSH_ADDR) | options);
+	val = (addr & L2C_FLUSH_ADDR) | options;
+
+	if (priv->atomic_flush)
+		REG_WRITE_ATOMIC(&priv->regs->flush_mem_addr, val);
+	else
+		REG_WRITE(&priv->regs->flush_mem_addr, val);
+
 	return 0;
 }
 
 STATIC INLINE int l2cache_reg_flushline(int way, int index, int options)
 {
 	struct l2cache_priv *priv = l2cachepriv;
+	unsigned int val;
 
 	options = 0 | (options & (L2C_FLUSHSI_FMODE));
-	REG_WRITE(&priv->regs->flush_set_index, 
-			((index << L2C_FLUSHSI_INDEX_BIT) & L2C_FLUSHSI_INDEX) |
-			((way << L2C_FLUSHSI_WAY_BIT) & L2C_FLUSHSI_WAY) | 
-			options
-	);
+	val = ((index << L2C_FLUSHSI_INDEX_BIT) & L2C_FLUSHSI_INDEX) |
+			((way << L2C_FLUSHSI_WAY_BIT) & L2C_FLUSHSI_WAY) |
+			options;
+
+	if (priv->atomic_flush)
+		REG_WRITE_ATOMIC(&priv->regs->flush_set_index, val);
+	else
+		REG_WRITE(&priv->regs->flush_set_index, val);
+
 	return 0;
 }
 
 STATIC INLINE int l2cache_reg_flushway(unsigned int tag, int way, int options)
 {
 	struct l2cache_priv *priv = l2cachepriv;
+	unsigned int val;
 
-	options = (options & ~(L2C_FLUSHSI_TAG | L2C_FLUSHSI_WAY)) 
+	options = (options & ~(L2C_FLUSHSI_TAG | L2C_FLUSHSI_WAY))
 		| L2C_FLUSHSI_WF;
-	REG_WRITE(&priv->regs->flush_set_index, 
-			(tag & L2C_FLUSHSI_TAG) | 
-			( (way << L2C_FLUSHSI_WAY_BIT) & L2C_FLUSHSI_WAY) | 
-			options);
+	val = (tag & L2C_FLUSHSI_TAG) |
+		  ( (way << L2C_FLUSHSI_WAY_BIT) & L2C_FLUSHSI_WAY) |
+		    options;
+	if (priv->atomic_flush)
+		REG_WRITE_ATOMIC(&priv->regs->flush_set_index, val);
+	else
+		REG_WRITE(&priv->regs->flush_set_index, val);
+
 	return 0;
 }
 
-- 
2.34.1



More information about the devel mailing list