162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cbe_regs.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Accessor routines for the various MMIO register blocks of the CBE 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (c) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/percpu.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/pgtable.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/io.h> 1862306a36Sopenharmony_ci#include <asm/ptrace.h> 1962306a36Sopenharmony_ci#include <asm/cell-regs.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * Current implementation uses "cpu" nodes. We build our own mapping 2362306a36Sopenharmony_ci * array of cpu numbers to cpu nodes locally for now to allow interrupt 2462306a36Sopenharmony_ci * time code to have a fast path rather than call of_get_cpu_node(). If 2562306a36Sopenharmony_ci * we implement cpu hotplug, we'll have to install an appropriate notifier 2662306a36Sopenharmony_ci * in order to release references to the cpu going away 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic struct cbe_regs_map 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct device_node *cpu_node; 3162306a36Sopenharmony_ci struct device_node *be_node; 3262306a36Sopenharmony_ci struct cbe_pmd_regs __iomem *pmd_regs; 3362306a36Sopenharmony_ci struct cbe_iic_regs __iomem *iic_regs; 3462306a36Sopenharmony_ci struct cbe_mic_tm_regs __iomem *mic_tm_regs; 3562306a36Sopenharmony_ci struct cbe_pmd_shadow_regs pmd_shadow_regs; 3662306a36Sopenharmony_ci} cbe_regs_maps[MAX_CBE]; 3762306a36Sopenharmony_cistatic int cbe_regs_map_count; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic struct cbe_thread_map 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct device_node *cpu_node; 4262306a36Sopenharmony_ci struct device_node *be_node; 4362306a36Sopenharmony_ci struct cbe_regs_map *regs; 4462306a36Sopenharmony_ci unsigned int thread_id; 4562306a36Sopenharmony_ci unsigned int cbe_id; 4662306a36Sopenharmony_ci} cbe_thread_map[NR_CPUS]; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic cpumask_t cbe_local_mask[MAX_CBE] = { [0 ... MAX_CBE-1] = {CPU_BITS_NONE} }; 4962306a36Sopenharmony_cistatic cpumask_t cbe_first_online_cpu = { CPU_BITS_NONE }; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic struct cbe_regs_map *cbe_find_map(struct device_node *np) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci int i; 5462306a36Sopenharmony_ci struct device_node *tmp_np; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!of_node_is_type(np, "spe")) { 5762306a36Sopenharmony_ci for (i = 0; i < cbe_regs_map_count; i++) 5862306a36Sopenharmony_ci if (cbe_regs_maps[i].cpu_node == np || 5962306a36Sopenharmony_ci cbe_regs_maps[i].be_node == np) 6062306a36Sopenharmony_ci return &cbe_regs_maps[i]; 6162306a36Sopenharmony_ci return NULL; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (np->data) 6562306a36Sopenharmony_ci return np->data; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* walk up path until cpu or be node was found */ 6862306a36Sopenharmony_ci tmp_np = np; 6962306a36Sopenharmony_ci do { 7062306a36Sopenharmony_ci tmp_np = tmp_np->parent; 7162306a36Sopenharmony_ci /* on a correct devicetree we wont get up to root */ 7262306a36Sopenharmony_ci BUG_ON(!tmp_np); 7362306a36Sopenharmony_ci } while (!of_node_is_type(tmp_np, "cpu") || 7462306a36Sopenharmony_ci !of_node_is_type(tmp_np, "be")); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci np->data = cbe_find_map(tmp_np); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return np->data; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 8462306a36Sopenharmony_ci if (map == NULL) 8562306a36Sopenharmony_ci return NULL; 8662306a36Sopenharmony_ci return map->pmd_regs; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_pmd_regs); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 9362306a36Sopenharmony_ci if (map == NULL) 9462306a36Sopenharmony_ci return NULL; 9562306a36Sopenharmony_ci return map->pmd_regs; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_cpu_pmd_regs); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct cbe_pmd_shadow_regs *cbe_get_pmd_shadow_regs(struct device_node *np) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 10262306a36Sopenharmony_ci if (map == NULL) 10362306a36Sopenharmony_ci return NULL; 10462306a36Sopenharmony_ci return &map->pmd_shadow_regs; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct cbe_pmd_shadow_regs *cbe_get_cpu_pmd_shadow_regs(int cpu) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 11062306a36Sopenharmony_ci if (map == NULL) 11162306a36Sopenharmony_ci return NULL; 11262306a36Sopenharmony_ci return &map->pmd_shadow_regs; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 11862306a36Sopenharmony_ci if (map == NULL) 11962306a36Sopenharmony_ci return NULL; 12062306a36Sopenharmony_ci return map->iic_regs; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistruct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 12662306a36Sopenharmony_ci if (map == NULL) 12762306a36Sopenharmony_ci return NULL; 12862306a36Sopenharmony_ci return map->iic_regs; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_find_map(np); 13462306a36Sopenharmony_ci if (map == NULL) 13562306a36Sopenharmony_ci return NULL; 13662306a36Sopenharmony_ci return map->mic_tm_regs; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistruct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct cbe_regs_map *map = cbe_thread_map[cpu].regs; 14262306a36Sopenharmony_ci if (map == NULL) 14362306a36Sopenharmony_ci return NULL; 14462306a36Sopenharmony_ci return map->mic_tm_regs; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_cpu_mic_tm_regs); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciu32 cbe_get_hw_thread_id(int cpu) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci return cbe_thread_map[cpu].thread_id; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_get_hw_thread_id); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciu32 cbe_cpu_to_node(int cpu) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return cbe_thread_map[cpu].cbe_id; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_cpu_to_node); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciu32 cbe_node_to_cpu(int node) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci return cpumask_first(&cbe_local_mask[node]); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cbe_node_to_cpu); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic struct device_node *__init cbe_get_be_node(int cpu_id) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct device_node *np; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for_each_node_by_type (np, "be") { 17262306a36Sopenharmony_ci int len,i; 17362306a36Sopenharmony_ci const phandle *cpu_handle; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci cpu_handle = of_get_property(np, "cpus", &len); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * the CAB SLOF tree is non compliant, so we just assume 17962306a36Sopenharmony_ci * there is only one node 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci if (WARN_ON_ONCE(!cpu_handle)) 18262306a36Sopenharmony_ci return np; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for (i = 0; i < len; i++) { 18562306a36Sopenharmony_ci struct device_node *ch_np = of_find_node_by_phandle(cpu_handle[i]); 18662306a36Sopenharmony_ci struct device_node *ci_np = of_get_cpu_node(cpu_id, NULL); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci of_node_put(ch_np); 18962306a36Sopenharmony_ci of_node_put(ci_np); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (ch_np == ci_np) 19262306a36Sopenharmony_ci return np; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return NULL; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void __init cbe_fill_regs_map(struct cbe_regs_map *map) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci if(map->be_node) { 20262306a36Sopenharmony_ci struct device_node *be, *np, *parent_np; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci be = map->be_node; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for_each_node_by_type(np, "pervasive") { 20762306a36Sopenharmony_ci parent_np = of_get_parent(np); 20862306a36Sopenharmony_ci if (parent_np == be) 20962306a36Sopenharmony_ci map->pmd_regs = of_iomap(np, 0); 21062306a36Sopenharmony_ci of_node_put(parent_np); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci for_each_node_by_type(np, "CBEA-Internal-Interrupt-Controller") { 21462306a36Sopenharmony_ci parent_np = of_get_parent(np); 21562306a36Sopenharmony_ci if (parent_np == be) 21662306a36Sopenharmony_ci map->iic_regs = of_iomap(np, 2); 21762306a36Sopenharmony_ci of_node_put(parent_np); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci for_each_node_by_type(np, "mic-tm") { 22162306a36Sopenharmony_ci parent_np = of_get_parent(np); 22262306a36Sopenharmony_ci if (parent_np == be) 22362306a36Sopenharmony_ci map->mic_tm_regs = of_iomap(np, 0); 22462306a36Sopenharmony_ci of_node_put(parent_np); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci } else { 22762306a36Sopenharmony_ci struct device_node *cpu; 22862306a36Sopenharmony_ci /* That hack must die die die ! */ 22962306a36Sopenharmony_ci const struct address_prop { 23062306a36Sopenharmony_ci unsigned long address; 23162306a36Sopenharmony_ci unsigned int len; 23262306a36Sopenharmony_ci } __attribute__((packed)) *prop; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci cpu = map->cpu_node; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci prop = of_get_property(cpu, "pervasive", NULL); 23762306a36Sopenharmony_ci if (prop != NULL) 23862306a36Sopenharmony_ci map->pmd_regs = ioremap(prop->address, prop->len); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci prop = of_get_property(cpu, "iic", NULL); 24162306a36Sopenharmony_ci if (prop != NULL) 24262306a36Sopenharmony_ci map->iic_regs = ioremap(prop->address, prop->len); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci prop = of_get_property(cpu, "mic-tm", NULL); 24562306a36Sopenharmony_ci if (prop != NULL) 24662306a36Sopenharmony_ci map->mic_tm_regs = ioremap(prop->address, prop->len); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_civoid __init cbe_regs_init(void) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int i; 25462306a36Sopenharmony_ci unsigned int thread_id; 25562306a36Sopenharmony_ci struct device_node *cpu; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Build local fast map of CPUs */ 25862306a36Sopenharmony_ci for_each_possible_cpu(i) { 25962306a36Sopenharmony_ci cbe_thread_map[i].cpu_node = of_get_cpu_node(i, &thread_id); 26062306a36Sopenharmony_ci cbe_thread_map[i].be_node = cbe_get_be_node(i); 26162306a36Sopenharmony_ci cbe_thread_map[i].thread_id = thread_id; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Find maps for each device tree CPU */ 26562306a36Sopenharmony_ci for_each_node_by_type(cpu, "cpu") { 26662306a36Sopenharmony_ci struct cbe_regs_map *map; 26762306a36Sopenharmony_ci unsigned int cbe_id; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci cbe_id = cbe_regs_map_count++; 27062306a36Sopenharmony_ci map = &cbe_regs_maps[cbe_id]; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (cbe_regs_map_count > MAX_CBE) { 27362306a36Sopenharmony_ci printk(KERN_ERR "cbe_regs: More BE chips than supported" 27462306a36Sopenharmony_ci "!\n"); 27562306a36Sopenharmony_ci cbe_regs_map_count--; 27662306a36Sopenharmony_ci of_node_put(cpu); 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci of_node_put(map->cpu_node); 28062306a36Sopenharmony_ci map->cpu_node = of_node_get(cpu); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for_each_possible_cpu(i) { 28362306a36Sopenharmony_ci struct cbe_thread_map *thread = &cbe_thread_map[i]; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (thread->cpu_node == cpu) { 28662306a36Sopenharmony_ci thread->regs = map; 28762306a36Sopenharmony_ci thread->cbe_id = cbe_id; 28862306a36Sopenharmony_ci map->be_node = thread->be_node; 28962306a36Sopenharmony_ci cpumask_set_cpu(i, &cbe_local_mask[cbe_id]); 29062306a36Sopenharmony_ci if(thread->thread_id == 0) 29162306a36Sopenharmony_ci cpumask_set_cpu(i, &cbe_first_online_cpu); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci cbe_fill_regs_map(map); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 299