summary refs log tree commit diff
path: root/arch/mips/lantiq
diff options
context:
space:
mode:
authorJohn Crispin <blogic@openwrt.org>2013-01-19 08:54:27 +0000
committerJohn Crispin <blogic@openwrt.org>2013-02-17 00:15:18 +0100
commit26365625947fb7d6501065272a1fd59460c0f4ed (patch)
tree5966c84966c51d1c45a6f0896ceb04e2980fed53 /arch/mips/lantiq
parentbae696a267d81ea268f4de1e396b8c82154f22ed (diff)
downloadlinux-26365625947fb7d6501065272a1fd59460c0f4ed.tar.gz
MIPS: lantiq: rework external irq code
This code makes the irqs used by the EIU loadable from the DT. Additionally we
add a helper that allows the pinctrl layer to map external irqs to real irq
numbers.

Signed-off-by: John Crispin <blogic@openwrt.org>
Patchwork: http://patchwork.linux-mips.org/patch/4818/
Diffstat (limited to 'arch/mips/lantiq')
-rw-r--r--arch/mips/lantiq/irq.c105
1 files changed, 73 insertions, 32 deletions
diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c
index a7935bf0fecb..51194875f158 100644
--- a/arch/mips/lantiq/irq.c
+++ b/arch/mips/lantiq/irq.c
@@ -33,17 +33,10 @@
 /* register definitions - external irqs */
 #define LTQ_EIU_EXIN_C		0x0000
 #define LTQ_EIU_EXIN_INIC	0x0004
+#define LTQ_EIU_EXIN_INC	0x0008
 #define LTQ_EIU_EXIN_INEN	0x000C
 
-/* irq numbers used by the external interrupt unit (EIU) */
-#define LTQ_EIU_IR0		(INT_NUM_IM4_IRL0 + 30)
-#define LTQ_EIU_IR1		(INT_NUM_IM3_IRL0 + 31)
-#define LTQ_EIU_IR2		(INT_NUM_IM1_IRL0 + 26)
-#define LTQ_EIU_IR3		INT_NUM_IM1_IRL0
-#define LTQ_EIU_IR4		(INT_NUM_IM1_IRL0 + 1)
-#define LTQ_EIU_IR5		(INT_NUM_IM1_IRL0 + 2)
-#define LTQ_EIU_IR6		(INT_NUM_IM2_IRL0 + 30)
-#define XWAY_EXIN_COUNT		3
+/* number of external interrupts */
 #define MAX_EIU			6
 
 /* the performance counter */
@@ -72,20 +65,19 @@
 int gic_present;
 #endif
 
-static unsigned short ltq_eiu_irq[MAX_EIU] = {
-	LTQ_EIU_IR0,
-	LTQ_EIU_IR1,
-	LTQ_EIU_IR2,
-	LTQ_EIU_IR3,
-	LTQ_EIU_IR4,
-	LTQ_EIU_IR5,
-};
-
 static int exin_avail;
+static struct resource ltq_eiu_irq[MAX_EIU];
 static void __iomem *ltq_icu_membase[MAX_IM];
 static void __iomem *ltq_eiu_membase;
 static struct irq_domain *ltq_domain;
 
+int ltq_eiu_get_irq(int exin)
+{
+	if (exin < exin_avail)
+		return ltq_eiu_irq[exin].start;
+	return -1;
+}
+
 void ltq_disable_irq(struct irq_data *d)
 {
 	u32 ier = LTQ_ICU_IM0_IER;
@@ -128,19 +120,65 @@ void ltq_enable_irq(struct irq_data *d)
 	ltq_icu_w32(im, ltq_icu_r32(im, ier) | BIT(offset), ier);
 }
 
+static int ltq_eiu_settype(struct irq_data *d, unsigned int type)
+{
+	int i;
+
+	for (i = 0; i < MAX_EIU; i++) {
+		if (d->hwirq == ltq_eiu_irq[i].start) {
+			int val = 0;
+			int edge = 0;
+
+			switch (type) {
+			case IRQF_TRIGGER_NONE:
+				break;
+			case IRQF_TRIGGER_RISING:
+				val = 1;
+				edge = 1;
+				break;
+			case IRQF_TRIGGER_FALLING:
+				val = 2;
+				edge = 1;
+				break;
+			case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
+				val = 3;
+				edge = 1;
+				break;
+			case IRQF_TRIGGER_HIGH:
+				val = 5;
+				break;
+			case IRQF_TRIGGER_LOW:
+				val = 6;
+				break;
+			default:
+				pr_err("invalid type %d for irq %ld\n",
+					type, d->hwirq);
+				return -EINVAL;
+			}
+
+			if (edge)
+				irq_set_handler(d->hwirq, handle_edge_irq);
+
+			ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
+				(val << (i * 4)), LTQ_EIU_EXIN_C);
+		}
+	}
+
+	return 0;
+}
+
 static unsigned int ltq_startup_eiu_irq(struct irq_data *d)
 {
 	int i;
 
 	ltq_enable_irq(d);
 	for (i = 0; i < MAX_EIU; i++) {
-		if (d->hwirq == ltq_eiu_irq[i]) {
-			/* low level - we should really handle set_type */
-			ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
-				(0x6 << (i * 4)), LTQ_EIU_EXIN_C);
+		if (d->hwirq == ltq_eiu_irq[i].start) {
+			/* by default we are low level triggered */
+			ltq_eiu_settype(d, IRQF_TRIGGER_LOW);
 			/* clear all pending */
-			ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~BIT(i),
-				LTQ_EIU_EXIN_INIC);
+			ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INC) & ~BIT(i),
+				LTQ_EIU_EXIN_INC);
 			/* enable */
 			ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i),
 				LTQ_EIU_EXIN_INEN);
@@ -157,7 +195,7 @@ static void ltq_shutdown_eiu_irq(struct irq_data *d)
 
 	ltq_disable_irq(d);
 	for (i = 0; i < MAX_EIU; i++) {
-		if (d->hwirq == ltq_eiu_irq[i]) {
+		if (d->hwirq == ltq_eiu_irq[i].start) {
 			/* disable */
 			ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i),
 				LTQ_EIU_EXIN_INEN);
@@ -186,6 +224,7 @@ static struct irq_chip ltq_eiu_type = {
 	.irq_ack = ltq_ack_irq,
 	.irq_mask = ltq_disable_irq,
 	.irq_mask_ack = ltq_mask_and_ack_irq,
+	.irq_set_type = ltq_eiu_settype,
 };
 
 static void ltq_hw_irqdispatch(int module)
@@ -301,7 +340,7 @@ static int icu_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
 		return 0;
 
 	for (i = 0; i < exin_avail; i++)
-		if (hw == ltq_eiu_irq[i])
+		if (hw == ltq_eiu_irq[i].start)
 			chip = &ltq_eiu_type;
 
 	irq_set_chip_and_handler(hw, chip, handle_level_irq);
@@ -323,7 +362,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
 {
 	struct device_node *eiu_node;
 	struct resource res;
-	int i;
+	int i, ret;
 
 	for (i = 0; i < MAX_IM; i++) {
 		if (of_address_to_resource(node, i, &res))
@@ -340,17 +379,19 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent)
 	}
 
 	/* the external interrupts are optional and xway only */
-	eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu");
+	eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu-xway");
 	if (eiu_node && !of_address_to_resource(eiu_node, 0, &res)) {
 		/* find out how many external irq sources we have */
-		const __be32 *count = of_get_property(node,
-							"lantiq,count",	NULL);
+		exin_avail = of_irq_count(eiu_node);
 
-		if (count)
-			exin_avail = *count;
 		if (exin_avail > MAX_EIU)
 			exin_avail = MAX_EIU;
 
+		ret = of_irq_to_resource_table(eiu_node,
+						ltq_eiu_irq, exin_avail);
+		if (ret != exin_avail)
+			panic("failed to load external irq resources\n");
+
 		if (request_mem_region(res.start, resource_size(&res),
 							res.name) < 0)
 			pr_err("Failed to request eiu memory");