162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * spu management operations for of based platforms 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 662306a36Sopenharmony_ci * Copyright 2006 Sony Corp. 762306a36Sopenharmony_ci * (C) Copyright 2007 TOSHIBA CORPORATION 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/ptrace.h> 1462306a36Sopenharmony_ci#include <linux/wait.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/spu.h> 2362306a36Sopenharmony_ci#include <asm/spu_priv1.h> 2462306a36Sopenharmony_ci#include <asm/firmware.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "spufs/spufs.h" 2762306a36Sopenharmony_ci#include "interrupt.h" 2862306a36Sopenharmony_ci#include "spu_priv1_mmio.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct device_node *spu_devnode(struct spu *spu) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return spu->devnode; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_devnode); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic u64 __init find_spu_unit_number(struct device_node *spe) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci const unsigned int *prop; 4062306a36Sopenharmony_ci int proplen; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* new device trees should provide the physical-id attribute */ 4362306a36Sopenharmony_ci prop = of_get_property(spe, "physical-id", &proplen); 4462306a36Sopenharmony_ci if (proplen == 4) 4562306a36Sopenharmony_ci return (u64)*prop; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* celleb device tree provides the unit-id */ 4862306a36Sopenharmony_ci prop = of_get_property(spe, "unit-id", &proplen); 4962306a36Sopenharmony_ci if (proplen == 4) 5062306a36Sopenharmony_ci return (u64)*prop; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* legacy device trees provide the id in the reg attribute */ 5362306a36Sopenharmony_ci prop = of_get_property(spe, "reg", &proplen); 5462306a36Sopenharmony_ci if (proplen == 4) 5562306a36Sopenharmony_ci return (u64)*prop; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void spu_unmap(struct spu *spu) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) 6362306a36Sopenharmony_ci iounmap(spu->priv1); 6462306a36Sopenharmony_ci iounmap(spu->priv2); 6562306a36Sopenharmony_ci iounmap(spu->problem); 6662306a36Sopenharmony_ci iounmap((__force u8 __iomem *)spu->local_store); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int __init spu_map_interrupts_old(struct spu *spu, 7062306a36Sopenharmony_ci struct device_node *np) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci unsigned int isrc; 7362306a36Sopenharmony_ci const u32 *tmp; 7462306a36Sopenharmony_ci int nid; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Get the interrupt source unit from the device-tree */ 7762306a36Sopenharmony_ci tmp = of_get_property(np, "isrc", NULL); 7862306a36Sopenharmony_ci if (!tmp) 7962306a36Sopenharmony_ci return -ENODEV; 8062306a36Sopenharmony_ci isrc = tmp[0]; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci tmp = of_get_property(np->parent->parent, "node-id", NULL); 8362306a36Sopenharmony_ci if (!tmp) { 8462306a36Sopenharmony_ci printk(KERN_WARNING "%s: can't find node-id\n", __func__); 8562306a36Sopenharmony_ci nid = spu->node; 8662306a36Sopenharmony_ci } else 8762306a36Sopenharmony_ci nid = tmp[0]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Add the node number */ 9062306a36Sopenharmony_ci isrc |= nid << IIC_IRQ_NODE_SHIFT; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Now map interrupts of all 3 classes */ 9362306a36Sopenharmony_ci spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); 9462306a36Sopenharmony_ci spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc); 9562306a36Sopenharmony_ci spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Right now, we only fail if class 2 failed */ 9862306a36Sopenharmony_ci if (!spu->irqs[2]) 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void __iomem * __init spu_map_prop_old(struct spu *spu, 10562306a36Sopenharmony_ci struct device_node *n, 10662306a36Sopenharmony_ci const char *name) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci const struct address_prop { 10962306a36Sopenharmony_ci unsigned long address; 11062306a36Sopenharmony_ci unsigned int len; 11162306a36Sopenharmony_ci } __attribute__((packed)) *prop; 11262306a36Sopenharmony_ci int proplen; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci prop = of_get_property(n, name, &proplen); 11562306a36Sopenharmony_ci if (prop == NULL || proplen != sizeof (struct address_prop)) 11662306a36Sopenharmony_ci return NULL; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return ioremap(prop->address, prop->len); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int __init spu_map_device_old(struct spu *spu) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct device_node *node = spu->devnode; 12462306a36Sopenharmony_ci const char *prop; 12562306a36Sopenharmony_ci int ret; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = -ENODEV; 12862306a36Sopenharmony_ci spu->name = of_get_property(node, "name", NULL); 12962306a36Sopenharmony_ci if (!spu->name) 13062306a36Sopenharmony_ci goto out; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci prop = of_get_property(node, "local-store", NULL); 13362306a36Sopenharmony_ci if (!prop) 13462306a36Sopenharmony_ci goto out; 13562306a36Sopenharmony_ci spu->local_store_phys = *(unsigned long *)prop; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* we use local store as ram, not io memory */ 13862306a36Sopenharmony_ci spu->local_store = (void __force *) 13962306a36Sopenharmony_ci spu_map_prop_old(spu, node, "local-store"); 14062306a36Sopenharmony_ci if (!spu->local_store) 14162306a36Sopenharmony_ci goto out; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci prop = of_get_property(node, "problem", NULL); 14462306a36Sopenharmony_ci if (!prop) 14562306a36Sopenharmony_ci goto out_unmap; 14662306a36Sopenharmony_ci spu->problem_phys = *(unsigned long *)prop; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci spu->problem = spu_map_prop_old(spu, node, "problem"); 14962306a36Sopenharmony_ci if (!spu->problem) 15062306a36Sopenharmony_ci goto out_unmap; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci spu->priv2 = spu_map_prop_old(spu, node, "priv2"); 15362306a36Sopenharmony_ci if (!spu->priv2) 15462306a36Sopenharmony_ci goto out_unmap; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 15762306a36Sopenharmony_ci spu->priv1 = spu_map_prop_old(spu, node, "priv1"); 15862306a36Sopenharmony_ci if (!spu->priv1) 15962306a36Sopenharmony_ci goto out_unmap; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ret = 0; 16362306a36Sopenharmony_ci goto out; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciout_unmap: 16662306a36Sopenharmony_ci spu_unmap(spu); 16762306a36Sopenharmony_ciout: 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int __init spu_map_interrupts(struct spu *spu, struct device_node *np) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int i; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci for (i=0; i < 3; i++) { 17662306a36Sopenharmony_ci spu->irqs[i] = irq_of_parse_and_map(np, i); 17762306a36Sopenharmony_ci if (!spu->irqs[i]) 17862306a36Sopenharmony_ci goto err; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cierr: 18362306a36Sopenharmony_ci pr_debug("failed to map irq %x for spu %s\n", i, spu->name); 18462306a36Sopenharmony_ci for (; i >= 0; i--) { 18562306a36Sopenharmony_ci if (spu->irqs[i]) 18662306a36Sopenharmony_ci irq_dispose_mapping(spu->irqs[i]); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int __init spu_map_resource(struct spu *spu, int nr, 19262306a36Sopenharmony_ci void __iomem** virt, unsigned long *phys) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct device_node *np = spu->devnode; 19562306a36Sopenharmony_ci struct resource resource = { }; 19662306a36Sopenharmony_ci unsigned long len; 19762306a36Sopenharmony_ci int ret; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ret = of_address_to_resource(np, nr, &resource); 20062306a36Sopenharmony_ci if (ret) 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci if (phys) 20362306a36Sopenharmony_ci *phys = resource.start; 20462306a36Sopenharmony_ci len = resource_size(&resource); 20562306a36Sopenharmony_ci *virt = ioremap(resource.start, len); 20662306a36Sopenharmony_ci if (!*virt) 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int __init spu_map_device(struct spu *spu) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct device_node *np = spu->devnode; 21462306a36Sopenharmony_ci int ret = -ENODEV; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci spu->name = of_get_property(np, "name", NULL); 21762306a36Sopenharmony_ci if (!spu->name) 21862306a36Sopenharmony_ci goto out; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = spu_map_resource(spu, 0, (void __iomem**)&spu->local_store, 22162306a36Sopenharmony_ci &spu->local_store_phys); 22262306a36Sopenharmony_ci if (ret) { 22362306a36Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 0\n", 22462306a36Sopenharmony_ci np); 22562306a36Sopenharmony_ci goto out; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci ret = spu_map_resource(spu, 1, (void __iomem**)&spu->problem, 22862306a36Sopenharmony_ci &spu->problem_phys); 22962306a36Sopenharmony_ci if (ret) { 23062306a36Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 1\n", 23162306a36Sopenharmony_ci np); 23262306a36Sopenharmony_ci goto out_unmap; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci ret = spu_map_resource(spu, 2, (void __iomem**)&spu->priv2, NULL); 23562306a36Sopenharmony_ci if (ret) { 23662306a36Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 2\n", 23762306a36Sopenharmony_ci np); 23862306a36Sopenharmony_ci goto out_unmap; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) 24162306a36Sopenharmony_ci ret = spu_map_resource(spu, 3, 24262306a36Sopenharmony_ci (void __iomem**)&spu->priv1, NULL); 24362306a36Sopenharmony_ci if (ret) { 24462306a36Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 3\n", 24562306a36Sopenharmony_ci np); 24662306a36Sopenharmony_ci goto out_unmap; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci pr_debug("spu_new: %pOF maps:\n", np); 24962306a36Sopenharmony_ci pr_debug(" local store : 0x%016lx -> 0x%p\n", 25062306a36Sopenharmony_ci spu->local_store_phys, spu->local_store); 25162306a36Sopenharmony_ci pr_debug(" problem state : 0x%016lx -> 0x%p\n", 25262306a36Sopenharmony_ci spu->problem_phys, spu->problem); 25362306a36Sopenharmony_ci pr_debug(" priv2 : 0x%p\n", spu->priv2); 25462306a36Sopenharmony_ci pr_debug(" priv1 : 0x%p\n", spu->priv1); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciout_unmap: 25962306a36Sopenharmony_ci spu_unmap(spu); 26062306a36Sopenharmony_ciout: 26162306a36Sopenharmony_ci pr_debug("failed to map spe %s: %d\n", spu->name, ret); 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int __init of_enumerate_spus(int (*fn)(void *data)) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci int ret; 26862306a36Sopenharmony_ci struct device_node *node; 26962306a36Sopenharmony_ci unsigned int n = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ret = -ENODEV; 27262306a36Sopenharmony_ci for_each_node_by_type(node, "spe") { 27362306a36Sopenharmony_ci ret = fn(node); 27462306a36Sopenharmony_ci if (ret) { 27562306a36Sopenharmony_ci printk(KERN_WARNING "%s: Error initializing %pOFn\n", 27662306a36Sopenharmony_ci __func__, node); 27762306a36Sopenharmony_ci of_node_put(node); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci n++; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci return ret ? ret : n; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int __init of_create_spu(struct spu *spu, void *data) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int ret; 28862306a36Sopenharmony_ci struct device_node *spe = (struct device_node *)data; 28962306a36Sopenharmony_ci static int legacy_map = 0, legacy_irq = 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci spu->devnode = of_node_get(spe); 29262306a36Sopenharmony_ci spu->spe_id = find_spu_unit_number(spe); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci spu->node = of_node_to_nid(spe); 29562306a36Sopenharmony_ci if (spu->node >= MAX_NUMNODES) { 29662306a36Sopenharmony_ci printk(KERN_WARNING "SPE %pOF on node %d ignored," 29762306a36Sopenharmony_ci " node number too big\n", spe, spu->node); 29862306a36Sopenharmony_ci printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n"); 29962306a36Sopenharmony_ci ret = -ENODEV; 30062306a36Sopenharmony_ci goto out; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = spu_map_device(spu); 30462306a36Sopenharmony_ci if (ret) { 30562306a36Sopenharmony_ci if (!legacy_map) { 30662306a36Sopenharmony_ci legacy_map = 1; 30762306a36Sopenharmony_ci printk(KERN_WARNING "%s: Legacy device tree found, " 30862306a36Sopenharmony_ci "trying to map old style\n", __func__); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci ret = spu_map_device_old(spu); 31162306a36Sopenharmony_ci if (ret) { 31262306a36Sopenharmony_ci printk(KERN_ERR "Unable to map %s\n", 31362306a36Sopenharmony_ci spu->name); 31462306a36Sopenharmony_ci goto out; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = spu_map_interrupts(spu, spe); 31962306a36Sopenharmony_ci if (ret) { 32062306a36Sopenharmony_ci if (!legacy_irq) { 32162306a36Sopenharmony_ci legacy_irq = 1; 32262306a36Sopenharmony_ci printk(KERN_WARNING "%s: Legacy device tree found, " 32362306a36Sopenharmony_ci "trying old style irq\n", __func__); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci ret = spu_map_interrupts_old(spu, spe); 32662306a36Sopenharmony_ci if (ret) { 32762306a36Sopenharmony_ci printk(KERN_ERR "%s: could not map interrupts\n", 32862306a36Sopenharmony_ci spu->name); 32962306a36Sopenharmony_ci goto out_unmap; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci pr_debug("Using SPE %s %p %p %p %p %d\n", spu->name, 33462306a36Sopenharmony_ci spu->local_store, spu->problem, spu->priv1, 33562306a36Sopenharmony_ci spu->priv2, spu->number); 33662306a36Sopenharmony_ci goto out; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ciout_unmap: 33962306a36Sopenharmony_ci spu_unmap(spu); 34062306a36Sopenharmony_ciout: 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int of_destroy_spu(struct spu *spu) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci spu_unmap(spu); 34762306a36Sopenharmony_ci of_node_put(spu->devnode); 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void enable_spu_by_master_run(struct spu_context *ctx) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci ctx->ops->master_start(ctx); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic void disable_spu_by_master_run(struct spu_context *ctx) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci ctx->ops->master_stop(ctx); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* Hardcoded affinity idxs for qs20 */ 36262306a36Sopenharmony_ci#define QS20_SPES_PER_BE 8 36362306a36Sopenharmony_cistatic int qs20_reg_idxs[QS20_SPES_PER_BE] = { 0, 2, 4, 6, 7, 5, 3, 1 }; 36462306a36Sopenharmony_cistatic int qs20_reg_memory[QS20_SPES_PER_BE] = { 1, 1, 0, 0, 0, 0, 0, 0 }; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic struct spu *__init spu_lookup_reg(int node, u32 reg) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct spu *spu; 36962306a36Sopenharmony_ci const u32 *spu_reg; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) { 37262306a36Sopenharmony_ci spu_reg = of_get_property(spu_devnode(spu), "reg", NULL); 37362306a36Sopenharmony_ci if (*spu_reg == reg) 37462306a36Sopenharmony_ci return spu; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci return NULL; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void __init init_affinity_qs20_harcoded(void) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci int node, i; 38262306a36Sopenharmony_ci struct spu *last_spu, *spu; 38362306a36Sopenharmony_ci u32 reg; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (node = 0; node < MAX_NUMNODES; node++) { 38662306a36Sopenharmony_ci last_spu = NULL; 38762306a36Sopenharmony_ci for (i = 0; i < QS20_SPES_PER_BE; i++) { 38862306a36Sopenharmony_ci reg = qs20_reg_idxs[i]; 38962306a36Sopenharmony_ci spu = spu_lookup_reg(node, reg); 39062306a36Sopenharmony_ci if (!spu) 39162306a36Sopenharmony_ci continue; 39262306a36Sopenharmony_ci spu->has_mem_affinity = qs20_reg_memory[reg]; 39362306a36Sopenharmony_ci if (last_spu) 39462306a36Sopenharmony_ci list_add_tail(&spu->aff_list, 39562306a36Sopenharmony_ci &last_spu->aff_list); 39662306a36Sopenharmony_ci last_spu = spu; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int __init of_has_vicinity(void) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct device_node *dn; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci for_each_node_by_type(dn, "spe") { 40662306a36Sopenharmony_ci if (of_property_present(dn, "vicinity")) { 40762306a36Sopenharmony_ci of_node_put(dn); 40862306a36Sopenharmony_ci return 1; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic struct spu *__init devnode_spu(int cbe, struct device_node *dn) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct spu *spu; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci list_for_each_entry(spu, &cbe_spu_info[cbe].spus, cbe_list) 41962306a36Sopenharmony_ci if (spu_devnode(spu) == dn) 42062306a36Sopenharmony_ci return spu; 42162306a36Sopenharmony_ci return NULL; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic struct spu * __init 42562306a36Sopenharmony_cineighbour_spu(int cbe, struct device_node *target, struct device_node *avoid) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct spu *spu; 42862306a36Sopenharmony_ci struct device_node *spu_dn; 42962306a36Sopenharmony_ci const phandle *vic_handles; 43062306a36Sopenharmony_ci int lenp, i; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci list_for_each_entry(spu, &cbe_spu_info[cbe].spus, cbe_list) { 43362306a36Sopenharmony_ci spu_dn = spu_devnode(spu); 43462306a36Sopenharmony_ci if (spu_dn == avoid) 43562306a36Sopenharmony_ci continue; 43662306a36Sopenharmony_ci vic_handles = of_get_property(spu_dn, "vicinity", &lenp); 43762306a36Sopenharmony_ci for (i=0; i < (lenp / sizeof(phandle)); i++) { 43862306a36Sopenharmony_ci if (vic_handles[i] == target->phandle) 43962306a36Sopenharmony_ci return spu; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci return NULL; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void __init init_affinity_node(int cbe) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct spu *spu, *last_spu; 44862306a36Sopenharmony_ci struct device_node *vic_dn, *last_spu_dn; 44962306a36Sopenharmony_ci phandle avoid_ph; 45062306a36Sopenharmony_ci const phandle *vic_handles; 45162306a36Sopenharmony_ci int lenp, i, added; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci last_spu = list_first_entry(&cbe_spu_info[cbe].spus, struct spu, 45462306a36Sopenharmony_ci cbe_list); 45562306a36Sopenharmony_ci avoid_ph = 0; 45662306a36Sopenharmony_ci for (added = 1; added < cbe_spu_info[cbe].n_spus; added++) { 45762306a36Sopenharmony_ci last_spu_dn = spu_devnode(last_spu); 45862306a36Sopenharmony_ci vic_handles = of_get_property(last_spu_dn, "vicinity", &lenp); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * Walk through each phandle in vicinity property of the spu 46262306a36Sopenharmony_ci * (typically two vicinity phandles per spe node) 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ci for (i = 0; i < (lenp / sizeof(phandle)); i++) { 46562306a36Sopenharmony_ci if (vic_handles[i] == avoid_ph) 46662306a36Sopenharmony_ci continue; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci vic_dn = of_find_node_by_phandle(vic_handles[i]); 46962306a36Sopenharmony_ci if (!vic_dn) 47062306a36Sopenharmony_ci continue; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (of_node_name_eq(vic_dn, "spe") ) { 47362306a36Sopenharmony_ci spu = devnode_spu(cbe, vic_dn); 47462306a36Sopenharmony_ci avoid_ph = last_spu_dn->phandle; 47562306a36Sopenharmony_ci } else { 47662306a36Sopenharmony_ci /* 47762306a36Sopenharmony_ci * "mic-tm" and "bif0" nodes do not have 47862306a36Sopenharmony_ci * vicinity property. So we need to find the 47962306a36Sopenharmony_ci * spe which has vic_dn as neighbour, but 48062306a36Sopenharmony_ci * skipping the one we came from (last_spu_dn) 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci spu = neighbour_spu(cbe, vic_dn, last_spu_dn); 48362306a36Sopenharmony_ci if (!spu) 48462306a36Sopenharmony_ci continue; 48562306a36Sopenharmony_ci if (of_node_name_eq(vic_dn, "mic-tm")) { 48662306a36Sopenharmony_ci last_spu->has_mem_affinity = 1; 48762306a36Sopenharmony_ci spu->has_mem_affinity = 1; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci avoid_ph = vic_dn->phandle; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci of_node_put(vic_dn); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci list_add_tail(&spu->aff_list, &last_spu->aff_list); 49562306a36Sopenharmony_ci last_spu = spu; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void __init init_affinity_fw(void) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci int cbe; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (cbe = 0; cbe < MAX_NUMNODES; cbe++) 50662306a36Sopenharmony_ci init_affinity_node(cbe); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int __init init_affinity(void) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci if (of_has_vicinity()) { 51262306a36Sopenharmony_ci init_affinity_fw(); 51362306a36Sopenharmony_ci } else { 51462306a36Sopenharmony_ci if (of_machine_is_compatible("IBM,CPBW-1.0")) 51562306a36Sopenharmony_ci init_affinity_qs20_harcoded(); 51662306a36Sopenharmony_ci else 51762306a36Sopenharmony_ci printk("No affinity configuration found\n"); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ciconst struct spu_management_ops spu_management_of_ops = { 52462306a36Sopenharmony_ci .enumerate_spus = of_enumerate_spus, 52562306a36Sopenharmony_ci .create_spu = of_create_spu, 52662306a36Sopenharmony_ci .destroy_spu = of_destroy_spu, 52762306a36Sopenharmony_ci .enable_spu = enable_spu_by_master_run, 52862306a36Sopenharmony_ci .disable_spu = disable_spu_by_master_run, 52962306a36Sopenharmony_ci .init_affinity = init_affinity, 53062306a36Sopenharmony_ci}; 531