18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * spu management operations for of based platforms 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 68c2ecf20Sopenharmony_ci * Copyright 2006 Sony Corp. 78c2ecf20Sopenharmony_ci * (C) Copyright 2007 TOSHIBA CORPORATION 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 148c2ecf20Sopenharmony_ci#include <linux/wait.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/spu.h> 218c2ecf20Sopenharmony_ci#include <asm/spu_priv1.h> 228c2ecf20Sopenharmony_ci#include <asm/firmware.h> 238c2ecf20Sopenharmony_ci#include <asm/prom.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "spufs/spufs.h" 268c2ecf20Sopenharmony_ci#include "interrupt.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct device_node *spu_devnode(struct spu *spu) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return spu->devnode; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spu_devnode); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic u64 __init find_spu_unit_number(struct device_node *spe) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci const unsigned int *prop; 388c2ecf20Sopenharmony_ci int proplen; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* new device trees should provide the physical-id attribute */ 418c2ecf20Sopenharmony_ci prop = of_get_property(spe, "physical-id", &proplen); 428c2ecf20Sopenharmony_ci if (proplen == 4) 438c2ecf20Sopenharmony_ci return (u64)*prop; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* celleb device tree provides the unit-id */ 468c2ecf20Sopenharmony_ci prop = of_get_property(spe, "unit-id", &proplen); 478c2ecf20Sopenharmony_ci if (proplen == 4) 488c2ecf20Sopenharmony_ci return (u64)*prop; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* legacy device trees provide the id in the reg attribute */ 518c2ecf20Sopenharmony_ci prop = of_get_property(spe, "reg", &proplen); 528c2ecf20Sopenharmony_ci if (proplen == 4) 538c2ecf20Sopenharmony_ci return (u64)*prop; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void spu_unmap(struct spu *spu) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) 618c2ecf20Sopenharmony_ci iounmap(spu->priv1); 628c2ecf20Sopenharmony_ci iounmap(spu->priv2); 638c2ecf20Sopenharmony_ci iounmap(spu->problem); 648c2ecf20Sopenharmony_ci iounmap((__force u8 __iomem *)spu->local_store); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int __init spu_map_interrupts_old(struct spu *spu, 688c2ecf20Sopenharmony_ci struct device_node *np) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci unsigned int isrc; 718c2ecf20Sopenharmony_ci const u32 *tmp; 728c2ecf20Sopenharmony_ci int nid; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Get the interrupt source unit from the device-tree */ 758c2ecf20Sopenharmony_ci tmp = of_get_property(np, "isrc", NULL); 768c2ecf20Sopenharmony_ci if (!tmp) 778c2ecf20Sopenharmony_ci return -ENODEV; 788c2ecf20Sopenharmony_ci isrc = tmp[0]; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci tmp = of_get_property(np->parent->parent, "node-id", NULL); 818c2ecf20Sopenharmony_ci if (!tmp) { 828c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: can't find node-id\n", __func__); 838c2ecf20Sopenharmony_ci nid = spu->node; 848c2ecf20Sopenharmony_ci } else 858c2ecf20Sopenharmony_ci nid = tmp[0]; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Add the node number */ 888c2ecf20Sopenharmony_ci isrc |= nid << IIC_IRQ_NODE_SHIFT; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Now map interrupts of all 3 classes */ 918c2ecf20Sopenharmony_ci spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); 928c2ecf20Sopenharmony_ci spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc); 938c2ecf20Sopenharmony_ci spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Right now, we only fail if class 2 failed */ 968c2ecf20Sopenharmony_ci if (!spu->irqs[2]) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void __iomem * __init spu_map_prop_old(struct spu *spu, 1038c2ecf20Sopenharmony_ci struct device_node *n, 1048c2ecf20Sopenharmony_ci const char *name) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci const struct address_prop { 1078c2ecf20Sopenharmony_ci unsigned long address; 1088c2ecf20Sopenharmony_ci unsigned int len; 1098c2ecf20Sopenharmony_ci } __attribute__((packed)) *prop; 1108c2ecf20Sopenharmony_ci int proplen; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci prop = of_get_property(n, name, &proplen); 1138c2ecf20Sopenharmony_ci if (prop == NULL || proplen != sizeof (struct address_prop)) 1148c2ecf20Sopenharmony_ci return NULL; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return ioremap(prop->address, prop->len); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int __init spu_map_device_old(struct spu *spu) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct device_node *node = spu->devnode; 1228c2ecf20Sopenharmony_ci const char *prop; 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = -ENODEV; 1268c2ecf20Sopenharmony_ci spu->name = of_get_property(node, "name", NULL); 1278c2ecf20Sopenharmony_ci if (!spu->name) 1288c2ecf20Sopenharmony_ci goto out; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci prop = of_get_property(node, "local-store", NULL); 1318c2ecf20Sopenharmony_ci if (!prop) 1328c2ecf20Sopenharmony_ci goto out; 1338c2ecf20Sopenharmony_ci spu->local_store_phys = *(unsigned long *)prop; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* we use local store as ram, not io memory */ 1368c2ecf20Sopenharmony_ci spu->local_store = (void __force *) 1378c2ecf20Sopenharmony_ci spu_map_prop_old(spu, node, "local-store"); 1388c2ecf20Sopenharmony_ci if (!spu->local_store) 1398c2ecf20Sopenharmony_ci goto out; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci prop = of_get_property(node, "problem", NULL); 1428c2ecf20Sopenharmony_ci if (!prop) 1438c2ecf20Sopenharmony_ci goto out_unmap; 1448c2ecf20Sopenharmony_ci spu->problem_phys = *(unsigned long *)prop; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci spu->problem = spu_map_prop_old(spu, node, "problem"); 1478c2ecf20Sopenharmony_ci if (!spu->problem) 1488c2ecf20Sopenharmony_ci goto out_unmap; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spu->priv2 = spu_map_prop_old(spu, node, "priv2"); 1518c2ecf20Sopenharmony_ci if (!spu->priv2) 1528c2ecf20Sopenharmony_ci goto out_unmap; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 1558c2ecf20Sopenharmony_ci spu->priv1 = spu_map_prop_old(spu, node, "priv1"); 1568c2ecf20Sopenharmony_ci if (!spu->priv1) 1578c2ecf20Sopenharmony_ci goto out_unmap; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = 0; 1618c2ecf20Sopenharmony_ci goto out; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciout_unmap: 1648c2ecf20Sopenharmony_ci spu_unmap(spu); 1658c2ecf20Sopenharmony_ciout: 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int __init spu_map_interrupts(struct spu *spu, struct device_node *np) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci int i; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for (i=0; i < 3; i++) { 1748c2ecf20Sopenharmony_ci spu->irqs[i] = irq_of_parse_and_map(np, i); 1758c2ecf20Sopenharmony_ci if (!spu->irqs[i]) 1768c2ecf20Sopenharmony_ci goto err; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cierr: 1818c2ecf20Sopenharmony_ci pr_debug("failed to map irq %x for spu %s\n", i, spu->name); 1828c2ecf20Sopenharmony_ci for (; i >= 0; i--) { 1838c2ecf20Sopenharmony_ci if (spu->irqs[i]) 1848c2ecf20Sopenharmony_ci irq_dispose_mapping(spu->irqs[i]); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int spu_map_resource(struct spu *spu, int nr, 1908c2ecf20Sopenharmony_ci void __iomem** virt, unsigned long *phys) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct device_node *np = spu->devnode; 1938c2ecf20Sopenharmony_ci struct resource resource = { }; 1948c2ecf20Sopenharmony_ci unsigned long len; 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, nr, &resource); 1988c2ecf20Sopenharmony_ci if (ret) 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci if (phys) 2018c2ecf20Sopenharmony_ci *phys = resource.start; 2028c2ecf20Sopenharmony_ci len = resource_size(&resource); 2038c2ecf20Sopenharmony_ci *virt = ioremap(resource.start, len); 2048c2ecf20Sopenharmony_ci if (!*virt) 2058c2ecf20Sopenharmony_ci return -EINVAL; 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int __init spu_map_device(struct spu *spu) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct device_node *np = spu->devnode; 2128c2ecf20Sopenharmony_ci int ret = -ENODEV; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci spu->name = of_get_property(np, "name", NULL); 2158c2ecf20Sopenharmony_ci if (!spu->name) 2168c2ecf20Sopenharmony_ci goto out; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = spu_map_resource(spu, 0, (void __iomem**)&spu->local_store, 2198c2ecf20Sopenharmony_ci &spu->local_store_phys); 2208c2ecf20Sopenharmony_ci if (ret) { 2218c2ecf20Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 0\n", 2228c2ecf20Sopenharmony_ci np); 2238c2ecf20Sopenharmony_ci goto out; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci ret = spu_map_resource(spu, 1, (void __iomem**)&spu->problem, 2268c2ecf20Sopenharmony_ci &spu->problem_phys); 2278c2ecf20Sopenharmony_ci if (ret) { 2288c2ecf20Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 1\n", 2298c2ecf20Sopenharmony_ci np); 2308c2ecf20Sopenharmony_ci goto out_unmap; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci ret = spu_map_resource(spu, 2, (void __iomem**)&spu->priv2, NULL); 2338c2ecf20Sopenharmony_ci if (ret) { 2348c2ecf20Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 2\n", 2358c2ecf20Sopenharmony_ci np); 2368c2ecf20Sopenharmony_ci goto out_unmap; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) 2398c2ecf20Sopenharmony_ci ret = spu_map_resource(spu, 3, 2408c2ecf20Sopenharmony_ci (void __iomem**)&spu->priv1, NULL); 2418c2ecf20Sopenharmony_ci if (ret) { 2428c2ecf20Sopenharmony_ci pr_debug("spu_new: failed to map %pOF resource 3\n", 2438c2ecf20Sopenharmony_ci np); 2448c2ecf20Sopenharmony_ci goto out_unmap; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci pr_debug("spu_new: %pOF maps:\n", np); 2478c2ecf20Sopenharmony_ci pr_debug(" local store : 0x%016lx -> 0x%p\n", 2488c2ecf20Sopenharmony_ci spu->local_store_phys, spu->local_store); 2498c2ecf20Sopenharmony_ci pr_debug(" problem state : 0x%016lx -> 0x%p\n", 2508c2ecf20Sopenharmony_ci spu->problem_phys, spu->problem); 2518c2ecf20Sopenharmony_ci pr_debug(" priv2 : 0x%p\n", spu->priv2); 2528c2ecf20Sopenharmony_ci pr_debug(" priv1 : 0x%p\n", spu->priv1); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciout_unmap: 2578c2ecf20Sopenharmony_ci spu_unmap(spu); 2588c2ecf20Sopenharmony_ciout: 2598c2ecf20Sopenharmony_ci pr_debug("failed to map spe %s: %d\n", spu->name, ret); 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int __init of_enumerate_spus(int (*fn)(void *data)) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci int ret; 2668c2ecf20Sopenharmony_ci struct device_node *node; 2678c2ecf20Sopenharmony_ci unsigned int n = 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ret = -ENODEV; 2708c2ecf20Sopenharmony_ci for_each_node_by_type(node, "spe") { 2718c2ecf20Sopenharmony_ci ret = fn(node); 2728c2ecf20Sopenharmony_ci if (ret) { 2738c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Error initializing %pOFn\n", 2748c2ecf20Sopenharmony_ci __func__, node); 2758c2ecf20Sopenharmony_ci of_node_put(node); 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci n++; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci return ret ? ret : n; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int __init of_create_spu(struct spu *spu, void *data) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int ret; 2868c2ecf20Sopenharmony_ci struct device_node *spe = (struct device_node *)data; 2878c2ecf20Sopenharmony_ci static int legacy_map = 0, legacy_irq = 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spu->devnode = of_node_get(spe); 2908c2ecf20Sopenharmony_ci spu->spe_id = find_spu_unit_number(spe); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci spu->node = of_node_to_nid(spe); 2938c2ecf20Sopenharmony_ci if (spu->node >= MAX_NUMNODES) { 2948c2ecf20Sopenharmony_ci printk(KERN_WARNING "SPE %pOF on node %d ignored," 2958c2ecf20Sopenharmony_ci " node number too big\n", spe, spu->node); 2968c2ecf20Sopenharmony_ci printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n"); 2978c2ecf20Sopenharmony_ci ret = -ENODEV; 2988c2ecf20Sopenharmony_ci goto out; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = spu_map_device(spu); 3028c2ecf20Sopenharmony_ci if (ret) { 3038c2ecf20Sopenharmony_ci if (!legacy_map) { 3048c2ecf20Sopenharmony_ci legacy_map = 1; 3058c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Legacy device tree found, " 3068c2ecf20Sopenharmony_ci "trying to map old style\n", __func__); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci ret = spu_map_device_old(spu); 3098c2ecf20Sopenharmony_ci if (ret) { 3108c2ecf20Sopenharmony_ci printk(KERN_ERR "Unable to map %s\n", 3118c2ecf20Sopenharmony_ci spu->name); 3128c2ecf20Sopenharmony_ci goto out; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ret = spu_map_interrupts(spu, spe); 3178c2ecf20Sopenharmony_ci if (ret) { 3188c2ecf20Sopenharmony_ci if (!legacy_irq) { 3198c2ecf20Sopenharmony_ci legacy_irq = 1; 3208c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Legacy device tree found, " 3218c2ecf20Sopenharmony_ci "trying old style irq\n", __func__); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci ret = spu_map_interrupts_old(spu, spe); 3248c2ecf20Sopenharmony_ci if (ret) { 3258c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: could not map interrupts\n", 3268c2ecf20Sopenharmony_ci spu->name); 3278c2ecf20Sopenharmony_ci goto out_unmap; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci pr_debug("Using SPE %s %p %p %p %p %d\n", spu->name, 3328c2ecf20Sopenharmony_ci spu->local_store, spu->problem, spu->priv1, 3338c2ecf20Sopenharmony_ci spu->priv2, spu->number); 3348c2ecf20Sopenharmony_ci goto out; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ciout_unmap: 3378c2ecf20Sopenharmony_ci spu_unmap(spu); 3388c2ecf20Sopenharmony_ciout: 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int of_destroy_spu(struct spu *spu) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci spu_unmap(spu); 3458c2ecf20Sopenharmony_ci of_node_put(spu->devnode); 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void enable_spu_by_master_run(struct spu_context *ctx) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci ctx->ops->master_start(ctx); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void disable_spu_by_master_run(struct spu_context *ctx) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci ctx->ops->master_stop(ctx); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* Hardcoded affinity idxs for qs20 */ 3608c2ecf20Sopenharmony_ci#define QS20_SPES_PER_BE 8 3618c2ecf20Sopenharmony_cistatic int qs20_reg_idxs[QS20_SPES_PER_BE] = { 0, 2, 4, 6, 7, 5, 3, 1 }; 3628c2ecf20Sopenharmony_cistatic int qs20_reg_memory[QS20_SPES_PER_BE] = { 1, 1, 0, 0, 0, 0, 0, 0 }; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic struct spu *spu_lookup_reg(int node, u32 reg) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct spu *spu; 3678c2ecf20Sopenharmony_ci const u32 *spu_reg; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) { 3708c2ecf20Sopenharmony_ci spu_reg = of_get_property(spu_devnode(spu), "reg", NULL); 3718c2ecf20Sopenharmony_ci if (*spu_reg == reg) 3728c2ecf20Sopenharmony_ci return spu; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci return NULL; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic void init_affinity_qs20_harcoded(void) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci int node, i; 3808c2ecf20Sopenharmony_ci struct spu *last_spu, *spu; 3818c2ecf20Sopenharmony_ci u32 reg; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci for (node = 0; node < MAX_NUMNODES; node++) { 3848c2ecf20Sopenharmony_ci last_spu = NULL; 3858c2ecf20Sopenharmony_ci for (i = 0; i < QS20_SPES_PER_BE; i++) { 3868c2ecf20Sopenharmony_ci reg = qs20_reg_idxs[i]; 3878c2ecf20Sopenharmony_ci spu = spu_lookup_reg(node, reg); 3888c2ecf20Sopenharmony_ci if (!spu) 3898c2ecf20Sopenharmony_ci continue; 3908c2ecf20Sopenharmony_ci spu->has_mem_affinity = qs20_reg_memory[reg]; 3918c2ecf20Sopenharmony_ci if (last_spu) 3928c2ecf20Sopenharmony_ci list_add_tail(&spu->aff_list, 3938c2ecf20Sopenharmony_ci &last_spu->aff_list); 3948c2ecf20Sopenharmony_ci last_spu = spu; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int of_has_vicinity(void) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct device_node *dn; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci for_each_node_by_type(dn, "spe") { 4048c2ecf20Sopenharmony_ci if (of_find_property(dn, "vicinity", NULL)) { 4058c2ecf20Sopenharmony_ci of_node_put(dn); 4068c2ecf20Sopenharmony_ci return 1; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic struct spu *devnode_spu(int cbe, struct device_node *dn) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct spu *spu; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci list_for_each_entry(spu, &cbe_spu_info[cbe].spus, cbe_list) 4178c2ecf20Sopenharmony_ci if (spu_devnode(spu) == dn) 4188c2ecf20Sopenharmony_ci return spu; 4198c2ecf20Sopenharmony_ci return NULL; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic struct spu * 4238c2ecf20Sopenharmony_cineighbour_spu(int cbe, struct device_node *target, struct device_node *avoid) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct spu *spu; 4268c2ecf20Sopenharmony_ci struct device_node *spu_dn; 4278c2ecf20Sopenharmony_ci const phandle *vic_handles; 4288c2ecf20Sopenharmony_ci int lenp, i; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci list_for_each_entry(spu, &cbe_spu_info[cbe].spus, cbe_list) { 4318c2ecf20Sopenharmony_ci spu_dn = spu_devnode(spu); 4328c2ecf20Sopenharmony_ci if (spu_dn == avoid) 4338c2ecf20Sopenharmony_ci continue; 4348c2ecf20Sopenharmony_ci vic_handles = of_get_property(spu_dn, "vicinity", &lenp); 4358c2ecf20Sopenharmony_ci for (i=0; i < (lenp / sizeof(phandle)); i++) { 4368c2ecf20Sopenharmony_ci if (vic_handles[i] == target->phandle) 4378c2ecf20Sopenharmony_ci return spu; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci return NULL; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic void init_affinity_node(int cbe) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct spu *spu, *last_spu; 4468c2ecf20Sopenharmony_ci struct device_node *vic_dn, *last_spu_dn; 4478c2ecf20Sopenharmony_ci phandle avoid_ph; 4488c2ecf20Sopenharmony_ci const phandle *vic_handles; 4498c2ecf20Sopenharmony_ci int lenp, i, added; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci last_spu = list_first_entry(&cbe_spu_info[cbe].spus, struct spu, 4528c2ecf20Sopenharmony_ci cbe_list); 4538c2ecf20Sopenharmony_ci avoid_ph = 0; 4548c2ecf20Sopenharmony_ci for (added = 1; added < cbe_spu_info[cbe].n_spus; added++) { 4558c2ecf20Sopenharmony_ci last_spu_dn = spu_devnode(last_spu); 4568c2ecf20Sopenharmony_ci vic_handles = of_get_property(last_spu_dn, "vicinity", &lenp); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* 4598c2ecf20Sopenharmony_ci * Walk through each phandle in vicinity property of the spu 4608c2ecf20Sopenharmony_ci * (tipically two vicinity phandles per spe node) 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci for (i = 0; i < (lenp / sizeof(phandle)); i++) { 4638c2ecf20Sopenharmony_ci if (vic_handles[i] == avoid_ph) 4648c2ecf20Sopenharmony_ci continue; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci vic_dn = of_find_node_by_phandle(vic_handles[i]); 4678c2ecf20Sopenharmony_ci if (!vic_dn) 4688c2ecf20Sopenharmony_ci continue; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (of_node_name_eq(vic_dn, "spe") ) { 4718c2ecf20Sopenharmony_ci spu = devnode_spu(cbe, vic_dn); 4728c2ecf20Sopenharmony_ci avoid_ph = last_spu_dn->phandle; 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci /* 4758c2ecf20Sopenharmony_ci * "mic-tm" and "bif0" nodes do not have 4768c2ecf20Sopenharmony_ci * vicinity property. So we need to find the 4778c2ecf20Sopenharmony_ci * spe which has vic_dn as neighbour, but 4788c2ecf20Sopenharmony_ci * skipping the one we came from (last_spu_dn) 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci spu = neighbour_spu(cbe, vic_dn, last_spu_dn); 4818c2ecf20Sopenharmony_ci if (!spu) 4828c2ecf20Sopenharmony_ci continue; 4838c2ecf20Sopenharmony_ci if (of_node_name_eq(vic_dn, "mic-tm")) { 4848c2ecf20Sopenharmony_ci last_spu->has_mem_affinity = 1; 4858c2ecf20Sopenharmony_ci spu->has_mem_affinity = 1; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci avoid_ph = vic_dn->phandle; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci list_add_tail(&spu->aff_list, &last_spu->aff_list); 4918c2ecf20Sopenharmony_ci last_spu = spu; 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic void init_affinity_fw(void) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci int cbe; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci for (cbe = 0; cbe < MAX_NUMNODES; cbe++) 5028c2ecf20Sopenharmony_ci init_affinity_node(cbe); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int __init init_affinity(void) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci if (of_has_vicinity()) { 5088c2ecf20Sopenharmony_ci init_affinity_fw(); 5098c2ecf20Sopenharmony_ci } else { 5108c2ecf20Sopenharmony_ci if (of_machine_is_compatible("IBM,CPBW-1.0")) 5118c2ecf20Sopenharmony_ci init_affinity_qs20_harcoded(); 5128c2ecf20Sopenharmony_ci else 5138c2ecf20Sopenharmony_ci printk("No affinity configuration found\n"); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ciconst struct spu_management_ops spu_management_of_ops = { 5208c2ecf20Sopenharmony_ci .enumerate_spus = of_enumerate_spus, 5218c2ecf20Sopenharmony_ci .create_spu = of_create_spu, 5228c2ecf20Sopenharmony_ci .destroy_spu = of_destroy_spu, 5238c2ecf20Sopenharmony_ci .enable_spu = enable_spu_by_master_run, 5248c2ecf20Sopenharmony_ci .disable_spu = disable_spu_by_master_run, 5258c2ecf20Sopenharmony_ci .init_affinity = init_affinity, 5268c2ecf20Sopenharmony_ci}; 527