Path: blob/master/arch/powerpc/platforms/cell/spu_manage.c
10818 views
/*1* spu management operations for of based platforms2*3* (C) Copyright IBM Deutschland Entwicklung GmbH 20054* Copyright 2006 Sony Corp.5* (C) Copyright 2007 TOSHIBA CORPORATION6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; version 2 of the License.10*11* This program is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License along17* with this program; if not, write to the Free Software Foundation, Inc.,18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.19*/2021#include <linux/interrupt.h>22#include <linux/list.h>23#include <linux/module.h>24#include <linux/ptrace.h>25#include <linux/wait.h>26#include <linux/mm.h>27#include <linux/io.h>28#include <linux/mutex.h>29#include <linux/device.h>3031#include <asm/spu.h>32#include <asm/spu_priv1.h>33#include <asm/firmware.h>34#include <asm/prom.h>3536#include "spufs/spufs.h"37#include "interrupt.h"3839struct device_node *spu_devnode(struct spu *spu)40{41return spu->devnode;42}4344EXPORT_SYMBOL_GPL(spu_devnode);4546static u64 __init find_spu_unit_number(struct device_node *spe)47{48const unsigned int *prop;49int proplen;5051/* new device trees should provide the physical-id attribute */52prop = of_get_property(spe, "physical-id", &proplen);53if (proplen == 4)54return (u64)*prop;5556/* celleb device tree provides the unit-id */57prop = of_get_property(spe, "unit-id", &proplen);58if (proplen == 4)59return (u64)*prop;6061/* legacy device trees provide the id in the reg attribute */62prop = of_get_property(spe, "reg", &proplen);63if (proplen == 4)64return (u64)*prop;6566return 0;67}6869static void spu_unmap(struct spu *spu)70{71if (!firmware_has_feature(FW_FEATURE_LPAR))72iounmap(spu->priv1);73iounmap(spu->priv2);74iounmap(spu->problem);75iounmap((__force u8 __iomem *)spu->local_store);76}7778static int __init spu_map_interrupts_old(struct spu *spu,79struct device_node *np)80{81unsigned int isrc;82const u32 *tmp;83int nid;8485/* Get the interrupt source unit from the device-tree */86tmp = of_get_property(np, "isrc", NULL);87if (!tmp)88return -ENODEV;89isrc = tmp[0];9091tmp = of_get_property(np->parent->parent, "node-id", NULL);92if (!tmp) {93printk(KERN_WARNING "%s: can't find node-id\n", __func__);94nid = spu->node;95} else96nid = tmp[0];9798/* Add the node number */99isrc |= nid << IIC_IRQ_NODE_SHIFT;100101/* Now map interrupts of all 3 classes */102spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc);103spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc);104spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc);105106/* Right now, we only fail if class 2 failed */107return spu->irqs[2] == NO_IRQ ? -EINVAL : 0;108}109110static void __iomem * __init spu_map_prop_old(struct spu *spu,111struct device_node *n,112const char *name)113{114const struct address_prop {115unsigned long address;116unsigned int len;117} __attribute__((packed)) *prop;118int proplen;119120prop = of_get_property(n, name, &proplen);121if (prop == NULL || proplen != sizeof (struct address_prop))122return NULL;123124return ioremap(prop->address, prop->len);125}126127static int __init spu_map_device_old(struct spu *spu)128{129struct device_node *node = spu->devnode;130const char *prop;131int ret;132133ret = -ENODEV;134spu->name = of_get_property(node, "name", NULL);135if (!spu->name)136goto out;137138prop = of_get_property(node, "local-store", NULL);139if (!prop)140goto out;141spu->local_store_phys = *(unsigned long *)prop;142143/* we use local store as ram, not io memory */144spu->local_store = (void __force *)145spu_map_prop_old(spu, node, "local-store");146if (!spu->local_store)147goto out;148149prop = of_get_property(node, "problem", NULL);150if (!prop)151goto out_unmap;152spu->problem_phys = *(unsigned long *)prop;153154spu->problem = spu_map_prop_old(spu, node, "problem");155if (!spu->problem)156goto out_unmap;157158spu->priv2 = spu_map_prop_old(spu, node, "priv2");159if (!spu->priv2)160goto out_unmap;161162if (!firmware_has_feature(FW_FEATURE_LPAR)) {163spu->priv1 = spu_map_prop_old(spu, node, "priv1");164if (!spu->priv1)165goto out_unmap;166}167168ret = 0;169goto out;170171out_unmap:172spu_unmap(spu);173out:174return ret;175}176177static int __init spu_map_interrupts(struct spu *spu, struct device_node *np)178{179struct of_irq oirq;180int ret;181int i;182183for (i=0; i < 3; i++) {184ret = of_irq_map_one(np, i, &oirq);185if (ret) {186pr_debug("spu_new: failed to get irq %d\n", i);187goto err;188}189ret = -EINVAL;190pr_debug(" irq %d no 0x%x on %s\n", i, oirq.specifier[0],191oirq.controller->full_name);192spu->irqs[i] = irq_create_of_mapping(oirq.controller,193oirq.specifier, oirq.size);194if (spu->irqs[i] == NO_IRQ) {195pr_debug("spu_new: failed to map it !\n");196goto err;197}198}199return 0;200201err:202pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier,203spu->name);204for (; i >= 0; i--) {205if (spu->irqs[i] != NO_IRQ)206irq_dispose_mapping(spu->irqs[i]);207}208return ret;209}210211static int spu_map_resource(struct spu *spu, int nr,212void __iomem** virt, unsigned long *phys)213{214struct device_node *np = spu->devnode;215struct resource resource = { };216unsigned long len;217int ret;218219ret = of_address_to_resource(np, nr, &resource);220if (ret)221return ret;222if (phys)223*phys = resource.start;224len = resource.end - resource.start + 1;225*virt = ioremap(resource.start, len);226if (!*virt)227return -EINVAL;228return 0;229}230231static int __init spu_map_device(struct spu *spu)232{233struct device_node *np = spu->devnode;234int ret = -ENODEV;235236spu->name = of_get_property(np, "name", NULL);237if (!spu->name)238goto out;239240ret = spu_map_resource(spu, 0, (void __iomem**)&spu->local_store,241&spu->local_store_phys);242if (ret) {243pr_debug("spu_new: failed to map %s resource 0\n",244np->full_name);245goto out;246}247ret = spu_map_resource(spu, 1, (void __iomem**)&spu->problem,248&spu->problem_phys);249if (ret) {250pr_debug("spu_new: failed to map %s resource 1\n",251np->full_name);252goto out_unmap;253}254ret = spu_map_resource(spu, 2, (void __iomem**)&spu->priv2, NULL);255if (ret) {256pr_debug("spu_new: failed to map %s resource 2\n",257np->full_name);258goto out_unmap;259}260if (!firmware_has_feature(FW_FEATURE_LPAR))261ret = spu_map_resource(spu, 3,262(void __iomem**)&spu->priv1, NULL);263if (ret) {264pr_debug("spu_new: failed to map %s resource 3\n",265np->full_name);266goto out_unmap;267}268pr_debug("spu_new: %s maps:\n", np->full_name);269pr_debug(" local store : 0x%016lx -> 0x%p\n",270spu->local_store_phys, spu->local_store);271pr_debug(" problem state : 0x%016lx -> 0x%p\n",272spu->problem_phys, spu->problem);273pr_debug(" priv2 : 0x%p\n", spu->priv2);274pr_debug(" priv1 : 0x%p\n", spu->priv1);275276return 0;277278out_unmap:279spu_unmap(spu);280out:281pr_debug("failed to map spe %s: %d\n", spu->name, ret);282return ret;283}284285static int __init of_enumerate_spus(int (*fn)(void *data))286{287int ret;288struct device_node *node;289unsigned int n = 0;290291ret = -ENODEV;292for (node = of_find_node_by_type(NULL, "spe");293node; node = of_find_node_by_type(node, "spe")) {294ret = fn(node);295if (ret) {296printk(KERN_WARNING "%s: Error initializing %s\n",297__func__, node->name);298break;299}300n++;301}302return ret ? ret : n;303}304305static int __init of_create_spu(struct spu *spu, void *data)306{307int ret;308struct device_node *spe = (struct device_node *)data;309static int legacy_map = 0, legacy_irq = 0;310311spu->devnode = of_node_get(spe);312spu->spe_id = find_spu_unit_number(spe);313314spu->node = of_node_to_nid(spe);315if (spu->node >= MAX_NUMNODES) {316printk(KERN_WARNING "SPE %s on node %d ignored,"317" node number too big\n", spe->full_name, spu->node);318printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n");319ret = -ENODEV;320goto out;321}322323ret = spu_map_device(spu);324if (ret) {325if (!legacy_map) {326legacy_map = 1;327printk(KERN_WARNING "%s: Legacy device tree found, "328"trying to map old style\n", __func__);329}330ret = spu_map_device_old(spu);331if (ret) {332printk(KERN_ERR "Unable to map %s\n",333spu->name);334goto out;335}336}337338ret = spu_map_interrupts(spu, spe);339if (ret) {340if (!legacy_irq) {341legacy_irq = 1;342printk(KERN_WARNING "%s: Legacy device tree found, "343"trying old style irq\n", __func__);344}345ret = spu_map_interrupts_old(spu, spe);346if (ret) {347printk(KERN_ERR "%s: could not map interrupts\n",348spu->name);349goto out_unmap;350}351}352353pr_debug("Using SPE %s %p %p %p %p %d\n", spu->name,354spu->local_store, spu->problem, spu->priv1,355spu->priv2, spu->number);356goto out;357358out_unmap:359spu_unmap(spu);360out:361return ret;362}363364static int of_destroy_spu(struct spu *spu)365{366spu_unmap(spu);367of_node_put(spu->devnode);368return 0;369}370371static void enable_spu_by_master_run(struct spu_context *ctx)372{373ctx->ops->master_start(ctx);374}375376static void disable_spu_by_master_run(struct spu_context *ctx)377{378ctx->ops->master_stop(ctx);379}380381/* Hardcoded affinity idxs for qs20 */382#define QS20_SPES_PER_BE 8383static int qs20_reg_idxs[QS20_SPES_PER_BE] = { 0, 2, 4, 6, 7, 5, 3, 1 };384static int qs20_reg_memory[QS20_SPES_PER_BE] = { 1, 1, 0, 0, 0, 0, 0, 0 };385386static struct spu *spu_lookup_reg(int node, u32 reg)387{388struct spu *spu;389const u32 *spu_reg;390391list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) {392spu_reg = of_get_property(spu_devnode(spu), "reg", NULL);393if (*spu_reg == reg)394return spu;395}396return NULL;397}398399static void init_affinity_qs20_harcoded(void)400{401int node, i;402struct spu *last_spu, *spu;403u32 reg;404405for (node = 0; node < MAX_NUMNODES; node++) {406last_spu = NULL;407for (i = 0; i < QS20_SPES_PER_BE; i++) {408reg = qs20_reg_idxs[i];409spu = spu_lookup_reg(node, reg);410if (!spu)411continue;412spu->has_mem_affinity = qs20_reg_memory[reg];413if (last_spu)414list_add_tail(&spu->aff_list,415&last_spu->aff_list);416last_spu = spu;417}418}419}420421static int of_has_vicinity(void)422{423struct device_node *dn;424425for_each_node_by_type(dn, "spe") {426if (of_find_property(dn, "vicinity", NULL)) {427of_node_put(dn);428return 1;429}430}431return 0;432}433434static struct spu *devnode_spu(int cbe, struct device_node *dn)435{436struct spu *spu;437438list_for_each_entry(spu, &cbe_spu_info[cbe].spus, cbe_list)439if (spu_devnode(spu) == dn)440return spu;441return NULL;442}443444static struct spu *445neighbour_spu(int cbe, struct device_node *target, struct device_node *avoid)446{447struct spu *spu;448struct device_node *spu_dn;449const phandle *vic_handles;450int lenp, i;451452list_for_each_entry(spu, &cbe_spu_info[cbe].spus, cbe_list) {453spu_dn = spu_devnode(spu);454if (spu_dn == avoid)455continue;456vic_handles = of_get_property(spu_dn, "vicinity", &lenp);457for (i=0; i < (lenp / sizeof(phandle)); i++) {458if (vic_handles[i] == target->phandle)459return spu;460}461}462return NULL;463}464465static void init_affinity_node(int cbe)466{467struct spu *spu, *last_spu;468struct device_node *vic_dn, *last_spu_dn;469phandle avoid_ph;470const phandle *vic_handles;471const char *name;472int lenp, i, added;473474last_spu = list_first_entry(&cbe_spu_info[cbe].spus, struct spu,475cbe_list);476avoid_ph = 0;477for (added = 1; added < cbe_spu_info[cbe].n_spus; added++) {478last_spu_dn = spu_devnode(last_spu);479vic_handles = of_get_property(last_spu_dn, "vicinity", &lenp);480481/*482* Walk through each phandle in vicinity property of the spu483* (tipically two vicinity phandles per spe node)484*/485for (i = 0; i < (lenp / sizeof(phandle)); i++) {486if (vic_handles[i] == avoid_ph)487continue;488489vic_dn = of_find_node_by_phandle(vic_handles[i]);490if (!vic_dn)491continue;492493/* a neighbour might be spe, mic-tm, or bif0 */494name = of_get_property(vic_dn, "name", NULL);495if (!name)496continue;497498if (strcmp(name, "spe") == 0) {499spu = devnode_spu(cbe, vic_dn);500avoid_ph = last_spu_dn->phandle;501} else {502/*503* "mic-tm" and "bif0" nodes do not have504* vicinity property. So we need to find the505* spe which has vic_dn as neighbour, but506* skipping the one we came from (last_spu_dn)507*/508spu = neighbour_spu(cbe, vic_dn, last_spu_dn);509if (!spu)510continue;511if (!strcmp(name, "mic-tm")) {512last_spu->has_mem_affinity = 1;513spu->has_mem_affinity = 1;514}515avoid_ph = vic_dn->phandle;516}517518list_add_tail(&spu->aff_list, &last_spu->aff_list);519last_spu = spu;520break;521}522}523}524525static void init_affinity_fw(void)526{527int cbe;528529for (cbe = 0; cbe < MAX_NUMNODES; cbe++)530init_affinity_node(cbe);531}532533static int __init init_affinity(void)534{535if (of_has_vicinity()) {536init_affinity_fw();537} else {538long root = of_get_flat_dt_root();539if (of_flat_dt_is_compatible(root, "IBM,CPBW-1.0"))540init_affinity_qs20_harcoded();541else542printk("No affinity configuration found\n");543}544545return 0;546}547548const struct spu_management_ops spu_management_of_ops = {549.enumerate_spus = of_enumerate_spus,550.create_spu = of_create_spu,551.destroy_spu = of_destroy_spu,552.enable_spu = enable_spu_by_master_run,553.disable_spu = disable_spu_by_master_run,554.init_affinity = init_affinity,555};556557558