18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * cbe_regs.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Accessor routines for the various MMIO register blocks of the CBE 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * (c) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/percpu.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/io.h> 188c2ecf20Sopenharmony_ci#include <asm/prom.h> 198c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 208c2ecf20Sopenharmony_ci#include <asm/cell-regs.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Current implementation uses "cpu" nodes. We build our own mapping 248c2ecf20Sopenharmony_ci * array of cpu numbers to cpu nodes locally for now to allow interrupt 258c2ecf20Sopenharmony_ci * time code to have a fast path rather than call of_get_cpu_node(). If 268c2ecf20Sopenharmony_ci * we implement cpu hotplug, we'll have to install an appropriate norifier 278c2ecf20Sopenharmony_ci * in order to release references to the cpu going away 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic struct cbe_regs_map 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct device_node *cpu_node; 328c2ecf20Sopenharmony_ci struct device_node *be_node; 338c2ecf20Sopenharmony_ci struct cbe_pmd_regs __iomem *pmd_regs; 348c2ecf20Sopenharmony_ci struct cbe_iic_regs __iomem *iic_regs; 358c2ecf20Sopenharmony_ci struct cbe_mic_tm_regs __iomem *mic_tm_regs; 368c2ecf20Sopenharmony_ci struct cbe_pmd_shadow_regs pmd_shadow_regs; 378c2ecf20Sopenharmony_ci} cbe_regs_maps[MAX_CBE]; 388c2ecf20Sopenharmony_cistatic int cbe_regs_map_count; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct cbe_thread_map 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct device_node *cpu_node; 438c2ecf20Sopenharmony_ci struct device_node *be_node; 448c2ecf20Sopenharmony_ci struct cbe_regs_map *regs; 458c2ecf20Sopenharmony_ci unsigned int thread_id; 468c2ecf20Sopenharmony_ci unsigned int cbe_id; 478c2ecf20Sopenharmony_ci} cbe_thread_map[NR_CPUS]; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = {CPU_BITS_NONE} }; 508c2ecf20Sopenharmony_cistatic cpumask_t cbe_first_online_cpu = { CPU_BITS_NONE }; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct cbe_regs_map *cbe_find_map(struct device_node *np) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int i; 558c2ecf20Sopenharmony_ci struct device_node *tmp_np; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (!of_node_is_type(np, "spe")) { 588c2ecf20Sopenharmony_ci for (i = 0; i < cbe_regs_map_count; i++) 598c2ecf20Sopenharmony_ci if (cbe_regs_maps[i].cpu_node == np || 608c2ecf20Sopenharmony_ci cbe_regs_maps[i].be_node == np) 618c2ecf20Sopenharmony_ci return &cbe_regs_maps[i]; 628c2ecf20Sopenharmony_ci return NULL; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (np->data) 668c2ecf20Sopenharmony_ci return np->data; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* walk up path until cpu or be node was found */ 698c2ecf20Sopenharmony_ci tmp_np = np; 708c2ecf20Sopenharmony_ci do { 718c2ecf20Sopenharmony_ci tmp_np = tmp_np->parent; 728c2ecf20Sopenharmony_ci /* on a correct devicetree we wont get up to root */ 738c2ecf20Sopenharmony_ci BUG_ON(!tmp_np); 748c2ecf20Sopenharmony_ci } while (!of_node_is_type(tmp_np, "cpu") || 758c2ecf20Sopenharmony_ci !of_node_is_type(tmp_np, "be")); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci np->data = cbe_find_map(tmp_np); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return np->data; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 858c2ecf20Sopenharmony_ci if (map == NULL) 868c2ecf20Sopenharmony_ci return NULL; 878c2ecf20Sopenharmony_ci return map->pmd_regs; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_pmd_regs); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 948c2ecf20Sopenharmony_ci if (map == NULL) 958c2ecf20Sopenharmony_ci return NULL; 968c2ecf20Sopenharmony_ci return map->pmd_regs; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_cpu_pmd_regs); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct cbe_pmd_shadow_regs *cbe_get_pmd_shadow_regs(struct device_node *np) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 1038c2ecf20Sopenharmony_ci if (map == NULL) 1048c2ecf20Sopenharmony_ci return NULL; 1058c2ecf20Sopenharmony_ci return &map->pmd_shadow_regs; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistruct cbe_pmd_shadow_regs *cbe_get_cpu_pmd_shadow_regs(int cpu) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 1118c2ecf20Sopenharmony_ci if (map == NULL) 1128c2ecf20Sopenharmony_ci return NULL; 1138c2ecf20Sopenharmony_ci return &map->pmd_shadow_regs; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistruct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 1198c2ecf20Sopenharmony_ci if (map == NULL) 1208c2ecf20Sopenharmony_ci return NULL; 1218c2ecf20Sopenharmony_ci return map->iic_regs; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 1278c2ecf20Sopenharmony_ci if (map == NULL) 1288c2ecf20Sopenharmony_ci return NULL; 1298c2ecf20Sopenharmony_ci return map->iic_regs; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 1358c2ecf20Sopenharmony_ci if (map == NULL) 1368c2ecf20Sopenharmony_ci return NULL; 1378c2ecf20Sopenharmony_ci return map->mic_tm_regs; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistruct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 1438c2ecf20Sopenharmony_ci if (map == NULL) 1448c2ecf20Sopenharmony_ci return NULL; 1458c2ecf20Sopenharmony_ci return map->mic_tm_regs; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_cpu_mic_tm_regs); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciu32 cbe_get_hw_thread_id(int cpu) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci return cbe_thread_map[cpu].thread_id; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_hw_thread_id); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciu32 cbe_cpu_to_node(int cpu) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci return cbe_thread_map[cpu].cbe_id; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_cpu_to_node); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciu32 cbe_node_to_cpu(int node) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci return cpumask_first(&cbe_local_mask[node]); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_node_to_cpu); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic struct device_node *cbe_get_be_node(int cpu_id) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct device_node *np; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci for_each_node_by_type (np, "be") { 1738c2ecf20Sopenharmony_ci int len,i; 1748c2ecf20Sopenharmony_ci const phandle *cpu_handle; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci cpu_handle = of_get_property(np, "cpus", &len); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * the CAB SLOF tree is non compliant, so we just assume 1808c2ecf20Sopenharmony_ci * there is only one node 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!cpu_handle)) 1838c2ecf20Sopenharmony_ci return np; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for (i=0; i<len; i++) 1868c2ecf20Sopenharmony_ci if (of_find_node_by_phandle(cpu_handle[i]) == of_get_cpu_node(cpu_id, NULL)) 1878c2ecf20Sopenharmony_ci return np; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return NULL; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void __init cbe_fill_regs_map(struct cbe_regs_map *map) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci if(map->be_node) { 1968c2ecf20Sopenharmony_ci struct device_node *be, *np; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci be = map->be_node; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci for_each_node_by_type(np, "pervasive") 2018c2ecf20Sopenharmony_ci if (of_get_parent(np) == be) 2028c2ecf20Sopenharmony_ci map->pmd_regs = of_iomap(np, 0); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for_each_node_by_type(np, "CBEA-Internal-Interrupt-Controller") 2058c2ecf20Sopenharmony_ci if (of_get_parent(np) == be) 2068c2ecf20Sopenharmony_ci map->iic_regs = of_iomap(np, 2); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for_each_node_by_type(np, "mic-tm") 2098c2ecf20Sopenharmony_ci if (of_get_parent(np) == be) 2108c2ecf20Sopenharmony_ci map->mic_tm_regs = of_iomap(np, 0); 2118c2ecf20Sopenharmony_ci } else { 2128c2ecf20Sopenharmony_ci struct device_node *cpu; 2138c2ecf20Sopenharmony_ci /* That hack must die die die ! */ 2148c2ecf20Sopenharmony_ci const struct address_prop { 2158c2ecf20Sopenharmony_ci unsigned long address; 2168c2ecf20Sopenharmony_ci unsigned int len; 2178c2ecf20Sopenharmony_ci } __attribute__((packed)) *prop; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci cpu = map->cpu_node; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci prop = of_get_property(cpu, "pervasive", NULL); 2228c2ecf20Sopenharmony_ci if (prop != NULL) 2238c2ecf20Sopenharmony_ci map->pmd_regs = ioremap(prop->address, prop->len); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci prop = of_get_property(cpu, "iic", NULL); 2268c2ecf20Sopenharmony_ci if (prop != NULL) 2278c2ecf20Sopenharmony_ci map->iic_regs = ioremap(prop->address, prop->len); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci prop = of_get_property(cpu, "mic-tm", NULL); 2308c2ecf20Sopenharmony_ci if (prop != NULL) 2318c2ecf20Sopenharmony_ci map->mic_tm_regs = ioremap(prop->address, prop->len); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_civoid __init cbe_regs_init(void) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci int i; 2398c2ecf20Sopenharmony_ci unsigned int thread_id; 2408c2ecf20Sopenharmony_ci struct device_node *cpu; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Build local fast map of CPUs */ 2438c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 2448c2ecf20Sopenharmony_ci cbe_thread_map[i].cpu_node = of_get_cpu_node(i, &thread_id); 2458c2ecf20Sopenharmony_ci cbe_thread_map[i].be_node = cbe_get_be_node(i); 2468c2ecf20Sopenharmony_ci cbe_thread_map[i].thread_id = thread_id; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Find maps for each device tree CPU */ 2508c2ecf20Sopenharmony_ci for_each_node_by_type(cpu, "cpu") { 2518c2ecf20Sopenharmony_ci struct cbe_regs_map *map; 2528c2ecf20Sopenharmony_ci unsigned int cbe_id; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci cbe_id = cbe_regs_map_count++; 2558c2ecf20Sopenharmony_ci map = &cbe_regs_maps[cbe_id]; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (cbe_regs_map_count > MAX_CBE) { 2588c2ecf20Sopenharmony_ci printk(KERN_ERR "cbe_regs: More BE chips than supported" 2598c2ecf20Sopenharmony_ci "!\n"); 2608c2ecf20Sopenharmony_ci cbe_regs_map_count--; 2618c2ecf20Sopenharmony_ci of_node_put(cpu); 2628c2ecf20Sopenharmony_ci return; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci map->cpu_node = cpu; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 2678c2ecf20Sopenharmony_ci struct cbe_thread_map *thread = &cbe_thread_map[i]; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (thread->cpu_node == cpu) { 2708c2ecf20Sopenharmony_ci thread->regs = map; 2718c2ecf20Sopenharmony_ci thread->cbe_id = cbe_id; 2728c2ecf20Sopenharmony_ci map->be_node = thread->be_node; 2738c2ecf20Sopenharmony_ci cpumask_set_cpu(i, &cbe_local_mask[cbe_id]); 2748c2ecf20Sopenharmony_ci if(thread->thread_id == 0) 2758c2ecf20Sopenharmony_ci cpumask_set_cpu(i, &cbe_first_online_cpu); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci cbe_fill_regs_map(map); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 283