We call the cache_hwirq_map() function with a linux IRQ number but it expects a HW irq number. This triggers a BUG on multic-chip setups in addition to not doing the right thing. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
		
			
				
	
	
		
			760 lines
		
	
	
	
		
			17 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			760 lines
		
	
	
	
		
			17 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright 2008-2011 IBM Corporation.
 | 
						|
 *
 | 
						|
 *  This program is free software; you can redistribute it and/or
 | 
						|
 *  modify it under the terms of the GNU General Public License
 | 
						|
 *  as published by the Free Software Foundation; either version
 | 
						|
 *  2 of the License, or (at your option) any later version.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/cpu.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/interrupt.h>
 | 
						|
#include <linux/irq.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/msi.h>
 | 
						|
#include <linux/of.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/smp.h>
 | 
						|
#include <linux/spinlock.h>
 | 
						|
#include <linux/types.h>
 | 
						|
 | 
						|
#include <asm/io.h>
 | 
						|
#include <asm/irq.h>
 | 
						|
#include <asm/xics.h>
 | 
						|
 | 
						|
#include "wsp.h"
 | 
						|
#include "ics.h"
 | 
						|
 | 
						|
 | 
						|
/* WSP ICS */
 | 
						|
 | 
						|
struct wsp_ics {
 | 
						|
	struct ics ics;
 | 
						|
	struct device_node *dn;
 | 
						|
	void __iomem *regs;
 | 
						|
	spinlock_t lock;
 | 
						|
	unsigned long *bitmap;
 | 
						|
	u32 chip_id;
 | 
						|
	u32 lsi_base;
 | 
						|
	u32 lsi_count;
 | 
						|
	u64 hwirq_start;
 | 
						|
	u64 count;
 | 
						|
#ifdef CONFIG_SMP
 | 
						|
	int *hwirq_cpu_map;
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
#define to_wsp_ics(ics)	container_of(ics, struct wsp_ics, ics)
 | 
						|
 | 
						|
#define INT_SRC_LAYER_BUID_REG(base)	((base) + 0x00)
 | 
						|
#define IODA_TBL_ADDR_REG(base)		((base) + 0x18)
 | 
						|
#define IODA_TBL_DATA_REG(base)		((base) + 0x20)
 | 
						|
#define XIVE_UPDATE_REG(base)		((base) + 0x28)
 | 
						|
#define ICS_INT_CAPS_REG(base)		((base) + 0x30)
 | 
						|
 | 
						|
#define TBL_AUTO_INCREMENT	((1UL << 63) | (1UL << 15))
 | 
						|
#define TBL_SELECT_XIST		(1UL << 48)
 | 
						|
#define TBL_SELECT_XIVT		(1UL << 49)
 | 
						|
 | 
						|
#define IODA_IRQ(irq)		((irq) & (0x7FFULL))	/* HRM 5.1.3.4 */
 | 
						|
 | 
						|
#define XIST_REQUIRED		0x8
 | 
						|
#define XIST_REJECTED		0x4
 | 
						|
#define XIST_PRESENTED		0x2
 | 
						|
#define XIST_PENDING		0x1
 | 
						|
 | 
						|
#define XIVE_SERVER_SHIFT	42
 | 
						|
#define XIVE_SERVER_MASK	0xFFFFULL
 | 
						|
#define XIVE_PRIORITY_MASK	0xFFULL
 | 
						|
#define XIVE_PRIORITY_SHIFT	32
 | 
						|
#define XIVE_WRITE_ENABLE	(1ULL << 63)
 | 
						|
 | 
						|
/*
 | 
						|
 * The docs refer to a 6 bit field called ChipID, which consists of a
 | 
						|
 * 3 bit NodeID and a 3 bit ChipID. On WSP the ChipID is always zero
 | 
						|
 * so we ignore it, and every where we use "chip id" in this code we
 | 
						|
 * mean the NodeID.
 | 
						|
 */
 | 
						|
#define WSP_ICS_CHIP_SHIFT		17
 | 
						|
 | 
						|
 | 
						|
static struct wsp_ics *ics_list;
 | 
						|
static int num_ics;
 | 
						|
 | 
						|
/* ICS Source controller accessors */
 | 
						|
 | 
						|
static u64 wsp_ics_get_xive(struct wsp_ics *ics, unsigned int irq)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	u64 xive;
 | 
						|
 | 
						|
	spin_lock_irqsave(&ics->lock, flags);
 | 
						|
	out_be64(IODA_TBL_ADDR_REG(ics->regs), TBL_SELECT_XIVT | IODA_IRQ(irq));
 | 
						|
	xive = in_be64(IODA_TBL_DATA_REG(ics->regs));
 | 
						|
	spin_unlock_irqrestore(&ics->lock, flags);
 | 
						|
 | 
						|
	return xive;
 | 
						|
}
 | 
						|
 | 
						|
static void wsp_ics_set_xive(struct wsp_ics *ics, unsigned int irq, u64 xive)
 | 
						|
{
 | 
						|
	xive &= ~XIVE_ADDR_MASK;
 | 
						|
	xive |= (irq & XIVE_ADDR_MASK);
 | 
						|
	xive |= XIVE_WRITE_ENABLE;
 | 
						|
 | 
						|
	out_be64(XIVE_UPDATE_REG(ics->regs), xive);
 | 
						|
}
 | 
						|
 | 
						|
static u64 xive_set_server(u64 xive, unsigned int server)
 | 
						|
{
 | 
						|
	u64 mask = ~(XIVE_SERVER_MASK << XIVE_SERVER_SHIFT);
 | 
						|
 | 
						|
	xive &= mask;
 | 
						|
	xive |= (server & XIVE_SERVER_MASK) << XIVE_SERVER_SHIFT;
 | 
						|
 | 
						|
	return xive;
 | 
						|
}
 | 
						|
 | 
						|
static u64 xive_set_priority(u64 xive, unsigned int priority)
 | 
						|
{
 | 
						|
	u64 mask = ~(XIVE_PRIORITY_MASK << XIVE_PRIORITY_SHIFT);
 | 
						|
 | 
						|
	xive &= mask;
 | 
						|
	xive |= (priority & XIVE_PRIORITY_MASK) << XIVE_PRIORITY_SHIFT;
 | 
						|
 | 
						|
	return xive;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifdef CONFIG_SMP
 | 
						|
/* Find logical CPUs within mask on a given chip and store result in ret */
 | 
						|
void cpus_on_chip(int chip_id, cpumask_t *mask, cpumask_t *ret)
 | 
						|
{
 | 
						|
	int cpu, chip;
 | 
						|
	struct device_node *cpu_dn, *dn;
 | 
						|
	const u32 *prop;
 | 
						|
 | 
						|
	cpumask_clear(ret);
 | 
						|
	for_each_cpu(cpu, mask) {
 | 
						|
		cpu_dn = of_get_cpu_node(cpu, NULL);
 | 
						|
		if (!cpu_dn)
 | 
						|
			continue;
 | 
						|
 | 
						|
		prop = of_get_property(cpu_dn, "at-node", NULL);
 | 
						|
		if (!prop) {
 | 
						|
			of_node_put(cpu_dn);
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		dn = of_find_node_by_phandle(*prop);
 | 
						|
		of_node_put(cpu_dn);
 | 
						|
 | 
						|
		chip = wsp_get_chip_id(dn);
 | 
						|
		if (chip == chip_id)
 | 
						|
			cpumask_set_cpu(cpu, ret);
 | 
						|
 | 
						|
		of_node_put(dn);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Store a suitable CPU to handle a hwirq in the ics->hwirq_cpu_map cache */
 | 
						|
static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
 | 
						|
			   const cpumask_t *affinity)
 | 
						|
{
 | 
						|
	cpumask_var_t avail, newmask;
 | 
						|
	int ret = -ENOMEM, cpu, cpu_rover = 0, target;
 | 
						|
	int index = hwirq - ics->hwirq_start;
 | 
						|
	unsigned int nodeid;
 | 
						|
 | 
						|
	BUG_ON(index < 0 || index >= ics->count);
 | 
						|
 | 
						|
	if (!ics->hwirq_cpu_map)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	if (!distribute_irqs) {
 | 
						|
		ics->hwirq_cpu_map[hwirq - ics->hwirq_start] = xics_default_server;
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Allocate needed CPU masks */
 | 
						|
	if (!alloc_cpumask_var(&avail, GFP_KERNEL))
 | 
						|
		goto ret;
 | 
						|
	if (!alloc_cpumask_var(&newmask, GFP_KERNEL))
 | 
						|
		goto freeavail;
 | 
						|
 | 
						|
	/* Find PBus attached to the source of this IRQ */
 | 
						|
	nodeid = (hwirq >> WSP_ICS_CHIP_SHIFT) & 0x3; /* 12:14 */
 | 
						|
 | 
						|
	/* Find CPUs that could handle this IRQ */
 | 
						|
	if (affinity)
 | 
						|
		cpumask_and(avail, cpu_online_mask, affinity);
 | 
						|
	else
 | 
						|
		cpumask_copy(avail, cpu_online_mask);
 | 
						|
 | 
						|
	/* Narrow selection down to logical CPUs on the same chip */
 | 
						|
	cpus_on_chip(nodeid, avail, newmask);
 | 
						|
 | 
						|
	/* Ensure we haven't narrowed it down to 0 */
 | 
						|
	if (unlikely(cpumask_empty(newmask))) {
 | 
						|
		if (unlikely(cpumask_empty(avail))) {
 | 
						|
			ret = -1;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		cpumask_copy(newmask, avail);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Choose a CPU out of those we narrowed it down to in round robin */
 | 
						|
	target = hwirq % cpumask_weight(newmask);
 | 
						|
	for_each_cpu(cpu, newmask) {
 | 
						|
		if (cpu_rover++ >= target) {
 | 
						|
			ics->hwirq_cpu_map[index] = get_hard_smp_processor_id(cpu);
 | 
						|
			ret = 0;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Shouldn't happen */
 | 
						|
	WARN_ON(1);
 | 
						|
 | 
						|
out:
 | 
						|
	free_cpumask_var(newmask);
 | 
						|
freeavail:
 | 
						|
	free_cpumask_var(avail);
 | 
						|
ret:
 | 
						|
	if (ret < 0) {
 | 
						|
		ics->hwirq_cpu_map[index] = cpumask_first(cpu_online_mask);
 | 
						|
		pr_warning("Error, falling hwirq 0x%x routing back to CPU %i\n",
 | 
						|
			   hwirq, ics->hwirq_cpu_map[index]);
 | 
						|
	}
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void alloc_irq_map(struct wsp_ics *ics)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	ics->hwirq_cpu_map = kmalloc(sizeof(int) * ics->count, GFP_KERNEL);
 | 
						|
	if (!ics->hwirq_cpu_map) {
 | 
						|
		pr_warning("Allocate hwirq_cpu_map failed, "
 | 
						|
			   "IRQ balancing disabled\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i=0; i < ics->count; i++)
 | 
						|
		ics->hwirq_cpu_map[i] = xics_default_server;
 | 
						|
}
 | 
						|
 | 
						|
static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
 | 
						|
{
 | 
						|
	int index = hwirq - ics->hwirq_start;
 | 
						|
 | 
						|
	BUG_ON(index < 0 || index >= ics->count);
 | 
						|
 | 
						|
	if (!ics->hwirq_cpu_map)
 | 
						|
		return xics_default_server;
 | 
						|
 | 
						|
	return ics->hwirq_cpu_map[index];
 | 
						|
}
 | 
						|
#else /* !CONFIG_SMP */
 | 
						|
static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
 | 
						|
			   const cpumask_t *affinity)
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
 | 
						|
{
 | 
						|
	return xics_default_server;
 | 
						|
}
 | 
						|
 | 
						|
static void alloc_irq_map(struct wsp_ics *ics) { }
 | 
						|
#endif
 | 
						|
 | 
						|
static void wsp_chip_unmask_irq(struct irq_data *d)
 | 
						|
{
 | 
						|
	unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
 | 
						|
	struct wsp_ics *ics;
 | 
						|
	int server;
 | 
						|
	u64 xive;
 | 
						|
 | 
						|
	if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
 | 
						|
		return;
 | 
						|
 | 
						|
	ics = d->chip_data;
 | 
						|
	if (WARN_ON(!ics))
 | 
						|
		return;
 | 
						|
 | 
						|
	server = get_irq_server(ics, hw_irq);
 | 
						|
 | 
						|
	xive = wsp_ics_get_xive(ics, hw_irq);
 | 
						|
	xive = xive_set_server(xive, server);
 | 
						|
	xive = xive_set_priority(xive, DEFAULT_PRIORITY);
 | 
						|
	wsp_ics_set_xive(ics, hw_irq, xive);
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int wsp_chip_startup(struct irq_data *d)
 | 
						|
{
 | 
						|
	/* unmask it */
 | 
						|
	wsp_chip_unmask_irq(d);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void wsp_mask_real_irq(unsigned int hw_irq, struct wsp_ics *ics)
 | 
						|
{
 | 
						|
	u64 xive;
 | 
						|
 | 
						|
	if (hw_irq == XICS_IPI)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (WARN_ON(!ics))
 | 
						|
		return;
 | 
						|
	xive = wsp_ics_get_xive(ics, hw_irq);
 | 
						|
	xive = xive_set_server(xive, xics_default_server);
 | 
						|
	xive = xive_set_priority(xive, LOWEST_PRIORITY);
 | 
						|
	wsp_ics_set_xive(ics, hw_irq, xive);
 | 
						|
}
 | 
						|
 | 
						|
static void wsp_chip_mask_irq(struct irq_data *d)
 | 
						|
{
 | 
						|
	unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
 | 
						|
	struct wsp_ics *ics = d->chip_data;
 | 
						|
 | 
						|
	if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
 | 
						|
		return;
 | 
						|
 | 
						|
	wsp_mask_real_irq(hw_irq, ics);
 | 
						|
}
 | 
						|
 | 
						|
static int wsp_chip_set_affinity(struct irq_data *d,
 | 
						|
				 const struct cpumask *cpumask, bool force)
 | 
						|
{
 | 
						|
	unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
 | 
						|
	struct wsp_ics *ics;
 | 
						|
	int ret;
 | 
						|
	u64 xive;
 | 
						|
 | 
						|
	if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	ics = d->chip_data;
 | 
						|
	if (WARN_ON(!ics))
 | 
						|
		return -1;
 | 
						|
	xive = wsp_ics_get_xive(ics, hw_irq);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * For the moment only implement delivery to all cpus or one cpu.
 | 
						|
	 * Get current irq_server for the given irq
 | 
						|
	 */
 | 
						|
	ret = cache_hwirq_map(ics, hw_irq, cpumask);
 | 
						|
	if (ret == -1) {
 | 
						|
		char cpulist[128];
 | 
						|
		cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
 | 
						|
		pr_warning("%s: No online cpus in the mask %s for irq %d\n",
 | 
						|
			   __func__, cpulist, d->irq);
 | 
						|
		return -1;
 | 
						|
	} else if (ret == -ENOMEM) {
 | 
						|
		pr_warning("%s: Out of memory\n", __func__);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	xive = xive_set_server(xive, get_irq_server(ics, hw_irq));
 | 
						|
	wsp_ics_set_xive(ics, hw_irq, xive);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct irq_chip wsp_irq_chip = {
 | 
						|
	.name = "WSP ICS",
 | 
						|
	.irq_startup		= wsp_chip_startup,
 | 
						|
	.irq_mask		= wsp_chip_mask_irq,
 | 
						|
	.irq_unmask		= wsp_chip_unmask_irq,
 | 
						|
	.irq_set_affinity	= wsp_chip_set_affinity
 | 
						|
};
 | 
						|
 | 
						|
static int wsp_ics_host_match(struct ics *ics, struct device_node *dn)
 | 
						|
{
 | 
						|
	/* All ICSs in the system implement a global irq number space,
 | 
						|
	 * so match against them all. */
 | 
						|
	return of_device_is_compatible(dn, "ibm,ppc-xics");
 | 
						|
}
 | 
						|
 | 
						|
static int wsp_ics_match_hwirq(struct wsp_ics *wsp_ics, unsigned int hwirq)
 | 
						|
{
 | 
						|
	if (hwirq >= wsp_ics->hwirq_start &&
 | 
						|
	    hwirq <  wsp_ics->hwirq_start + wsp_ics->count)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int wsp_ics_map(struct ics *ics, unsigned int virq)
 | 
						|
{
 | 
						|
	struct wsp_ics *wsp_ics = to_wsp_ics(ics);
 | 
						|
	unsigned int hw_irq = virq_to_hw(virq);
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
 | 
						|
		return -ENOENT;
 | 
						|
 | 
						|
	irq_set_chip_and_handler(virq, &wsp_irq_chip, handle_fasteoi_irq);
 | 
						|
 | 
						|
	irq_set_chip_data(virq, wsp_ics);
 | 
						|
 | 
						|
	spin_lock_irqsave(&wsp_ics->lock, flags);
 | 
						|
	bitmap_allocate_region(wsp_ics->bitmap, hw_irq - wsp_ics->hwirq_start, 0);
 | 
						|
	spin_unlock_irqrestore(&wsp_ics->lock, flags);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void wsp_ics_mask_unknown(struct ics *ics, unsigned long hw_irq)
 | 
						|
{
 | 
						|
	struct wsp_ics *wsp_ics = to_wsp_ics(ics);
 | 
						|
 | 
						|
	if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
 | 
						|
		return;
 | 
						|
 | 
						|
	pr_err("%s: IRQ %lu (real) is invalid, disabling it.\n", __func__, hw_irq);
 | 
						|
	wsp_mask_real_irq(hw_irq, wsp_ics);
 | 
						|
}
 | 
						|
 | 
						|
static long wsp_ics_get_server(struct ics *ics, unsigned long hw_irq)
 | 
						|
{
 | 
						|
	struct wsp_ics *wsp_ics = to_wsp_ics(ics);
 | 
						|
 | 
						|
	if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
 | 
						|
		return -ENOENT;
 | 
						|
 | 
						|
	return get_irq_server(wsp_ics, hw_irq);
 | 
						|
}
 | 
						|
 | 
						|
/* HW Number allocation API */
 | 
						|
 | 
						|
static struct wsp_ics *wsp_ics_find_dn_ics(struct device_node *dn)
 | 
						|
{
 | 
						|
	struct device_node *iparent;
 | 
						|
	int i;
 | 
						|
 | 
						|
	iparent = of_irq_find_parent(dn);
 | 
						|
	if (!iparent) {
 | 
						|
		pr_err("wsp_ics: Failed to find interrupt parent!\n");
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	for(i = 0; i < num_ics; i++) {
 | 
						|
		if(ics_list[i].dn == iparent)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (i >= num_ics) {
 | 
						|
		pr_err("wsp_ics: Unable to find parent bitmap!\n");
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return &ics_list[i];
 | 
						|
}
 | 
						|
 | 
						|
int wsp_ics_alloc_irq(struct device_node *dn, int num)
 | 
						|
{
 | 
						|
	struct wsp_ics *ics;
 | 
						|
	int order, offset;
 | 
						|
 | 
						|
	ics = wsp_ics_find_dn_ics(dn);
 | 
						|
	if (!ics)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	/* Fast, but overly strict if num isn't a power of two */
 | 
						|
	order = get_count_order(num);
 | 
						|
 | 
						|
	spin_lock_irq(&ics->lock);
 | 
						|
	offset = bitmap_find_free_region(ics->bitmap, ics->count, order);
 | 
						|
	spin_unlock_irq(&ics->lock);
 | 
						|
 | 
						|
	if (offset < 0)
 | 
						|
		return offset;
 | 
						|
 | 
						|
	return offset + ics->hwirq_start;
 | 
						|
}
 | 
						|
 | 
						|
void wsp_ics_free_irq(struct device_node *dn, unsigned int irq)
 | 
						|
{
 | 
						|
	struct wsp_ics *ics;
 | 
						|
 | 
						|
	ics = wsp_ics_find_dn_ics(dn);
 | 
						|
	if (WARN_ON(!ics))
 | 
						|
		return;
 | 
						|
 | 
						|
	spin_lock_irq(&ics->lock);
 | 
						|
	bitmap_release_region(ics->bitmap, irq, 0);
 | 
						|
	spin_unlock_irq(&ics->lock);
 | 
						|
}
 | 
						|
 | 
						|
/* Initialisation */
 | 
						|
 | 
						|
static int __init wsp_ics_bitmap_setup(struct wsp_ics *ics,
 | 
						|
				      struct device_node *dn)
 | 
						|
{
 | 
						|
	int len, i, j, size;
 | 
						|
	u32 start, count;
 | 
						|
	const u32 *p;
 | 
						|
 | 
						|
	size = BITS_TO_LONGS(ics->count) * sizeof(long);
 | 
						|
	ics->bitmap = kzalloc(size, GFP_KERNEL);
 | 
						|
	if (!ics->bitmap) {
 | 
						|
		pr_err("wsp_ics: ENOMEM allocating IRQ bitmap!\n");
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	spin_lock_init(&ics->lock);
 | 
						|
 | 
						|
	p = of_get_property(dn, "available-ranges", &len);
 | 
						|
	if (!p || !len) {
 | 
						|
		/* FIXME this should be a WARN() once mambo is updated */
 | 
						|
		pr_err("wsp_ics: No available-ranges defined for %s\n",
 | 
						|
			dn->full_name);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (len % (2 * sizeof(u32)) != 0) {
 | 
						|
		/* FIXME this should be a WARN() once mambo is updated */
 | 
						|
		pr_err("wsp_ics: Invalid available-ranges for %s\n",
 | 
						|
			dn->full_name);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	bitmap_fill(ics->bitmap, ics->count);
 | 
						|
 | 
						|
	for (i = 0; i < len / sizeof(u32); i += 2) {
 | 
						|
		start = of_read_number(p + i, 1);
 | 
						|
		count = of_read_number(p + i + 1, 1);
 | 
						|
 | 
						|
		pr_devel("%s: start: %d count: %d\n", __func__, start, count);
 | 
						|
 | 
						|
		if ((start + count) > (ics->hwirq_start + ics->count) ||
 | 
						|
		     start < ics->hwirq_start) {
 | 
						|
			pr_err("wsp_ics: Invalid range! -> %d to %d\n",
 | 
						|
					start, start + count);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		for (j = 0; j < count; j++)
 | 
						|
			bitmap_release_region(ics->bitmap,
 | 
						|
				(start + j) - ics->hwirq_start, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Ensure LSIs are not available for allocation */
 | 
						|
	bitmap_allocate_region(ics->bitmap, ics->lsi_base,
 | 
						|
			       get_count_order(ics->lsi_count));
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int __init wsp_ics_setup(struct wsp_ics *ics, struct device_node *dn)
 | 
						|
{
 | 
						|
	u32 lsi_buid, msi_buid, msi_base, msi_count;
 | 
						|
	void __iomem *regs;
 | 
						|
	const u32 *p;
 | 
						|
	int rc, len, i;
 | 
						|
	u64 caps, buid;
 | 
						|
 | 
						|
	p = of_get_property(dn, "interrupt-ranges", &len);
 | 
						|
	if (!p || len < (2 * sizeof(u32))) {
 | 
						|
		pr_err("wsp_ics: No/bad interrupt-ranges found on %s\n",
 | 
						|
			dn->full_name);
 | 
						|
		return -ENOENT;
 | 
						|
	}
 | 
						|
 | 
						|
	if (len > (2 * sizeof(u32))) {
 | 
						|
		pr_err("wsp_ics: Multiple ics ranges not supported.\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	regs = of_iomap(dn, 0);
 | 
						|
	if (!regs) {
 | 
						|
		pr_err("wsp_ics: of_iomap(%s) failed\n", dn->full_name);
 | 
						|
		return -ENXIO;
 | 
						|
	}
 | 
						|
 | 
						|
	ics->hwirq_start = of_read_number(p, 1);
 | 
						|
	ics->count = of_read_number(p + 1, 1);
 | 
						|
	ics->regs = regs;
 | 
						|
 | 
						|
	ics->chip_id = wsp_get_chip_id(dn);
 | 
						|
	if (WARN_ON(ics->chip_id < 0))
 | 
						|
		ics->chip_id = 0;
 | 
						|
 | 
						|
	/* Get some informations about the critter */
 | 
						|
	caps = in_be64(ICS_INT_CAPS_REG(ics->regs));
 | 
						|
	buid = in_be64(INT_SRC_LAYER_BUID_REG(ics->regs));
 | 
						|
	ics->lsi_count = caps >> 56;
 | 
						|
	msi_count = (caps >> 44) & 0x7ff;
 | 
						|
 | 
						|
	/* Note: LSI BUID is 9 bits, but really only 3 are BUID and the
 | 
						|
	 * rest is mixed in the interrupt number. We store the whole
 | 
						|
	 * thing though
 | 
						|
	 */
 | 
						|
	lsi_buid = (buid >> 48) & 0x1ff;
 | 
						|
	ics->lsi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | lsi_buid << 5;
 | 
						|
	msi_buid = (buid >> 37) & 0x7;
 | 
						|
	msi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | msi_buid << 11;
 | 
						|
 | 
						|
	pr_info("wsp_ics: Found %s\n", dn->full_name);
 | 
						|
	pr_info("wsp_ics:    irq range : 0x%06llx..0x%06llx\n",
 | 
						|
		ics->hwirq_start, ics->hwirq_start + ics->count - 1);
 | 
						|
	pr_info("wsp_ics:    %4d LSIs : 0x%06x..0x%06x\n",
 | 
						|
		ics->lsi_count, ics->lsi_base,
 | 
						|
		ics->lsi_base + ics->lsi_count - 1);
 | 
						|
	pr_info("wsp_ics:    %4d MSIs : 0x%06x..0x%06x\n",
 | 
						|
		msi_count, msi_base,
 | 
						|
		msi_base + msi_count - 1);
 | 
						|
 | 
						|
	/* Let's check the HW config is sane */
 | 
						|
	if (ics->lsi_base < ics->hwirq_start ||
 | 
						|
	    (ics->lsi_base + ics->lsi_count) > (ics->hwirq_start + ics->count))
 | 
						|
		pr_warning("wsp_ics: WARNING ! LSIs out of interrupt-ranges !\n");
 | 
						|
	if (msi_base < ics->hwirq_start ||
 | 
						|
	    (msi_base + msi_count) > (ics->hwirq_start + ics->count))
 | 
						|
		pr_warning("wsp_ics: WARNING ! MSIs out of interrupt-ranges !\n");
 | 
						|
 | 
						|
	/* We don't check for overlap between LSI and MSI, which will happen
 | 
						|
	 * if we use the same BUID, I'm not sure yet how legit that is.
 | 
						|
	 */
 | 
						|
 | 
						|
	rc = wsp_ics_bitmap_setup(ics, dn);
 | 
						|
	if (rc) {
 | 
						|
		iounmap(regs);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
 | 
						|
	ics->dn = of_node_get(dn);
 | 
						|
	alloc_irq_map(ics);
 | 
						|
 | 
						|
	for(i = 0; i < ics->count; i++)
 | 
						|
		wsp_mask_real_irq(ics->hwirq_start + i, ics);
 | 
						|
 | 
						|
	ics->ics.map = wsp_ics_map;
 | 
						|
	ics->ics.mask_unknown = wsp_ics_mask_unknown;
 | 
						|
	ics->ics.get_server = wsp_ics_get_server;
 | 
						|
	ics->ics.host_match = wsp_ics_host_match;
 | 
						|
 | 
						|
	xics_register_ics(&ics->ics);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void __init wsp_ics_set_default_server(void)
 | 
						|
{
 | 
						|
	struct device_node *np;
 | 
						|
	u32 hwid;
 | 
						|
 | 
						|
	/* Find the server number for the boot cpu. */
 | 
						|
	np = of_get_cpu_node(boot_cpuid, NULL);
 | 
						|
	BUG_ON(!np);
 | 
						|
 | 
						|
	hwid = get_hard_smp_processor_id(boot_cpuid);
 | 
						|
 | 
						|
	pr_info("wsp_ics: default server is %#x, CPU %s\n", hwid, np->full_name);
 | 
						|
	xics_default_server = hwid;
 | 
						|
 | 
						|
	of_node_put(np);
 | 
						|
}
 | 
						|
 | 
						|
static int __init wsp_ics_init(void)
 | 
						|
{
 | 
						|
	struct device_node *dn;
 | 
						|
	struct wsp_ics *ics;
 | 
						|
	int rc, found;
 | 
						|
 | 
						|
	wsp_ics_set_default_server();
 | 
						|
 | 
						|
	found = 0;
 | 
						|
	for_each_compatible_node(dn, NULL, "ibm,ppc-xics")
 | 
						|
		found++;
 | 
						|
 | 
						|
	if (found == 0) {
 | 
						|
		pr_err("wsp_ics: No ICS's found!\n");
 | 
						|
		return -ENODEV;
 | 
						|
	}
 | 
						|
 | 
						|
	ics_list = kmalloc(sizeof(*ics) * found, GFP_KERNEL);
 | 
						|
	if (!ics_list) {
 | 
						|
		pr_err("wsp_ics: No memory for structs.\n");
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	num_ics = 0;
 | 
						|
	ics = ics_list;
 | 
						|
	for_each_compatible_node(dn, NULL, "ibm,wsp-xics") {
 | 
						|
		rc = wsp_ics_setup(ics, dn);
 | 
						|
		if (rc == 0) {
 | 
						|
			ics++;
 | 
						|
			num_ics++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (found != num_ics) {
 | 
						|
		pr_err("wsp_ics: Failed setting up %d ICS's\n",
 | 
						|
			found - num_ics);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void __init wsp_init_irq(void)
 | 
						|
{
 | 
						|
	wsp_ics_init();
 | 
						|
	xics_init();
 | 
						|
 | 
						|
	/* We need to patch our irq chip's EOI to point to the right ICP */
 | 
						|
	wsp_irq_chip.irq_eoi = icp_ops->eoi;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_PCI_MSI
 | 
						|
static void wsp_ics_msi_unmask_irq(struct irq_data *d)
 | 
						|
{
 | 
						|
	wsp_chip_unmask_irq(d);
 | 
						|
	unmask_msi_irq(d);
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int wsp_ics_msi_startup(struct irq_data *d)
 | 
						|
{
 | 
						|
	wsp_ics_msi_unmask_irq(d);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void wsp_ics_msi_mask_irq(struct irq_data *d)
 | 
						|
{
 | 
						|
	mask_msi_irq(d);
 | 
						|
	wsp_chip_mask_irq(d);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * we do it this way because we reassinge default EOI handling in
 | 
						|
 * irq_init() above
 | 
						|
 */
 | 
						|
static void wsp_ics_eoi(struct irq_data *data)
 | 
						|
{
 | 
						|
	wsp_irq_chip.irq_eoi(data);
 | 
						|
}
 | 
						|
 | 
						|
static struct irq_chip wsp_ics_msi = {
 | 
						|
	.name = "WSP ICS MSI",
 | 
						|
	.irq_startup = wsp_ics_msi_startup,
 | 
						|
	.irq_mask = wsp_ics_msi_mask_irq,
 | 
						|
	.irq_unmask = wsp_ics_msi_unmask_irq,
 | 
						|
	.irq_eoi = wsp_ics_eoi,
 | 
						|
	.irq_set_affinity = wsp_chip_set_affinity
 | 
						|
};
 | 
						|
 | 
						|
void wsp_ics_set_msi_chip(unsigned int irq)
 | 
						|
{
 | 
						|
	irq_set_chip(irq, &wsp_ics_msi);
 | 
						|
}
 | 
						|
 | 
						|
void wsp_ics_set_std_chip(unsigned int irq)
 | 
						|
{
 | 
						|
	irq_set_chip(irq, &wsp_irq_chip);
 | 
						|
}
 | 
						|
#endif /* CONFIG_PCI_MSI */
 |