18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pptt.c - parsing of Processor Properties Topology Table (PPTT) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018, ARM 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file implements parsing of the Processor Properties Topology Table 88c2ecf20Sopenharmony_ci * which is optionally used to describe the processor and cache topology. 98c2ecf20Sopenharmony_ci * Due to the relative pointers used throughout the table, this doesn't 108c2ecf20Sopenharmony_ci * leverage the existing subtable parsing in the kernel. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The PPTT structure is an inverted tree, with each node potentially 138c2ecf20Sopenharmony_ci * holding one or two inverted tree data structures describing 148c2ecf20Sopenharmony_ci * the caches available at that level. Each cache structure optionally 158c2ecf20Sopenharmony_ci * contains properties describing the cache at a given level which can be 168c2ecf20Sopenharmony_ci * used to override hardware probed values. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ACPI PPTT: " fmt 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/acpi.h> 218c2ecf20Sopenharmony_ci#include <linux/cacheinfo.h> 228c2ecf20Sopenharmony_ci#include <acpi/processor.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic struct acpi_subtable_header *fetch_pptt_subtable(struct acpi_table_header *table_hdr, 258c2ecf20Sopenharmony_ci u32 pptt_ref) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct acpi_subtable_header *entry; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* there isn't a subtable at reference 0 */ 308c2ecf20Sopenharmony_ci if (pptt_ref < sizeof(struct acpi_subtable_header)) 318c2ecf20Sopenharmony_ci return NULL; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (pptt_ref + sizeof(struct acpi_subtable_header) > table_hdr->length) 348c2ecf20Sopenharmony_ci return NULL; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, pptt_ref); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (entry->length == 0) 398c2ecf20Sopenharmony_ci return NULL; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (pptt_ref + entry->length > table_hdr->length) 428c2ecf20Sopenharmony_ci return NULL; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return entry; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct acpi_pptt_processor *fetch_pptt_node(struct acpi_table_header *table_hdr, 488c2ecf20Sopenharmony_ci u32 pptt_ref) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return (struct acpi_pptt_processor *)fetch_pptt_subtable(table_hdr, pptt_ref); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic struct acpi_pptt_cache *fetch_pptt_cache(struct acpi_table_header *table_hdr, 548c2ecf20Sopenharmony_ci u32 pptt_ref) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return (struct acpi_pptt_cache *)fetch_pptt_subtable(table_hdr, pptt_ref); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct acpi_subtable_header *acpi_get_pptt_resource(struct acpi_table_header *table_hdr, 608c2ecf20Sopenharmony_ci struct acpi_pptt_processor *node, 618c2ecf20Sopenharmony_ci int resource) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci u32 *ref; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (resource >= node->number_of_priv_resources) 668c2ecf20Sopenharmony_ci return NULL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ref = ACPI_ADD_PTR(u32, node, sizeof(struct acpi_pptt_processor)); 698c2ecf20Sopenharmony_ci ref += resource; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return fetch_pptt_subtable(table_hdr, *ref); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic inline bool acpi_pptt_match_type(int table_type, int type) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci return ((table_type & ACPI_PPTT_MASK_CACHE_TYPE) == type || 778c2ecf20Sopenharmony_ci table_type & ACPI_PPTT_CACHE_TYPE_UNIFIED & type); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * acpi_pptt_walk_cache() - Attempt to find the requested acpi_pptt_cache 828c2ecf20Sopenharmony_ci * @table_hdr: Pointer to the head of the PPTT table 838c2ecf20Sopenharmony_ci * @local_level: passed res reflects this cache level 848c2ecf20Sopenharmony_ci * @res: cache resource in the PPTT we want to walk 858c2ecf20Sopenharmony_ci * @found: returns a pointer to the requested level if found 868c2ecf20Sopenharmony_ci * @level: the requested cache level 878c2ecf20Sopenharmony_ci * @type: the requested cache type 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * Attempt to find a given cache level, while counting the max number 908c2ecf20Sopenharmony_ci * of cache levels for the cache node. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Given a pptt resource, verify that it is a cache node, then walk 938c2ecf20Sopenharmony_ci * down each level of caches, counting how many levels are found 948c2ecf20Sopenharmony_ci * as well as checking the cache type (icache, dcache, unified). If a 958c2ecf20Sopenharmony_ci * level & type match, then we set found, and continue the search. 968c2ecf20Sopenharmony_ci * Once the entire cache branch has been walked return its max 978c2ecf20Sopenharmony_ci * depth. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * Return: The cache structure and the level we terminated with. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic unsigned int acpi_pptt_walk_cache(struct acpi_table_header *table_hdr, 1028c2ecf20Sopenharmony_ci unsigned int local_level, 1038c2ecf20Sopenharmony_ci struct acpi_subtable_header *res, 1048c2ecf20Sopenharmony_ci struct acpi_pptt_cache **found, 1058c2ecf20Sopenharmony_ci unsigned int level, int type) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct acpi_pptt_cache *cache; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (res->type != ACPI_PPTT_TYPE_CACHE) 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci cache = (struct acpi_pptt_cache *) res; 1138c2ecf20Sopenharmony_ci while (cache) { 1148c2ecf20Sopenharmony_ci local_level++; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (local_level == level && 1178c2ecf20Sopenharmony_ci cache->flags & ACPI_PPTT_CACHE_TYPE_VALID && 1188c2ecf20Sopenharmony_ci acpi_pptt_match_type(cache->attributes, type)) { 1198c2ecf20Sopenharmony_ci if (*found != NULL && cache != *found) 1208c2ecf20Sopenharmony_ci pr_warn("Found duplicate cache level/type unable to determine uniqueness\n"); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci pr_debug("Found cache @ level %u\n", level); 1238c2ecf20Sopenharmony_ci *found = cache; 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * continue looking at this node's resource list 1268c2ecf20Sopenharmony_ci * to verify that we don't find a duplicate 1278c2ecf20Sopenharmony_ci * cache node. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci cache = fetch_pptt_cache(table_hdr, cache->next_level_of_cache); 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci return local_level; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic struct acpi_pptt_cache * 1368c2ecf20Sopenharmony_ciacpi_find_cache_level(struct acpi_table_header *table_hdr, 1378c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node, 1388c2ecf20Sopenharmony_ci unsigned int *starting_level, unsigned int level, 1398c2ecf20Sopenharmony_ci int type) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct acpi_subtable_header *res; 1428c2ecf20Sopenharmony_ci unsigned int number_of_levels = *starting_level; 1438c2ecf20Sopenharmony_ci int resource = 0; 1448c2ecf20Sopenharmony_ci struct acpi_pptt_cache *ret = NULL; 1458c2ecf20Sopenharmony_ci unsigned int local_level; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* walk down from processor node */ 1488c2ecf20Sopenharmony_ci while ((res = acpi_get_pptt_resource(table_hdr, cpu_node, resource))) { 1498c2ecf20Sopenharmony_ci resource++; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci local_level = acpi_pptt_walk_cache(table_hdr, *starting_level, 1528c2ecf20Sopenharmony_ci res, &ret, level, type); 1538c2ecf20Sopenharmony_ci /* 1548c2ecf20Sopenharmony_ci * we are looking for the max depth. Since its potentially 1558c2ecf20Sopenharmony_ci * possible for a given node to have resources with differing 1568c2ecf20Sopenharmony_ci * depths verify that the depth we have found is the largest. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci if (number_of_levels < local_level) 1598c2ecf20Sopenharmony_ci number_of_levels = local_level; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci if (number_of_levels > *starting_level) 1628c2ecf20Sopenharmony_ci *starting_level = number_of_levels; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return ret; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/** 1688c2ecf20Sopenharmony_ci * acpi_count_levels() - Given a PPTT table, and a CPU node, count the caches 1698c2ecf20Sopenharmony_ci * @table_hdr: Pointer to the head of the PPTT table 1708c2ecf20Sopenharmony_ci * @cpu_node: processor node we wish to count caches for 1718c2ecf20Sopenharmony_ci * 1728c2ecf20Sopenharmony_ci * Given a processor node containing a processing unit, walk into it and count 1738c2ecf20Sopenharmony_ci * how many levels exist solely for it, and then walk up each level until we hit 1748c2ecf20Sopenharmony_ci * the root node (ignore the package level because it may be possible to have 1758c2ecf20Sopenharmony_ci * caches that exist across packages). Count the number of cache levels that 1768c2ecf20Sopenharmony_ci * exist at each level on the way up. 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Return: Total number of levels found. 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_cistatic int acpi_count_levels(struct acpi_table_header *table_hdr, 1818c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int total_levels = 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci do { 1868c2ecf20Sopenharmony_ci acpi_find_cache_level(table_hdr, cpu_node, &total_levels, 0, 0); 1878c2ecf20Sopenharmony_ci cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent); 1888c2ecf20Sopenharmony_ci } while (cpu_node); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return total_levels; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/** 1948c2ecf20Sopenharmony_ci * acpi_pptt_leaf_node() - Given a processor node, determine if its a leaf 1958c2ecf20Sopenharmony_ci * @table_hdr: Pointer to the head of the PPTT table 1968c2ecf20Sopenharmony_ci * @node: passed node is checked to see if its a leaf 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * Determine if the *node parameter is a leaf node by iterating the 1998c2ecf20Sopenharmony_ci * PPTT table, looking for nodes which reference it. 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * Return: 0 if we find a node referencing the passed node (or table error), 2028c2ecf20Sopenharmony_ci * or 1 if we don't. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic int acpi_pptt_leaf_node(struct acpi_table_header *table_hdr, 2058c2ecf20Sopenharmony_ci struct acpi_pptt_processor *node) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct acpi_subtable_header *entry; 2088c2ecf20Sopenharmony_ci unsigned long table_end; 2098c2ecf20Sopenharmony_ci u32 node_entry; 2108c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node; 2118c2ecf20Sopenharmony_ci u32 proc_sz; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (table_hdr->revision > 1) 2148c2ecf20Sopenharmony_ci return (node->flags & ACPI_PPTT_ACPI_LEAF_NODE); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci table_end = (unsigned long)table_hdr + table_hdr->length; 2178c2ecf20Sopenharmony_ci node_entry = ACPI_PTR_DIFF(node, table_hdr); 2188c2ecf20Sopenharmony_ci entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, 2198c2ecf20Sopenharmony_ci sizeof(struct acpi_table_pptt)); 2208c2ecf20Sopenharmony_ci proc_sz = sizeof(struct acpi_pptt_processor *); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci while ((unsigned long)entry + proc_sz < table_end) { 2238c2ecf20Sopenharmony_ci cpu_node = (struct acpi_pptt_processor *)entry; 2248c2ecf20Sopenharmony_ci if (entry->type == ACPI_PPTT_TYPE_PROCESSOR && 2258c2ecf20Sopenharmony_ci cpu_node->parent == node_entry) 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci if (entry->length == 0) 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry, 2308c2ecf20Sopenharmony_ci entry->length); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci return 1; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/** 2378c2ecf20Sopenharmony_ci * acpi_find_processor_node() - Given a PPTT table find the requested processor 2388c2ecf20Sopenharmony_ci * @table_hdr: Pointer to the head of the PPTT table 2398c2ecf20Sopenharmony_ci * @acpi_cpu_id: CPU we are searching for 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * Find the subtable entry describing the provided processor. 2428c2ecf20Sopenharmony_ci * This is done by iterating the PPTT table looking for processor nodes 2438c2ecf20Sopenharmony_ci * which have an acpi_processor_id that matches the acpi_cpu_id parameter 2448c2ecf20Sopenharmony_ci * passed into the function. If we find a node that matches this criteria 2458c2ecf20Sopenharmony_ci * we verify that its a leaf node in the topology rather than depending 2468c2ecf20Sopenharmony_ci * on the valid flag, which doesn't need to be set for leaf nodes. 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Return: NULL, or the processors acpi_pptt_processor* 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic struct acpi_pptt_processor *acpi_find_processor_node(struct acpi_table_header *table_hdr, 2518c2ecf20Sopenharmony_ci u32 acpi_cpu_id) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct acpi_subtable_header *entry; 2548c2ecf20Sopenharmony_ci unsigned long table_end; 2558c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node; 2568c2ecf20Sopenharmony_ci u32 proc_sz; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci table_end = (unsigned long)table_hdr + table_hdr->length; 2598c2ecf20Sopenharmony_ci entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, 2608c2ecf20Sopenharmony_ci sizeof(struct acpi_table_pptt)); 2618c2ecf20Sopenharmony_ci proc_sz = sizeof(struct acpi_pptt_processor *); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* find the processor structure associated with this cpuid */ 2648c2ecf20Sopenharmony_ci while ((unsigned long)entry + proc_sz < table_end) { 2658c2ecf20Sopenharmony_ci cpu_node = (struct acpi_pptt_processor *)entry; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (entry->length == 0) { 2688c2ecf20Sopenharmony_ci pr_warn("Invalid zero length subtable\n"); 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci if (entry->type == ACPI_PPTT_TYPE_PROCESSOR && 2728c2ecf20Sopenharmony_ci acpi_cpu_id == cpu_node->acpi_processor_id && 2738c2ecf20Sopenharmony_ci acpi_pptt_leaf_node(table_hdr, cpu_node)) { 2748c2ecf20Sopenharmony_ci return (struct acpi_pptt_processor *)entry; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry, 2788c2ecf20Sopenharmony_ci entry->length); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return NULL; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int acpi_find_cache_levels(struct acpi_table_header *table_hdr, 2858c2ecf20Sopenharmony_ci u32 acpi_cpu_id) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci int number_of_levels = 0; 2888c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci cpu = acpi_find_processor_node(table_hdr, acpi_cpu_id); 2918c2ecf20Sopenharmony_ci if (cpu) 2928c2ecf20Sopenharmony_ci number_of_levels = acpi_count_levels(table_hdr, cpu); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return number_of_levels; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic u8 acpi_cache_type(enum cache_type type) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci switch (type) { 3008c2ecf20Sopenharmony_ci case CACHE_TYPE_DATA: 3018c2ecf20Sopenharmony_ci pr_debug("Looking for data cache\n"); 3028c2ecf20Sopenharmony_ci return ACPI_PPTT_CACHE_TYPE_DATA; 3038c2ecf20Sopenharmony_ci case CACHE_TYPE_INST: 3048c2ecf20Sopenharmony_ci pr_debug("Looking for instruction cache\n"); 3058c2ecf20Sopenharmony_ci return ACPI_PPTT_CACHE_TYPE_INSTR; 3068c2ecf20Sopenharmony_ci default: 3078c2ecf20Sopenharmony_ci case CACHE_TYPE_UNIFIED: 3088c2ecf20Sopenharmony_ci pr_debug("Looking for unified cache\n"); 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * It is important that ACPI_PPTT_CACHE_TYPE_UNIFIED 3118c2ecf20Sopenharmony_ci * contains the bit pattern that will match both 3128c2ecf20Sopenharmony_ci * ACPI unified bit patterns because we use it later 3138c2ecf20Sopenharmony_ci * to match both cases. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci return ACPI_PPTT_CACHE_TYPE_UNIFIED; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *table_hdr, 3208c2ecf20Sopenharmony_ci u32 acpi_cpu_id, 3218c2ecf20Sopenharmony_ci enum cache_type type, 3228c2ecf20Sopenharmony_ci unsigned int level, 3238c2ecf20Sopenharmony_ci struct acpi_pptt_processor **node) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci unsigned int total_levels = 0; 3268c2ecf20Sopenharmony_ci struct acpi_pptt_cache *found = NULL; 3278c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node; 3288c2ecf20Sopenharmony_ci u8 acpi_type = acpi_cache_type(type); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pr_debug("Looking for CPU %d's level %u cache type %d\n", 3318c2ecf20Sopenharmony_ci acpi_cpu_id, level, acpi_type); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci cpu_node = acpi_find_processor_node(table_hdr, acpi_cpu_id); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci while (cpu_node && !found) { 3368c2ecf20Sopenharmony_ci found = acpi_find_cache_level(table_hdr, cpu_node, 3378c2ecf20Sopenharmony_ci &total_levels, level, acpi_type); 3388c2ecf20Sopenharmony_ci *node = cpu_node; 3398c2ecf20Sopenharmony_ci cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return found; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/** 3468c2ecf20Sopenharmony_ci * update_cache_properties() - Update cacheinfo for the given processor 3478c2ecf20Sopenharmony_ci * @this_leaf: Kernel cache info structure being updated 3488c2ecf20Sopenharmony_ci * @found_cache: The PPTT node describing this cache instance 3498c2ecf20Sopenharmony_ci * @cpu_node: A unique reference to describe this cache instance 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * The ACPI spec implies that the fields in the cache structures are used to 3528c2ecf20Sopenharmony_ci * extend and correct the information probed from the hardware. Lets only 3538c2ecf20Sopenharmony_ci * set fields that we determine are VALID. 3548c2ecf20Sopenharmony_ci * 3558c2ecf20Sopenharmony_ci * Return: nothing. Side effect of updating the global cacheinfo 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_cistatic void update_cache_properties(struct cacheinfo *this_leaf, 3588c2ecf20Sopenharmony_ci struct acpi_pptt_cache *found_cache, 3598c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci this_leaf->fw_token = cpu_node; 3628c2ecf20Sopenharmony_ci if (found_cache->flags & ACPI_PPTT_SIZE_PROPERTY_VALID) 3638c2ecf20Sopenharmony_ci this_leaf->size = found_cache->size; 3648c2ecf20Sopenharmony_ci if (found_cache->flags & ACPI_PPTT_LINE_SIZE_VALID) 3658c2ecf20Sopenharmony_ci this_leaf->coherency_line_size = found_cache->line_size; 3668c2ecf20Sopenharmony_ci if (found_cache->flags & ACPI_PPTT_NUMBER_OF_SETS_VALID) 3678c2ecf20Sopenharmony_ci this_leaf->number_of_sets = found_cache->number_of_sets; 3688c2ecf20Sopenharmony_ci if (found_cache->flags & ACPI_PPTT_ASSOCIATIVITY_VALID) 3698c2ecf20Sopenharmony_ci this_leaf->ways_of_associativity = found_cache->associativity; 3708c2ecf20Sopenharmony_ci if (found_cache->flags & ACPI_PPTT_WRITE_POLICY_VALID) { 3718c2ecf20Sopenharmony_ci switch (found_cache->attributes & ACPI_PPTT_MASK_WRITE_POLICY) { 3728c2ecf20Sopenharmony_ci case ACPI_PPTT_CACHE_POLICY_WT: 3738c2ecf20Sopenharmony_ci this_leaf->attributes = CACHE_WRITE_THROUGH; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci case ACPI_PPTT_CACHE_POLICY_WB: 3768c2ecf20Sopenharmony_ci this_leaf->attributes = CACHE_WRITE_BACK; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci if (found_cache->flags & ACPI_PPTT_ALLOCATION_TYPE_VALID) { 3818c2ecf20Sopenharmony_ci switch (found_cache->attributes & ACPI_PPTT_MASK_ALLOCATION_TYPE) { 3828c2ecf20Sopenharmony_ci case ACPI_PPTT_CACHE_READ_ALLOCATE: 3838c2ecf20Sopenharmony_ci this_leaf->attributes |= CACHE_READ_ALLOCATE; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case ACPI_PPTT_CACHE_WRITE_ALLOCATE: 3868c2ecf20Sopenharmony_ci this_leaf->attributes |= CACHE_WRITE_ALLOCATE; 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci case ACPI_PPTT_CACHE_RW_ALLOCATE: 3898c2ecf20Sopenharmony_ci case ACPI_PPTT_CACHE_RW_ALLOCATE_ALT: 3908c2ecf20Sopenharmony_ci this_leaf->attributes |= 3918c2ecf20Sopenharmony_ci CACHE_READ_ALLOCATE | CACHE_WRITE_ALLOCATE; 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * If cache type is NOCACHE, then the cache hasn't been specified 3978c2ecf20Sopenharmony_ci * via other mechanisms. Update the type if a cache type has been 3988c2ecf20Sopenharmony_ci * provided. 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * Note, we assume such caches are unified based on conventional system 4018c2ecf20Sopenharmony_ci * design and known examples. Significant work is required elsewhere to 4028c2ecf20Sopenharmony_ci * fully support data/instruction only type caches which are only 4038c2ecf20Sopenharmony_ci * specified in PPTT. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci if (this_leaf->type == CACHE_TYPE_NOCACHE && 4068c2ecf20Sopenharmony_ci found_cache->flags & ACPI_PPTT_CACHE_TYPE_VALID) 4078c2ecf20Sopenharmony_ci this_leaf->type = CACHE_TYPE_UNIFIED; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void cache_setup_acpi_cpu(struct acpi_table_header *table, 4118c2ecf20Sopenharmony_ci unsigned int cpu) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct acpi_pptt_cache *found_cache; 4148c2ecf20Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 4158c2ecf20Sopenharmony_ci u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu); 4168c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf; 4178c2ecf20Sopenharmony_ci unsigned int index = 0; 4188c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node = NULL; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci while (index < get_cpu_cacheinfo(cpu)->num_leaves) { 4218c2ecf20Sopenharmony_ci this_leaf = this_cpu_ci->info_list + index; 4228c2ecf20Sopenharmony_ci found_cache = acpi_find_cache_node(table, acpi_cpu_id, 4238c2ecf20Sopenharmony_ci this_leaf->type, 4248c2ecf20Sopenharmony_ci this_leaf->level, 4258c2ecf20Sopenharmony_ci &cpu_node); 4268c2ecf20Sopenharmony_ci pr_debug("found = %p %p\n", found_cache, cpu_node); 4278c2ecf20Sopenharmony_ci if (found_cache) 4288c2ecf20Sopenharmony_ci update_cache_properties(this_leaf, 4298c2ecf20Sopenharmony_ci found_cache, 4308c2ecf20Sopenharmony_ci cpu_node); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci index++; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic bool flag_identical(struct acpi_table_header *table_hdr, 4378c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct acpi_pptt_processor *next; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* heterogeneous machines must use PPTT revision > 1 */ 4428c2ecf20Sopenharmony_ci if (table_hdr->revision < 2) 4438c2ecf20Sopenharmony_ci return false; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* Locate the last node in the tree with IDENTICAL set */ 4468c2ecf20Sopenharmony_ci if (cpu->flags & ACPI_PPTT_ACPI_IDENTICAL) { 4478c2ecf20Sopenharmony_ci next = fetch_pptt_node(table_hdr, cpu->parent); 4488c2ecf20Sopenharmony_ci if (!(next && next->flags & ACPI_PPTT_ACPI_IDENTICAL)) 4498c2ecf20Sopenharmony_ci return true; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return false; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/* Passing level values greater than this will result in search termination */ 4568c2ecf20Sopenharmony_ci#define PPTT_ABORT_PACKAGE 0xFF 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic struct acpi_pptt_processor *acpi_find_processor_tag(struct acpi_table_header *table_hdr, 4598c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu, 4608c2ecf20Sopenharmony_ci int level, int flag) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct acpi_pptt_processor *prev_node; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci while (cpu && level) { 4658c2ecf20Sopenharmony_ci /* special case the identical flag to find last identical */ 4668c2ecf20Sopenharmony_ci if (flag == ACPI_PPTT_ACPI_IDENTICAL) { 4678c2ecf20Sopenharmony_ci if (flag_identical(table_hdr, cpu)) 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci } else if (cpu->flags & flag) 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci pr_debug("level %d\n", level); 4728c2ecf20Sopenharmony_ci prev_node = fetch_pptt_node(table_hdr, cpu->parent); 4738c2ecf20Sopenharmony_ci if (prev_node == NULL) 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci cpu = prev_node; 4768c2ecf20Sopenharmony_ci level--; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci return cpu; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void acpi_pptt_warn_missing(void) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci pr_warn_once("No PPTT table found, CPU and cache topology may be inaccurate\n"); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/** 4878c2ecf20Sopenharmony_ci * topology_get_acpi_cpu_tag() - Find a unique topology value for a feature 4888c2ecf20Sopenharmony_ci * @table: Pointer to the head of the PPTT table 4898c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 4908c2ecf20Sopenharmony_ci * @level: A level that terminates the search 4918c2ecf20Sopenharmony_ci * @flag: A flag which terminates the search 4928c2ecf20Sopenharmony_ci * 4938c2ecf20Sopenharmony_ci * Get a unique value given a CPU, and a topology level, that can be 4948c2ecf20Sopenharmony_ci * matched to determine which cpus share common topological features 4958c2ecf20Sopenharmony_ci * at that level. 4968c2ecf20Sopenharmony_ci * 4978c2ecf20Sopenharmony_ci * Return: Unique value, or -ENOENT if unable to locate CPU 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_cistatic int topology_get_acpi_cpu_tag(struct acpi_table_header *table, 5008c2ecf20Sopenharmony_ci unsigned int cpu, int level, int flag) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node; 5038c2ecf20Sopenharmony_ci u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci cpu_node = acpi_find_processor_node(table, acpi_cpu_id); 5068c2ecf20Sopenharmony_ci if (cpu_node) { 5078c2ecf20Sopenharmony_ci cpu_node = acpi_find_processor_tag(table, cpu_node, 5088c2ecf20Sopenharmony_ci level, flag); 5098c2ecf20Sopenharmony_ci /* 5108c2ecf20Sopenharmony_ci * As per specification if the processor structure represents 5118c2ecf20Sopenharmony_ci * an actual processor, then ACPI processor ID must be valid. 5128c2ecf20Sopenharmony_ci * For processor containers ACPI_PPTT_ACPI_PROCESSOR_ID_VALID 5138c2ecf20Sopenharmony_ci * should be set if the UID is valid 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci if (level == 0 || 5168c2ecf20Sopenharmony_ci cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID) 5178c2ecf20Sopenharmony_ci return cpu_node->acpi_processor_id; 5188c2ecf20Sopenharmony_ci return ACPI_PTR_DIFF(cpu_node, table); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci pr_warn_once("PPTT table found, but unable to locate core %d (%d)\n", 5218c2ecf20Sopenharmony_ci cpu, acpi_cpu_id); 5228c2ecf20Sopenharmony_ci return -ENOENT; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct acpi_table_header *table; 5288c2ecf20Sopenharmony_ci acpi_status status; 5298c2ecf20Sopenharmony_ci int retval; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); 5328c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5338c2ecf20Sopenharmony_ci acpi_pptt_warn_missing(); 5348c2ecf20Sopenharmony_ci return -ENOENT; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci retval = topology_get_acpi_cpu_tag(table, cpu, level, flag); 5378c2ecf20Sopenharmony_ci pr_debug("Topology Setup ACPI CPU %d, level %d ret = %d\n", 5388c2ecf20Sopenharmony_ci cpu, level, retval); 5398c2ecf20Sopenharmony_ci acpi_put_table(table); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return retval; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/** 5458c2ecf20Sopenharmony_ci * check_acpi_cpu_flag() - Determine if CPU node has a flag set 5468c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 5478c2ecf20Sopenharmony_ci * @rev: The minimum PPTT revision defining the flag 5488c2ecf20Sopenharmony_ci * @flag: The flag itself 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci * Check the node representing a CPU for a given flag. 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * Return: -ENOENT if the PPTT doesn't exist, the CPU cannot be found or 5538c2ecf20Sopenharmony_ci * the table revision isn't new enough. 5548c2ecf20Sopenharmony_ci * 1, any passed flag set 5558c2ecf20Sopenharmony_ci * 0, flag unset 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct acpi_table_header *table; 5608c2ecf20Sopenharmony_ci acpi_status status; 5618c2ecf20Sopenharmony_ci u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu); 5628c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node = NULL; 5638c2ecf20Sopenharmony_ci int ret = -ENOENT; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); 5668c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5678c2ecf20Sopenharmony_ci acpi_pptt_warn_missing(); 5688c2ecf20Sopenharmony_ci return ret; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (table->revision >= rev) 5728c2ecf20Sopenharmony_ci cpu_node = acpi_find_processor_node(table, acpi_cpu_id); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (cpu_node) 5758c2ecf20Sopenharmony_ci ret = (cpu_node->flags & flag) != 0; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci acpi_put_table(table); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci/** 5838c2ecf20Sopenharmony_ci * acpi_find_last_cache_level() - Determines the number of cache levels for a PE 5848c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 5858c2ecf20Sopenharmony_ci * 5868c2ecf20Sopenharmony_ci * Given a logical CPU number, returns the number of levels of cache represented 5878c2ecf20Sopenharmony_ci * in the PPTT. Errors caused by lack of a PPTT table, or otherwise, return 0 5888c2ecf20Sopenharmony_ci * indicating we didn't find any cache levels. 5898c2ecf20Sopenharmony_ci * 5908c2ecf20Sopenharmony_ci * Return: Cache levels visible to this core. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ciint acpi_find_last_cache_level(unsigned int cpu) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci u32 acpi_cpu_id; 5958c2ecf20Sopenharmony_ci struct acpi_table_header *table; 5968c2ecf20Sopenharmony_ci int number_of_levels = 0; 5978c2ecf20Sopenharmony_ci acpi_status status; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci pr_debug("Cache Setup find last level CPU=%d\n", cpu); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci acpi_cpu_id = get_acpi_id_for_cpu(cpu); 6028c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); 6038c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 6048c2ecf20Sopenharmony_ci acpi_pptt_warn_missing(); 6058c2ecf20Sopenharmony_ci } else { 6068c2ecf20Sopenharmony_ci number_of_levels = acpi_find_cache_levels(table, acpi_cpu_id); 6078c2ecf20Sopenharmony_ci acpi_put_table(table); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci pr_debug("Cache Setup find last level level=%d\n", number_of_levels); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return number_of_levels; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/** 6158c2ecf20Sopenharmony_ci * cache_setup_acpi() - Override CPU cache topology with data from the PPTT 6168c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * Updates the global cache info provided by cpu_get_cacheinfo() 6198c2ecf20Sopenharmony_ci * when there are valid properties in the acpi_pptt_cache nodes. A 6208c2ecf20Sopenharmony_ci * successful parse may not result in any updates if none of the 6218c2ecf20Sopenharmony_ci * cache levels have any valid flags set. Further, a unique value is 6228c2ecf20Sopenharmony_ci * associated with each known CPU cache entry. This unique value 6238c2ecf20Sopenharmony_ci * can be used to determine whether caches are shared between CPUs. 6248c2ecf20Sopenharmony_ci * 6258c2ecf20Sopenharmony_ci * Return: -ENOENT on failure to find table, or 0 on success 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ciint cache_setup_acpi(unsigned int cpu) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct acpi_table_header *table; 6308c2ecf20Sopenharmony_ci acpi_status status; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci pr_debug("Cache Setup ACPI CPU %d\n", cpu); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); 6358c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 6368c2ecf20Sopenharmony_ci acpi_pptt_warn_missing(); 6378c2ecf20Sopenharmony_ci return -ENOENT; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci cache_setup_acpi_cpu(table, cpu); 6418c2ecf20Sopenharmony_ci acpi_put_table(table); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return status; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/** 6478c2ecf20Sopenharmony_ci * acpi_pptt_cpu_is_thread() - Determine if CPU is a thread 6488c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 6498c2ecf20Sopenharmony_ci * 6508c2ecf20Sopenharmony_ci * Return: 1, a thread 6518c2ecf20Sopenharmony_ci * 0, not a thread 6528c2ecf20Sopenharmony_ci * -ENOENT ,if the PPTT doesn't exist, the CPU cannot be found or 6538c2ecf20Sopenharmony_ci * the table revision isn't new enough. 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_ciint acpi_pptt_cpu_is_thread(unsigned int cpu) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci return check_acpi_cpu_flag(cpu, 2, ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/** 6618c2ecf20Sopenharmony_ci * find_acpi_cpu_topology() - Determine a unique topology value for a given CPU 6628c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 6638c2ecf20Sopenharmony_ci * @level: The topological level for which we would like a unique ID 6648c2ecf20Sopenharmony_ci * 6658c2ecf20Sopenharmony_ci * Determine a topology unique ID for each thread/core/cluster/mc_grouping 6668c2ecf20Sopenharmony_ci * /socket/etc. This ID can then be used to group peers, which will have 6678c2ecf20Sopenharmony_ci * matching ids. 6688c2ecf20Sopenharmony_ci * 6698c2ecf20Sopenharmony_ci * The search terminates when either the requested level is found or 6708c2ecf20Sopenharmony_ci * we reach a root node. Levels beyond the termination point will return the 6718c2ecf20Sopenharmony_ci * same unique ID. The unique id for level 0 is the acpi processor id. All 6728c2ecf20Sopenharmony_ci * other levels beyond this use a generated value to uniquely identify 6738c2ecf20Sopenharmony_ci * a topological feature. 6748c2ecf20Sopenharmony_ci * 6758c2ecf20Sopenharmony_ci * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found. 6768c2ecf20Sopenharmony_ci * Otherwise returns a value which represents a unique topological feature. 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_ciint find_acpi_cpu_topology(unsigned int cpu, int level) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci return find_acpi_cpu_topology_tag(cpu, level, 0); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci/** 6848c2ecf20Sopenharmony_ci * find_acpi_cpu_cache_topology() - Determine a unique cache topology value 6858c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 6868c2ecf20Sopenharmony_ci * @level: The cache level for which we would like a unique ID 6878c2ecf20Sopenharmony_ci * 6888c2ecf20Sopenharmony_ci * Determine a unique ID for each unified cache in the system 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found. 6918c2ecf20Sopenharmony_ci * Otherwise returns a value which represents a unique topological feature. 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ciint find_acpi_cpu_cache_topology(unsigned int cpu, int level) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct acpi_table_header *table; 6968c2ecf20Sopenharmony_ci struct acpi_pptt_cache *found_cache; 6978c2ecf20Sopenharmony_ci acpi_status status; 6988c2ecf20Sopenharmony_ci u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu); 6998c2ecf20Sopenharmony_ci struct acpi_pptt_processor *cpu_node = NULL; 7008c2ecf20Sopenharmony_ci int ret = -1; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); 7038c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 7048c2ecf20Sopenharmony_ci acpi_pptt_warn_missing(); 7058c2ecf20Sopenharmony_ci return -ENOENT; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci found_cache = acpi_find_cache_node(table, acpi_cpu_id, 7098c2ecf20Sopenharmony_ci CACHE_TYPE_UNIFIED, 7108c2ecf20Sopenharmony_ci level, 7118c2ecf20Sopenharmony_ci &cpu_node); 7128c2ecf20Sopenharmony_ci if (found_cache) 7138c2ecf20Sopenharmony_ci ret = ACPI_PTR_DIFF(cpu_node, table); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci acpi_put_table(table); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return ret; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/** 7218c2ecf20Sopenharmony_ci * find_acpi_cpu_topology_package() - Determine a unique CPU package value 7228c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 7238c2ecf20Sopenharmony_ci * 7248c2ecf20Sopenharmony_ci * Determine a topology unique package ID for the given CPU. 7258c2ecf20Sopenharmony_ci * This ID can then be used to group peers, which will have matching ids. 7268c2ecf20Sopenharmony_ci * 7278c2ecf20Sopenharmony_ci * The search terminates when either a level is found with the PHYSICAL_PACKAGE 7288c2ecf20Sopenharmony_ci * flag set or we reach a root node. 7298c2ecf20Sopenharmony_ci * 7308c2ecf20Sopenharmony_ci * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found. 7318c2ecf20Sopenharmony_ci * Otherwise returns a value which represents the package for this CPU. 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ciint find_acpi_cpu_topology_package(unsigned int cpu) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE, 7368c2ecf20Sopenharmony_ci ACPI_PPTT_PHYSICAL_PACKAGE); 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/** 7408c2ecf20Sopenharmony_ci * find_acpi_cpu_topology_hetero_id() - Get a core architecture tag 7418c2ecf20Sopenharmony_ci * @cpu: Kernel logical CPU number 7428c2ecf20Sopenharmony_ci * 7438c2ecf20Sopenharmony_ci * Determine a unique heterogeneous tag for the given CPU. CPUs with the same 7448c2ecf20Sopenharmony_ci * implementation should have matching tags. 7458c2ecf20Sopenharmony_ci * 7468c2ecf20Sopenharmony_ci * The returned tag can be used to group peers with identical implementation. 7478c2ecf20Sopenharmony_ci * 7488c2ecf20Sopenharmony_ci * The search terminates when a level is found with the identical implementation 7498c2ecf20Sopenharmony_ci * flag set or we reach a root node. 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci * Due to limitations in the PPTT data structure, there may be rare situations 7528c2ecf20Sopenharmony_ci * where two cores in a heterogeneous machine may be identical, but won't have 7538c2ecf20Sopenharmony_ci * the same tag. 7548c2ecf20Sopenharmony_ci * 7558c2ecf20Sopenharmony_ci * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found. 7568c2ecf20Sopenharmony_ci * Otherwise returns a value which represents a group of identical cores 7578c2ecf20Sopenharmony_ci * similar to this CPU. 7588c2ecf20Sopenharmony_ci */ 7598c2ecf20Sopenharmony_ciint find_acpi_cpu_topology_hetero_id(unsigned int cpu) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE, 7628c2ecf20Sopenharmony_ci ACPI_PPTT_ACPI_IDENTICAL); 7638c2ecf20Sopenharmony_ci} 764