diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-13 11:42:18 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-13 11:42:18 -0800 |
commit | 098c30557a9a19827240aaadc137e4668157dc6b (patch) | |
tree | 793d589e27197e754fb38c5ae08b763d6e5d0334 /drivers/base/cacheinfo.c | |
parent | 72cca7baf4fba777b8ab770b902cf2e08941773f (diff) | |
parent | 5d47ec02c37ea632398cb251c884e3a488dff794 (diff) | |
download | linux-098c30557a9a19827240aaadc137e4668157dc6b.tar.bz2 |
Merge tag 'driver-core-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core updates from Greg KH:
"Here's the new driver core patches for 4.10-rc1.
Big thing here is the nice addition of "functional dependencies" to
the driver core. The idea has been talked about for a very long time,
great job to Rafael for stepping up and implementing it. It's been
tested for longer than the 4.9-rc1 date, we held off on merging it
earlier in order to feel more comfortable about it.
Other than that, it's just a handful of small other patches, some good
cleanups to the mess that is the firmware class code, and we have a
test driver for the deferred probe logic.
All of these have been in linux-next for a while with no reported
issues"
* tag 'driver-core-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (30 commits)
firmware: Correct handling of fw_state_wait() return value
driver core: Silence device links sphinx warning
firmware: remove warning at documentation generation time
drivers: base: dma-mapping: Fix typo in dmam_alloc_non_coherent comments
driver core: test_async: fix up typo found by 0-day
firmware: move fw_state_is_done() into UHM section
firmware: do not use fw_lock for fw_state protection
firmware: drop bit ops in favor of simple state machine
firmware: refactor loading status
firmware: fix usermode helper fallback loading
driver core: firmware_class: convert to use class_groups
driver core: devcoredump: convert to use class_groups
driver core: class: add class_groups support
kernfs: Declare two local data structures static
driver-core: fix platform_no_drv_owner.cocci warnings
drivers/base/memory.c: Remove unused 'first_page' variable
driver core: add CLASS_ATTR_WO()
drivers: base: cacheinfo: support DT overrides for cache properties
drivers: base: cacheinfo: add pr_fmt logging
drivers: base: cacheinfo: fix boot error message when acpi is enabled
...
Diffstat (limited to 'drivers/base/cacheinfo.c')
-rw-r--r-- | drivers/base/cacheinfo.c | 138 |
1 files changed, 134 insertions, 4 deletions
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 47983a2ba621..1e3903d0d994 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -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: |