Thread (11 messages) flat view 11 messages, 2 authors, 2012-09-05
STALE5125d

[PATCH V2 3/6] arm: cache-l2x0: add support for Aurora L2 cache ctrl

From: Gregory CLEMENT <hidden>
Date: 2012-09-04 14:50:18

Hi Will,

thanks for you review

On 09/04/2012 01:22 PM, Will Deacon wrote:> On Tue, Sep 04, 2012 at 11:40:29AM +0100, Gregory CLEMENT wrote:
quoted
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 3591940..684c188 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -25,6 +25,7 @@

 #include <asm/cacheflush.h>
 #include <asm/hardware/cache-l2x0.h>
+#include <asm/hardware/cache-aurora-l2.h>

 #define CACHE_LINE_SIZE                32
@@ -33,6 +34,11 @@ static DEFINE_RAW_SPINLOCK(l2x0_lock);
 static u32 l2x0_way_mask;      /* Bitmask of active ways */
 static u32 l2x0_size;
 static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
+static int l2_wt_override;
+
+/* Aurora don't have the cache ID register available, so we have to
+ * pass it though the device tree */
+static u32  cache_id_part_number_from_dt;

 struct l2x0_regs l2x0_saved_regs;
@@ -275,6 +281,130 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
        cache_sync();
        raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
+/*
+ * Note that the end addresses passed to Linux primitives are
+ * noninclusive, while the hardware cache range operations use
+ * inclusive start and end addresses.
+ */
+static unsigned long calc_range_end(unsigned long start, unsigned long end)
+{
+       unsigned long range_end;
+
+       BUG_ON(start & (CACHE_LINE_SIZE - 1));
+       BUG_ON(end & (CACHE_LINE_SIZE - 1));
Seems a bit overkill to use BUG_ON here. Can you not just ALIGN the
addresses instead?
OK I can align them and issue a warning about it.
quoted
+       /*
+        * Try to process all cache lines between 'start' and 'end'.
+        */
+       range_end = end;
+
+       /*
+        * Limit the number of cache lines processed at once,
+        * since cache range operations stall the CPU pipeline
+        * until completion.
+        */
+       if (range_end > start + MAX_RANGE_SIZE)
+               range_end = start + MAX_RANGE_SIZE;
+
+       /*
+        * Cache range operations can't straddle a page boundary.
+        */
+       if (range_end > (start | (PAGE_SIZE - 1)) + 1)
+               range_end = (start | (PAGE_SIZE - 1)) + 1;
PAGE_ALIGN(start) instead of these bitwise operations?
Yes it's better.
quoted
+       return range_end;
+}
+
+static void aurora_pa_range(unsigned long start, unsigned long end,
+                       unsigned long offset)
+{
+       unsigned long flags;
+
+       /*
+        * Make sure 'start' and 'end' reference the same page, as
+        * L2 is PIPT and range operations only do a TLB lookup on
+        * the start address.
+        */
+       BUG_ON((start ^ end) & ~(PAGE_SIZE - 1));
Eek. I think you should instead split this into multiple operations, one for
each page contained in the range.
Actually this function is already called for each page. Before each
call we use calc_range_end() which ensure that start address and end
address are in the same page. Maybe I can just removed this test and
move the comment on top of the function.
quoted
+       raw_spin_lock_irqsave(&l2x0_lock, flags);
+       writel(start, l2x0_base + AURORA_RANGE_BASE_ADDR_REG);
+       writel(end, l2x0_base + offset);
+       raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+
+       cache_sync();
+}
+
+static void aurora_inv_range(unsigned long start, unsigned long end)
+{
+       /*
+        * Clean and invalidate partial first cache line.
+        */
+       if (start & (CACHE_LINE_SIZE - 1)) {
+               writel((start & ~(CACHE_LINE_SIZE - 1)) & ~0x1f,
+                       l2x0_base + AURORA_FLUSH_PHY_ADDR_REG);
+               cache_sync();
writel implies a cache_sync if you have CONFIG_ARM_DMA_MEM_BUFFERABLE (there
are other places in your code where this comment also applies). For v7, this
is always the case.
As the aurora cache controller is only use with v7 CPU, then we can
removed them.
quoted
+               start = (start | (CACHE_LINE_SIZE - 1)) + 1;
+       }
It's pretty strange for a cache to be able to operate only on a subset of a
cacheline. Should you not just be rounding everything up to cache line size?
We have CACHE_LINE_SIZE = 32 and we mask it with 0x1F. So indeed we
can round everything to cache line size and make only one call.
quoted
+       /*
+        * Clean and invalidate partial last cache line.
+        */
+       if (start < end && end & (CACHE_LINE_SIZE - 1)) {
+               writel((end & ~(CACHE_LINE_SIZE - 1)) & ~0x1f,
+                       l2x0_base + AURORA_FLUSH_PHY_ADDR_REG);
+               cache_sync();
+               end &= ~(CACHE_LINE_SIZE - 1);
+       }
+
+       /*
+        * Invalidate all full cache lines between 'start' and 'end'.
+        */
+       while (start < end) {
+               unsigned long range_end = calc_range_end(start, end);
+               aurora_pa_range(start, range_end - CACHE_LINE_SIZE,
+                               AURORA_INVAL_RANGE_REG);
+               start = range_end;
+       }
+
+       dsb();
Why? (same for the other dsbs following writels/cache_syncs).
I will ask if it is really needed.
quoted
@@ -312,18 +449,22 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
        u32 cache_id;
        u32 way_size = 0;
        int ways;
+       int way_size_shift = 3;
Can this be expressed in terms of a named constant? (something like
L2X0_AUX_CTRL_WAY_SIZE_MASK).
Sure.
quoted
        /* Determine the number of ways */
-       switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
+       switch (cache_id) {
        case L2X0_CACHE_ID_PART_L310:
                if (aux & (1 << 16))
                        ways = 16;
@@ -340,6 +481,30 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
                ways = (aux >> 13) & 0xf;
                type = "L210";
                break;
+
+       case AURORA_CACHE_ID:
+               sync_reg_offset = AURORA_SYNC_REG;
+
+               switch ((aux >> 13) & 0xf) {
+               case 3:
+                       ways = 4;
+                       break;
+               case 7:
+                       ways = 8;
+                       break;
+               case 11:
+                       ways = 16;
+                       break;
+               case 15:
+                       ways = 32;
+                       break;
+               default:
+                       ways = 8;
+                       break;
Do the 3,7,11,15 correspond to something meaningful or can you do:

	ways = 2 << ((n + 1) >> 2);

instead?
Good, you found the logic behind this series! I will use it.
quoted
+static void aurora_resume(void)
+{
+       u32 u;
+
+       u = readl(l2x0_base + L2X0_CTRL);
+       if (!(u & 1)) {
We should probably add a L2X0_CTRL_EN define and use that (also update the
enabling code to use it as well).
Should it be a separate patch or can I include it with this one?
quoted
+static void __init aurora_broadcast_l2_commands(void)
+{
+       __u32 u;
+       /* Enable Broadcasting of cache commands to L2*/
+       __asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u));
+       u |= 0x100;             /* Set the FW bit */
Again, just add a AURORA_CTRL_FW define for this.
OK
quoted
+static void __init aurora_of_setup(const struct device_node *np,
+                               u32 *aux_val, u32 *aux_mask)
+{
+       u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
+       u32 mask =  AURORA_ACR_REPLACEMENT_MASK;
+
+       of_property_read_u32(np, "cache-id-part",
+                       &cache_id_part_number_from_dt);
+
+       /* Determine and save the write policy */
+       l2_wt_override = of_property_read_bool(np, "wt-override");
+
+       if (l2_wt_override) {
+               val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY;
+               mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK;
+       }
smp_mb() after the assignment to l2_wt_override?
Sorry I don't get your point, why do you think we need this?
quoted
+       *aux_val &= ~mask;
+       *aux_val |= val;
+       *aux_mask &= ~mask;
+}
+
Will
-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help