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