diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-21 18:55:10 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-21 18:55:10 -0700 | 
| commit | 5375871d432ae9fc581014ac117b96aaee3cd0c7 (patch) | |
| tree | be98e8255b0f927fb920fb532a598b93fa140dbe /arch/powerpc/sysdev/ge/ge_pic.c | |
| parent | b57cb7231b2ce52d3dda14a7b417ae125fb2eb97 (diff) | |
| parent | dfbc2d75c1bd47c3186fa91f1655ea2f3825b0ec (diff) | |
| download | olio-linux-3.10-5375871d432ae9fc581014ac117b96aaee3cd0c7.tar.xz olio-linux-3.10-5375871d432ae9fc581014ac117b96aaee3cd0c7.zip  | |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Pull powerpc merge from Benjamin Herrenschmidt:
 "Here's the powerpc batch for this merge window.  It is going to be a
  bit more nasty than usual as in touching things outside of
  arch/powerpc mostly due to the big iSeriesectomy :-) We finally got
  rid of the bugger (legacy iSeries support) which was a PITA to
  maintain and that nobody really used anymore.
  Here are some of the highlights:
   - Legacy iSeries is gone.  Thanks Stephen ! There's still some bits
     and pieces remaining if you do a grep -ir series arch/powerpc but
     they are harmless and will be removed in the next few weeks
     hopefully.
   - The 'fadump' functionality (Firmware Assisted Dump) replaces the
     previous (equivalent) "pHyp assisted dump"...  it's a rewrite of a
     mechanism to get the hypervisor to do crash dumps on pSeries, the
     new implementation hopefully being much more reliable.  Thanks
     Mahesh Salgaonkar.
   - The "EEH" code (pSeries PCI error handling & recovery) got a big
     spring cleaning, motivated by the need to be able to implement a
     new backend for it on top of some new different type of firwmare.
     The work isn't complete yet, but a good chunk of the cleanups is
     there.  Note that this adds a field to struct device_node which is
     not very nice and which Grant objects to.  I will have a patch soon
     that moves that to a powerpc private data structure (hopefully
     before rc1) and we'll improve things further later on (hopefully
     getting rid of the need for that pointer completely).  Thanks Gavin
     Shan.
   - I dug into our exception & interrupt handling code to improve the
     way we do lazy interrupt handling (and make it work properly with
     "edge" triggered interrupt sources), and while at it found & fixed
     a wagon of issues in those areas, including adding support for page
     fault retry & fatal signals on page faults.
   - Your usual random batch of small fixes & updates, including a bunch
     of new embedded boards, both Freescale and APM based ones, etc..."
I fixed up some conflicts with the generalized irq-domain changes from
Grant Likely, hopefully correctly.
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (141 commits)
  powerpc/ps3: Do not adjust the wrapper load address
  powerpc: Remove the rest of the legacy iSeries include files
  powerpc: Remove the remaining CONFIG_PPC_ISERIES pieces
  init: Remove CONFIG_PPC_ISERIES
  powerpc: Remove FW_FEATURE ISERIES from arch code
  tty/hvc_vio: FW_FEATURE_ISERIES is no longer selectable
  powerpc/spufs: Fix double unlocks
  powerpc/5200: convert mpc5200 to use of_platform_populate()
  powerpc/mpc5200: add options to mpc5200_defconfig
  powerpc/mpc52xx: add a4m072 board support
  powerpc/mpc5200: update mpc5200_defconfig to fit for charon board
  Documentation/powerpc/mpc52xx.txt: Checkpatch cleanup
  powerpc/44x: Add additional device support for APM821xx SoC and Bluestone board
  powerpc/44x: Add support PCI-E for APM821xx SoC and Bluestone board
  MAINTAINERS: Update PowerPC 4xx tree
  powerpc/44x: The bug fixed support for APM821xx SoC and Bluestone board
  powerpc: document the FSL MPIC message register binding
  powerpc: add support for MPIC message register API
  powerpc/fsl: Added aliased MSIIR register address to MSI node in dts
  powerpc/85xx: mpc8548cds - add 36-bit dts
  ...
Diffstat (limited to 'arch/powerpc/sysdev/ge/ge_pic.c')
| -rw-r--r-- | arch/powerpc/sysdev/ge/ge_pic.c | 251 | 
1 files changed, 251 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/ge/ge_pic.c b/arch/powerpc/sysdev/ge/ge_pic.c new file mode 100644 index 00000000000..2bcb78bb3a1 --- /dev/null +++ b/arch/powerpc/sysdev/ge/ge_pic.c @@ -0,0 +1,251 @@ +/* + * Interrupt handling for GE FPGA based PIC + * + * Author: Martyn Welch <martyn.welch@ge.com> + * + * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2.  This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/irq.h> + +#include "ge_pic.h" + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0) +#else +#define DBG(fmt...) do { } while (0) +#endif + +#define GEF_PIC_NUM_IRQS	32 + +/* Interrupt Controller Interface Registers */ +#define GEF_PIC_INTR_STATUS	0x0000 + +#define GEF_PIC_INTR_MASK(cpu)	(0x0010 + (0x4 * cpu)) +#define GEF_PIC_CPU0_INTR_MASK	GEF_PIC_INTR_MASK(0) +#define GEF_PIC_CPU1_INTR_MASK	GEF_PIC_INTR_MASK(1) + +#define GEF_PIC_MCP_MASK(cpu)	(0x0018 + (0x4 * cpu)) +#define GEF_PIC_CPU0_MCP_MASK	GEF_PIC_MCP_MASK(0) +#define GEF_PIC_CPU1_MCP_MASK	GEF_PIC_MCP_MASK(1) + + +static DEFINE_RAW_SPINLOCK(gef_pic_lock); + +static void __iomem *gef_pic_irq_reg_base; +static struct irq_domain *gef_pic_irq_host; +static int gef_pic_cascade_irq; + +/* + * Interrupt Controller Handling + * + * The interrupt controller handles interrupts for most on board interrupts, + * apart from PCI interrupts. For example on SBC610: + * + * 17:31 RO Reserved + * 16    RO PCI Express Doorbell 3 Status + * 15    RO PCI Express Doorbell 2 Status + * 14    RO PCI Express Doorbell 1 Status + * 13    RO PCI Express Doorbell 0 Status + * 12    RO Real Time Clock Interrupt Status + * 11    RO Temperature Interrupt Status + * 10    RO Temperature Critical Interrupt Status + * 9     RO Ethernet PHY1 Interrupt Status + * 8     RO Ethernet PHY3 Interrupt Status + * 7     RO PEX8548 Interrupt Status + * 6     RO Reserved + * 5     RO Watchdog 0 Interrupt Status + * 4     RO Watchdog 1 Interrupt Status + * 3     RO AXIS Message FIFO A Interrupt Status + * 2     RO AXIS Message FIFO B Interrupt Status + * 1     RO AXIS Message FIFO C Interrupt Status + * 0     RO AXIS Message FIFO D Interrupt Status + * + * Interrupts can be forwarded to one of two output lines. Nothing + * clever is done, so if the masks are incorrectly set, a single input + * interrupt could generate interrupts on both output lines! + * + * The dual lines are there to allow the chained interrupts to be easily + * passed into two different cores. We currently do not use this functionality + * in this driver. + * + * Controller can also be configured to generate Machine checks (MCP), again on + * two lines, to be attached to two different cores. It is suggested that these + * should be masked out. + */ + +void gef_pic_cascade(unsigned int irq, struct irq_desc *desc) +{ +	struct irq_chip *chip = irq_desc_get_chip(desc); +	unsigned int cascade_irq; + +	/* +	 * See if we actually have an interrupt, call generic handling code if +	 * we do. +	 */ +	cascade_irq = gef_pic_get_irq(); + +	if (cascade_irq != NO_IRQ) +		generic_handle_irq(cascade_irq); + +	chip->irq_eoi(&desc->irq_data); +} + +static void gef_pic_mask(struct irq_data *d) +{ +	unsigned long flags; +	unsigned int hwirq = irqd_to_hwirq(d); +	u32 mask; + +	raw_spin_lock_irqsave(&gef_pic_lock, flags); +	mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); +	mask &= ~(1 << hwirq); +	out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); +	raw_spin_unlock_irqrestore(&gef_pic_lock, flags); +} + +static void gef_pic_mask_ack(struct irq_data *d) +{ +	/* Don't think we actually have to do anything to ack an interrupt, +	 * we just need to clear down the devices interrupt and it will go away +	 */ +	gef_pic_mask(d); +} + +static void gef_pic_unmask(struct irq_data *d) +{ +	unsigned long flags; +	unsigned int hwirq = irqd_to_hwirq(d); +	u32 mask; + +	raw_spin_lock_irqsave(&gef_pic_lock, flags); +	mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); +	mask |= (1 << hwirq); +	out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); +	raw_spin_unlock_irqrestore(&gef_pic_lock, flags); +} + +static struct irq_chip gef_pic_chip = { +	.name		= "gefp", +	.irq_mask	= gef_pic_mask, +	.irq_mask_ack	= gef_pic_mask_ack, +	.irq_unmask	= gef_pic_unmask, +}; + + +/* When an interrupt is being configured, this call allows some flexibilty + * in deciding which irq_chip structure is used + */ +static int gef_pic_host_map(struct irq_domain *h, unsigned int virq, +			  irq_hw_number_t hwirq) +{ +	/* All interrupts are LEVEL sensitive */ +	irq_set_status_flags(virq, IRQ_LEVEL); +	irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq); + +	return 0; +} + +static int gef_pic_host_xlate(struct irq_domain *h, struct device_node *ct, +			    const u32 *intspec, unsigned int intsize, +			    irq_hw_number_t *out_hwirq, unsigned int *out_flags) +{ + +	*out_hwirq = intspec[0]; +	if (intsize > 1) +		*out_flags = intspec[1]; +	else +		*out_flags = IRQ_TYPE_LEVEL_HIGH; + +	return 0; +} + +static const struct irq_domain_ops gef_pic_host_ops = { +	.map	= gef_pic_host_map, +	.xlate	= gef_pic_host_xlate, +}; + + +/* + * Initialisation of PIC, this should be called in BSP + */ +void __init gef_pic_init(struct device_node *np) +{ +	unsigned long flags; + +	/* Map the devices registers into memory */ +	gef_pic_irq_reg_base = of_iomap(np, 0); + +	raw_spin_lock_irqsave(&gef_pic_lock, flags); + +	/* Initialise everything as masked. */ +	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0); +	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0); + +	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0); +	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0); + +	raw_spin_unlock_irqrestore(&gef_pic_lock, flags); + +	/* Map controller */ +	gef_pic_cascade_irq = irq_of_parse_and_map(np, 0); +	if (gef_pic_cascade_irq == NO_IRQ) { +		printk(KERN_ERR "SBC610: failed to map cascade interrupt"); +		return; +	} + +	/* Setup an irq_domain structure */ +	gef_pic_irq_host = irq_domain_add_linear(np, GEF_PIC_NUM_IRQS, +					  &gef_pic_host_ops, NULL); +	if (gef_pic_irq_host == NULL) +		return; + +	/* Chain with parent controller */ +	irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade); +} + +/* + * This is called when we receive an interrupt with apparently comes from this + * chip - check, returning the highest interrupt generated or return NO_IRQ + */ +unsigned int gef_pic_get_irq(void) +{ +	u32 cause, mask, active; +	unsigned int virq = NO_IRQ; +	int hwirq; + +	cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS); + +	mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); + +	active = cause & mask; + +	if (active) { +		for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) { +			if (active & (0x1 << hwirq)) +				break; +		} +		virq = irq_linear_revmap(gef_pic_irq_host, +			(irq_hw_number_t)hwirq); +	} + +	return virq; +} +  |