]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/base/cacheinfo.c
drivers: base: cacheinfo: support DT overrides for cache properties
[mirror_ubuntu-zesty-kernel.git] / drivers / base / cacheinfo.c
index e9fd32e91668992e478f31423c1e56be0724e1d2..2376628c599c12175e97b005766a09aadb22becb 100644 (file)
@@ -16,6 +16,9 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
 #include <linux/bitops.h>
 #include <linux/cacheinfo.h>
 #include <linux/compiler.h>
@@ -85,7 +88,120 @@ static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
 {
        return sib_leaf->of_node == this_leaf->of_node;
 }
+
+/* OF properties to query for a given cache type */
+struct cache_type_info {
+       const char *size_prop;
+       const char *line_size_props[2];
+       const char *nr_sets_prop;
+};
+
+static const struct cache_type_info cache_type_info[] = {
+       {
+               .size_prop       = "cache-size",
+               .line_size_props = { "cache-line-size",
+                                    "cache-block-size", },
+               .nr_sets_prop    = "cache-sets",
+       }, {
+               .size_prop       = "i-cache-size",
+               .line_size_props = { "i-cache-line-size",
+                                    "i-cache-block-size", },
+               .nr_sets_prop    = "i-cache-sets",
+       }, {
+               .size_prop       = "d-cache-size",
+               .line_size_props = { "d-cache-line-size",
+                                    "d-cache-block-size", },
+               .nr_sets_prop    = "d-cache-sets",
+       },
+};
+
+static inline int get_cacheinfo_idx(enum cache_type type)
+{
+       if (type == CACHE_TYPE_UNIFIED)
+               return 0;
+       return type;
+}
+
+static void cache_size(struct cacheinfo *this_leaf)
+{
+       const char *propname;
+       const __be32 *cache_size;
+       int ct_idx;
+
+       ct_idx = get_cacheinfo_idx(this_leaf->type);
+       propname = cache_type_info[ct_idx].size_prop;
+
+       cache_size = of_get_property(this_leaf->of_node, propname, NULL);
+       if (cache_size)
+               this_leaf->size = of_read_number(cache_size, 1);
+}
+
+/* not cache_line_size() because that's a macro in include/linux/cache.h */
+static void cache_get_line_size(struct cacheinfo *this_leaf)
+{
+       const __be32 *line_size;
+       int i, lim, ct_idx;
+
+       ct_idx = get_cacheinfo_idx(this_leaf->type);
+       lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props);
+
+       for (i = 0; i < lim; i++) {
+               const char *propname;
+
+               propname = cache_type_info[ct_idx].line_size_props[i];
+               line_size = of_get_property(this_leaf->of_node, propname, NULL);
+               if (line_size)
+                       break;
+       }
+
+       if (line_size)
+               this_leaf->coherency_line_size = of_read_number(line_size, 1);
+}
+
+static void cache_nr_sets(struct cacheinfo *this_leaf)
+{
+       const char *propname;
+       const __be32 *nr_sets;
+       int ct_idx;
+
+       ct_idx = get_cacheinfo_idx(this_leaf->type);
+       propname = cache_type_info[ct_idx].nr_sets_prop;
+
+       nr_sets = of_get_property(this_leaf->of_node, propname, NULL);
+       if (nr_sets)
+               this_leaf->number_of_sets = of_read_number(nr_sets, 1);
+}
+
+static void cache_associativity(struct cacheinfo *this_leaf)
+{
+       unsigned int line_size = this_leaf->coherency_line_size;
+       unsigned int nr_sets = this_leaf->number_of_sets;
+       unsigned int size = this_leaf->size;
+
+       /*
+        * If the cache is fully associative, there is no need to
+        * check the other properties.
+        */
+       if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0))
+               this_leaf->ways_of_associativity = (size / nr_sets) / line_size;
+}
+
+static void cache_of_override_properties(unsigned int cpu)
+{
+       int index;
+       struct cacheinfo *this_leaf;
+       struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+       for (index = 0; index < cache_leaves(cpu); index++) {
+               this_leaf = this_cpu_ci->info_list + index;
+               cache_size(this_leaf);
+               cache_get_line_size(this_leaf);
+               cache_nr_sets(this_leaf);
+               cache_associativity(this_leaf);
+       }
+}
 #else
+static void cache_of_override_properties(unsigned int cpu) { }
 static inline int cache_setup_of_node(unsigned int cpu) { return 0; }
 static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
                                           struct cacheinfo *sib_leaf)
@@ -104,9 +220,16 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
        struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
        struct cacheinfo *this_leaf, *sib_leaf;
        unsigned int index;
-       int ret;
+       int ret = 0;
 
-       ret = cache_setup_of_node(cpu);
+       if (this_cpu_ci->cpu_map_populated)
+               return 0;
+
+       if (of_have_populated_dt())
+               ret = cache_setup_of_node(cpu);
+       else if (!acpi_disabled)
+               /* No cache property/hierarchy support yet in ACPI */
+               ret = -ENOTSUPP;
        if (ret)
                return ret;
 
@@ -161,6 +284,12 @@ static void cache_shared_cpu_map_remove(unsigned int cpu)
        }
 }
 
+static void cache_override_properties(unsigned int cpu)
+{
+       if (of_have_populated_dt())
+               return cache_of_override_properties(cpu);
+}
+
 static void free_cache_attributes(unsigned int cpu)
 {
        if (!per_cpu_cacheinfo(cpu))
@@ -203,10 +332,11 @@ static int detect_cache_attributes(unsigned int cpu)
         */
        ret = cache_shared_cpu_map_setup(cpu);
        if (ret) {
-               pr_warn("Unable to detect cache hierarchy from DT for CPU %d\n",
-                       cpu);
+               pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu);
                goto free_ci;
        }
+
+       cache_override_properties(cpu);
        return 0;
 
 free_ci: