All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: <linuxppc-dev@ozlabs.org>
Subject: [PATCH 9/15] Add arch/powerpc driver for UIC, PPC4xx interrupt controller
Date: Mon,  5 Mar 2007 14:24:52 +1100 (EST)	[thread overview]
Message-ID: <20070305032452.9EF85DDF18@ozlabs.org> (raw)
In-Reply-To: <20070305032307.GB31417@localhost.localdomain>

This patch adds a driver to arch/powerpc/sysdev for the UIC, the
on-chip interrupt controller from IBM/AMCC 4xx chips.  It uses the new
irq host mapping infrastructure.

Signed-off-by: David Gibson <dwg@au1.ibm.com>
---

 arch/powerpc/sysdev/Makefile |    1 
 arch/powerpc/sysdev/uic.c    |  338 +++++++++++++++++++++++++++++++++++++++++++
 include/asm-powerpc/uic.h    |   23 ++
 3 files changed, 362 insertions(+)

Index: working-2.6/arch/powerpc/sysdev/uic.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ working-2.6/arch/powerpc/sysdev/uic.c	2007-02-21 15:54:39.000000000 +1100
@@ -0,0 +1,338 @@
+/*
+ * arch/powerpc/sysdev/uic.c
+ *
+ * IBM PowerPC 4xx Universal Interrupt Controller
+ *
+ * Copyright 2007 David Gibson <dwg@au1.ibm.com>, 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/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/sysdev.h>
+#include <linux/device.h>
+#include <linux/bootmem.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/dcr.h>
+
+#define NR_UIC_INTS	32
+
+#define UIC_SR		0x0
+#define UIC_ER		0x2
+#define UIC_CR		0x3
+#define UIC_PR		0x4
+#define UIC_TR		0x5
+#define UIC_MSR		0x6
+#define UIC_VR		0x7
+#define UIC_VCR		0x8
+
+#define uic_irq_to_hw(virq)	(irq_map[virq].hwirq)
+
+struct uic *primary_uic;
+
+struct uic {
+	int index;
+	int dcrbase;
+
+	spinlock_t lock;
+
+	/* The remapper for this UIC */
+	struct irq_host	*irqhost;
+
+	/* For secondary UICs, the cascade interrupt's irqaction */
+	struct irqaction cascade;
+
+	/* The device node of the interrupt controller */
+	struct device_node *of_node;
+};
+
+static void uic_unmask_irq(unsigned int virq)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	unsigned long flags;
+	u32 er;
+
+	spin_lock_irqsave(&uic->lock, flags);
+	er = mfdcr(uic->dcrbase + UIC_ER);
+	er |= 1 << (31 - src);
+	mtdcr(uic->dcrbase + UIC_ER, er);
+	spin_unlock_irqrestore(&uic->lock, flags);
+}
+
+static void uic_mask_irq(unsigned int virq)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	unsigned long flags;
+	u32 er;
+
+	spin_lock_irqsave(&uic->lock, flags);
+	er = mfdcr(uic->dcrbase + UIC_ER);
+	er &= ~(1 << (31 - src));
+	mtdcr(uic->dcrbase + UIC_ER, er);
+	spin_unlock_irqrestore(&uic->lock, flags);
+}
+
+static void uic_ack_irq(unsigned int virq)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	unsigned long flags;
+
+	spin_lock_irqsave(&uic->lock, flags);
+	mtdcr(uic->dcrbase + UIC_SR, 1 << (31-src));
+	spin_unlock_irqrestore(&uic->lock, flags);
+}
+
+static int uic_set_irq_type(unsigned int virq, unsigned int flow_type)
+{
+	struct uic *uic = get_irq_chip_data(virq);
+	unsigned int src = uic_irq_to_hw(virq);
+	struct irq_desc *desc = get_irq_desc(virq);
+	unsigned long flags;
+	int trigger, polarity;
+	u32 tr, pr, mask;
+
+	switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_NONE:
+		uic_mask_irq(virq);
+		return 0;
+
+	case IRQ_TYPE_EDGE_RISING:
+		trigger = 1; polarity = 1;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		trigger = 1; polarity = 0;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		trigger = 0; polarity = 1;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		trigger = 0; polarity = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mask = ~(1 << (31 - src));
+
+	spin_lock_irqsave(&uic->lock, flags);
+	tr = mfdcr(uic->dcrbase + UIC_TR);
+	pr = mfdcr(uic->dcrbase + UIC_PR);
+	tr = (tr & mask) | (trigger << (31-src));
+	pr = (pr & mask) | (polarity << (31-src));
+
+	mtdcr(uic->dcrbase + UIC_PR, pr);
+	mtdcr(uic->dcrbase + UIC_TR, tr);
+
+	desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
+	desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
+	if (trigger)
+		desc->status |= IRQ_LEVEL;
+
+	spin_unlock_irqrestore(&uic->lock, flags);
+
+	return 0;
+}
+
+static struct irq_chip uic_irq_chip = {
+	.typename	= " UIC  ",
+	.unmask		= uic_unmask_irq,
+	.mask		= uic_mask_irq,
+/* 	.mask_ack	= uic_mask_irq_and_ack, */
+	.ack		= uic_ack_irq,
+	.set_type	= uic_set_irq_type,
+};
+
+static int uic_host_match(struct irq_host *h, struct device_node *node)
+{
+	struct uic *uic = h->host_data;
+	return uic->of_node == node;
+}
+
+static int uic_host_map(struct irq_host *h, unsigned int virq,
+			irq_hw_number_t hw)
+{
+	struct uic *uic = h->host_data;
+
+	set_irq_chip_data(virq, uic);
+	/* Despite the name, handle_level_irq() works for both level
+	 * and edge irqs on UIC.  FIXME: check this is correct */
+	set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq);
+
+	/* Set default irq type */
+	set_irq_type(virq, IRQ_TYPE_NONE);
+
+	return 0;
+}
+
+static int uic_host_xlate(struct irq_host *h, struct device_node *ct,
+			  u32 *intspec, unsigned int intsize,
+			  irq_hw_number_t *out_hwirq, unsigned int *out_type)
+
+{
+	/* UIC intspecs must have 2 cells */
+	BUG_ON(intsize != 2);
+	*out_hwirq = intspec[0];
+	*out_type = intspec[1];
+	return 0;
+}
+
+static struct irq_host_ops uic_host_ops = {
+	.match	= uic_host_match,
+	.map	= uic_host_map,
+	.xlate	= uic_host_xlate,
+};
+
+irqreturn_t uic_cascade(int virq, void *data)
+{
+	struct uic *uic = data;
+	u32 msr;
+	int src;
+	int subvirq;
+
+	msr = mfdcr(uic->dcrbase + UIC_MSR);
+	src = 32 - ffs(msr);
+
+	subvirq = irq_linear_revmap(uic->irqhost, src);
+	generic_handle_irq(subvirq);
+
+	return IRQ_HANDLED;
+}
+
+static struct uic * __init uic_init_one(struct device_node *node)
+{
+	struct uic *uic;
+	const u32 *indexp, *dcrreg;
+	int len;
+
+	BUG_ON(! device_is_compatible(node, "ibm,uic"));
+
+	uic = alloc_bootmem(sizeof(*uic));
+	if (! uic)
+		return NULL; /* FIXME: panic? */
+
+	memset(uic, 0, sizeof(*uic));
+	uic->of_node = of_node_get(node);
+	indexp = get_property(node, "cell-index", &len);
+	if (!indexp || (len != sizeof(u32))) {
+		printk(KERN_ERR "uic: Device node %s has missing or invalid "
+		       "cell-index property\n", node->full_name);
+		return NULL;
+	}
+	uic->index = *indexp;
+
+	dcrreg = get_property(node, "dcr-reg", &len);
+	if (!dcrreg || (len != 2*sizeof(u32))) {
+		printk(KERN_ERR "uic: Device node %s has missing or invalid "
+		       "dcr-reg property\n", node->full_name);
+		return NULL;
+	}
+	uic->dcrbase = *dcrreg;
+
+	uic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, NR_UIC_INTS,
+				      &uic_host_ops, -1);
+	if (! uic->irqhost) {
+		of_node_put(node);
+		return NULL; /* FIXME: panic? */
+	}
+
+	uic->irqhost->host_data = uic;
+
+	/* Start with all interrupts disable and non-critical */
+	mtdcr(uic->dcrbase + UIC_ER, 0);
+	mtdcr(uic->dcrbase + UIC_CR, 0);
+
+	printk ("UIC%d (%d IRQ sources) at DCR 0x%x\n", uic->index,
+		NR_UIC_INTS, uic->dcrbase);
+
+	return uic;
+}
+
+void __init uic_init_tree(void)
+{
+	struct device_node *np;
+	struct uic *uic;
+	const u32 *interrupts;
+
+	/* First locate and initialize the top-level UIC */
+
+	np = of_find_compatible_node(NULL, NULL, "ibm,uic");
+	while (np) {
+		interrupts = get_property(np, "interrupts", NULL);
+		if (! interrupts)
+			break;
+
+		np = of_find_compatible_node(np, NULL, "ibm,uic");
+	}
+
+	BUG_ON(!np); /* uic_init_tree() assumes there's a UIC as the
+		      * top-level interrupt controller */
+	primary_uic = uic_init_one(np);
+	if (! primary_uic)
+		panic("Unable to initialize primary UIC %s\n", np->full_name);
+
+	irq_set_default_host(primary_uic->irqhost);
+	of_node_put(np);
+
+	/* The scan again for cascaded UICs */
+	np = of_find_compatible_node(NULL, NULL, "ibm,uic");
+	while (np) {
+		interrupts = get_property(np, "interrupts", NULL);
+		if (interrupts) {
+			/* Secondary UIC */
+			int cascade_virq;
+			int ret;
+
+			uic = uic_init_one(np);
+			if (! uic)
+				panic("Unable to initialize a secondary UIC %s\n",
+				      np->full_name);
+
+			cascade_virq = irq_of_parse_and_map(np, 0);
+
+			uic->cascade.handler = uic_cascade;
+			uic->cascade.name = "UIC cascade";
+			uic->cascade.dev_id = uic;
+
+			ret = setup_irq(cascade_virq, &uic->cascade);
+			if (ret)
+				printk(KERN_ERR "Failed to setup_irq(%d) for "
+				       "UIC%d cascade\n", cascade_virq,
+				       uic->index);
+
+			/* FIXME: setup critical cascade?? */
+		}
+
+		np = of_find_compatible_node(np, NULL, "ibm,uic");
+	}
+}
+
+/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */
+unsigned int uic_get_irq(void)
+{
+	u32 msr;
+	int src;
+
+	BUG_ON(! primary_uic);
+
+	msr = mfdcr(primary_uic->dcrbase + UIC_MSR);
+	src = 32 - ffs(msr);
+
+	return irq_linear_revmap(primary_uic->irqhost, src);
+}
Index: working-2.6/arch/powerpc/sysdev/Makefile
===================================================================
--- working-2.6.orig/arch/powerpc/sysdev/Makefile	2007-02-19 11:05:31.000000000 +1100
+++ working-2.6/arch/powerpc/sysdev/Makefile	2007-02-21 15:35:25.000000000 +1100
@@ -17,6 +17,7 @@ obj-$(CONFIG_QUICC_ENGINE)	+= qe_lib/
 ifeq ($(CONFIG_PPC_MERGE),y)
 obj-$(CONFIG_PPC_I8259)		+= i8259.o
 obj-$(CONFIG_PPC_83xx)		+= ipic.o
+obj-$(CONFIG_4xx)		+= uic.o
 endif
 
 # Temporary hack until we have migrated to asm-powerpc
Index: working-2.6/include/asm-powerpc/uic.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ working-2.6/include/asm-powerpc/uic.h	2007-02-21 15:35:25.000000000 +1100
@@ -0,0 +1,23 @@
+/*
+ * include/asm-powerpc/uic.h
+ *
+ * IBM PPC4xx UIC external definitions and structure.
+ *
+ * Maintainer: David Gibson <dwg@au1.ibm.com>
+ * Copyright 2007 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.
+ */
+#ifndef _ASM_POWERPC_UIC_H
+#define _ASM_POWERPC_UIC_H
+
+#ifdef __KERNEL__
+
+extern void __init uic_init_tree(void);
+extern unsigned int uic_get_irq(void);
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_UIC_H */

  parent reply	other threads:[~2007-03-05  3:24 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-03-05  3:23 [0/15] Ebony support, spin 3 David Gibson
2007-03-05  3:24 ` [PATCH 1/15] powerpc: Allow duplicate lmb_reserve() calls David Gibson
2007-03-05  3:24 ` [PATCH 2/15] Automatically lmb_reserve() initrd David Gibson
2007-04-18 11:43   ` Uytterhoeven, Geert
2007-03-05  3:24 ` [PATCH 3/15] Define FIXED_PORT flag for serial_core David Gibson
2007-03-05  3:24 ` [PATCH 4/15] Use resource_size_t for serial port IO addresses David Gibson
2007-03-05  3:24 ` [PATCH 11/15] zImage wrapper for Ebony David Gibson
2007-03-05 17:10   ` Mark A. Greer
2007-03-06  0:09     ` David Gibson
2007-03-07 19:47       ` Scott Wood
2007-03-08  0:24         ` David Gibson
2007-03-08  0:54           ` Mark A. Greer
2007-03-08  1:35             ` Josh Boyer
2007-03-09 17:50   ` Josh Boyer
2007-03-10  4:08     ` David Gibson
2007-03-05  3:24 ` [PATCH 10/15] Add device tree " David Gibson
2007-03-05 14:23   ` Josh Boyer
2007-03-06  0:04     ` David Gibson
2007-03-05  3:24 ` [PATCH 7/15] zImage: Cleanup and improve prep_kernel() David Gibson
2007-03-05  3:24 ` [PATCH 8/15] zImage: Cleanup and improve zImage entry point David Gibson
2007-03-15 22:35   ` Mark A. Greer
2007-03-16  0:14     ` David Gibson
2007-03-16  1:01       ` Mark A. Greer
2007-03-16  1:50         ` David Gibson
2007-03-16  3:36           ` Mark A. Greer
2007-03-20 20:20             ` Mark A. Greer
2007-03-15 23:02   ` Mark A. Greer
2007-03-16  0:18     ` David Gibson
2007-03-16  0:47       ` Mark A. Greer
2007-03-16  0:50         ` David Gibson
2007-03-16  3:45           ` Mark A. Greer
2007-03-16  4:02             ` David Gibson
2007-03-16 16:21           ` Scott Wood
2007-03-17  1:38             ` David Gibson
2007-03-17  3:15               ` Geoff Levand
2007-03-19 15:06               ` Scott Wood
2007-03-20  0:45                 ` David Gibson
2007-03-05  3:24 ` David Gibson [this message]
2007-03-05  3:24 ` [PATCH 5/15] Re-organize Kconfig code for 4xx in arch/powerpc David Gibson
2007-03-05  3:24 ` [PATCH 6/15] zImage: Add more flexible gunzip convenience functions David Gibson
2007-03-16 16:24   ` Geoff Levand
2007-03-17 12:59     ` David Gibson
2007-03-05  3:24 ` [PATCH 15/15] Add support for reset on Ebony David Gibson
2007-03-05  3:24 ` [PATCH 13/15] Port 44x MMU definitions to ARCH=powerpc David Gibson
2007-03-05  3:24 ` [PATCH 14/15] Early serial debug support for PPC44x David Gibson
2007-03-08 19:44   ` Josh Boyer
2007-03-09 17:42     ` Josh Boyer
2007-03-09 23:43       ` David Gibson
2007-03-05  3:24 ` [PATCH 12/15] Support for Ebony in arch/powerpc David Gibson
2007-03-05  8:02 ` Real time clock support " Zang Roy-r61911
2007-03-05 15:10   ` Kumar Gala
2007-03-05 17:04     ` Mark A. Greer
2007-03-05 13:57 ` [0/15] Ebony support, spin 3 Josh Boyer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070305032452.9EF85DDF18@ozlabs.org \
    --to=david@gibson.dropbear.id.au \
    --cc=linuxppc-dev@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.