#include <linux/init.h>
 #include <linux/smp.h>
 #include <linux/spinlock.h>
+#include <linux/log2.h>
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
  * pass it though the device tree */
 static u32 cache_id_part_number_from_dt;
 
+/**
+ * l2x0_cache_size_of_parse() - read cache size parameters from DT
+ * @np: the device tree node for the l2 cache
+ * @aux_val: pointer to machine-supplied auxilary register value, to
+ * be augmented by the call (bits to be set to 1)
+ * @aux_mask: pointer to machine-supplied auxilary register mask, to
+ * be augmented by the call (bits to be set to 0)
+ * @associativity: variable to return the calculated associativity in
+ * @max_way_size: the maximum size in bytes for the cache ways
+ */
+static void __init l2x0_cache_size_of_parse(const struct device_node *np,
+                                           u32 *aux_val, u32 *aux_mask,
+                                           u32 *associativity,
+                                           u32 max_way_size)
+{
+       u32 mask = 0, val = 0;
+       u32 cache_size = 0, sets = 0;
+       u32 way_size_bits = 1;
+       u32 way_size = 0;
+       u32 block_size = 0;
+       u32 line_size = 0;
+
+       of_property_read_u32(np, "cache-size", &cache_size);
+       of_property_read_u32(np, "cache-sets", &sets);
+       of_property_read_u32(np, "cache-block-size", &block_size);
+       of_property_read_u32(np, "cache-line-size", &line_size);
+
+       if (!cache_size || !sets)
+               return;
+
+       /* All these l2 caches have the same line = block size actually */
+       if (!line_size) {
+               if (block_size) {
+                       /* If linesize if not given, it is equal to blocksize */
+                       line_size = block_size;
+               } else {
+                       /* Fall back to known size */
+                       pr_warn("L2C OF: no cache block/line size given: "
+                               "falling back to default size %d bytes\n",
+                               CACHE_LINE_SIZE);
+                       line_size = CACHE_LINE_SIZE;
+               }
+       }
+
+       if (line_size != CACHE_LINE_SIZE)
+               pr_warn("L2C OF: DT supplied line size %d bytes does "
+                       "not match hardware line size of %d bytes\n",
+                       line_size,
+                       CACHE_LINE_SIZE);
+
+       /*
+        * Since:
+        * set size = cache size / sets
+        * ways = cache size / (sets * line size)
+        * way size = cache size / (cache size / (sets * line size))
+        * way size = sets * line size
+        * associativity = ways = cache size / way size
+        */
+       way_size = sets * line_size;
+       *associativity = cache_size / way_size;
+
+       if (way_size > max_way_size) {
+               pr_err("L2C OF: set size %dKB is too large\n", way_size);
+               return;
+       }
+
+       pr_info("L2C OF: override cache size: %d bytes (%dKB)\n",
+               cache_size, cache_size >> 10);
+       pr_info("L2C OF: override line size: %d bytes\n", line_size);
+       pr_info("L2C OF: override way size: %d bytes (%dKB)\n",
+               way_size, way_size >> 10);
+       pr_info("L2C OF: override associativity: %d\n", *associativity);
+
+       /*
+        * Calculates the bits 17:19 to set for way size:
+        * 512KB -> 6, 256KB -> 5, ... 16KB -> 1
+        */
+       way_size_bits = ilog2(way_size >> 10) - 3;
+       if (way_size_bits < 1 || way_size_bits > 6) {
+               pr_err("L2C OF: cache way size illegal: %dKB is not mapped\n",
+                      way_size);
+               return;
+       }
+
+       mask |= L2C_AUX_CTRL_WAY_SIZE_MASK;
+       val |= (way_size_bits << L2C_AUX_CTRL_WAY_SIZE_SHIFT);
+
+       *aux_val &= ~mask;
+       *aux_val |= val;
+       *aux_mask &= ~mask;
+}
+
 static void __init l2x0_of_parse(const struct device_node *np,
                                 u32 *aux_val, u32 *aux_mask)
 {
        u32 tag = 0;
        u32 dirty = 0;
        u32 val = 0, mask = 0;
+       u32 assoc;
 
        of_property_read_u32(np, "arm,tag-latency", &tag);
        if (tag) {
                val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
        }
 
+       l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_256K);
+       if (assoc > 8) {
+               pr_err("l2x0 of: cache setting yield too high associativity\n");
+               pr_err("l2x0 of: %d calculated, max 8\n", assoc);
+       } else {
+               mask |= L2X0_AUX_CTRL_ASSOC_MASK;
+               val |= (assoc << L2X0_AUX_CTRL_ASSOC_SHIFT);
+       }
+
        *aux_val &= ~mask;
        *aux_val |= val;
        *aux_mask &= ~mask;
        u32 data[3] = { 0, 0, 0 };
        u32 tag[3] = { 0, 0, 0 };
        u32 filter[2] = { 0, 0 };
+       u32 assoc;
 
        of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
        if (tag[0] && tag[1] && tag[2])
                writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN,
                               l2x0_base + L310_ADDR_FILTER_START);
        }
+
+       l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K);
+       switch (assoc) {
+       case 16:
+               *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+               *aux_val |= L310_AUX_CTRL_ASSOCIATIVITY_16;
+               *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+               break;
+       case 8:
+               *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+               *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+               break;
+       default:
+               pr_err("PL310 OF: cache setting yield illegal associativity\n");
+               pr_err("PL310 OF: %d calculated, only 8 and 16 legal\n", assoc);
+               break;
+       }
 }
 
 static const struct l2c_init_data of_l2c310_data __initconst = {