Blackfin: add support for the on-chip MAC status interrupts

This patch provides infrastructure for MAC Wake-On-Lan and PHYINT use in
phylib.  New Interrupts added:

IRQ_MAC_PHYINT   /* PHY_INT Interrupt */
IRQ_MAC_MMCINT   /* MMC Counter Interrupt */
IRQ_MAC_RXFSINT  /* RX Frame-Status Interrupt */
IRQ_MAC_TXFSINT  /* TX Frame-Status Interrupt */
IRQ_MAC_WAKEDET  /* Wake-Up Interrupt */
IRQ_MAC_RXDMAERR /* RX DMA Direction Error Interrupt */
IRQ_MAC_TXDMAERR /* TX DMA Direction Error Interrupt */
IRQ_MAC_STMDONE  /* Station Mgt. Transfer Done Interrupt */

On BF537/6 the implementation is not straight forward since there are now
two chained chained_handlers.  A cleaner approach would have been to add
latter IRQs to the demux of IRQ_GENERIC_ERROR.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
diff --git a/arch/blackfin/mach-bf518/include/mach/irq.h b/arch/blackfin/mach-bf518/include/mach/irq.h
index 52edc84..435e76e 100644
--- a/arch/blackfin/mach-bf518/include/mach/irq.h
+++ b/arch/blackfin/mach-bf518/include/mach/irq.h
@@ -151,7 +151,16 @@
 
 #define GPIO_IRQ_BASE	IRQ_PF0
 
-#define NR_MACH_IRQS	(IRQ_PH15 + 1)
+#define IRQ_MAC_PHYINT		119 /* PHY_INT Interrupt */
+#define IRQ_MAC_MMCINT		120 /* MMC Counter Interrupt */
+#define IRQ_MAC_RXFSINT		121 /* RX Frame-Status Interrupt */
+#define IRQ_MAC_TXFSINT		122 /* TX Frame-Status Interrupt */
+#define IRQ_MAC_WAKEDET		123 /* Wake-Up Interrupt */
+#define IRQ_MAC_RXDMAERR	124 /* RX DMA Direction Error Interrupt */
+#define IRQ_MAC_TXDMAERR	125 /* TX DMA Direction Error Interrupt */
+#define IRQ_MAC_STMDONE		126 /* Station Mgt. Transfer Done Interrupt */
+
+#define NR_MACH_IRQS	(IRQ_MAC_STMDONE + 1)
 #define NR_IRQS		(NR_MACH_IRQS + NR_SPARE_IRQS)
 
 #define IVG7            7
diff --git a/arch/blackfin/mach-bf527/include/mach/irq.h b/arch/blackfin/mach-bf527/include/mach/irq.h
index 17604b4..704d925 100644
--- a/arch/blackfin/mach-bf527/include/mach/irq.h
+++ b/arch/blackfin/mach-bf527/include/mach/irq.h
@@ -151,7 +151,16 @@
 
 #define GPIO_IRQ_BASE	IRQ_PF0
 
-#define NR_MACH_IRQS	(IRQ_PH15 + 1)
+#define IRQ_MAC_PHYINT		119 /* PHY_INT Interrupt */
+#define IRQ_MAC_MMCINT		120 /* MMC Counter Interrupt */
+#define IRQ_MAC_RXFSINT		121 /* RX Frame-Status Interrupt */
+#define IRQ_MAC_TXFSINT		122 /* TX Frame-Status Interrupt */
+#define IRQ_MAC_WAKEDET		123 /* Wake-Up Interrupt */
+#define IRQ_MAC_RXDMAERR	124 /* RX DMA Direction Error Interrupt */
+#define IRQ_MAC_TXDMAERR	125 /* TX DMA Direction Error Interrupt */
+#define IRQ_MAC_STMDONE		126 /* Station Mgt. Transfer Done Interrupt */
+
+#define NR_MACH_IRQS	(IRQ_MAC_STMDONE + 1)
 #define NR_IRQS		(NR_MACH_IRQS + NR_SPARE_IRQS)
 
 #define IVG7            7
diff --git a/arch/blackfin/mach-bf537/include/mach/irq.h b/arch/blackfin/mach-bf537/include/mach/irq.h
index 9b2cbca..789a4f2 100644
--- a/arch/blackfin/mach-bf537/include/mach/irq.h
+++ b/arch/blackfin/mach-bf537/include/mach/irq.h
@@ -134,7 +134,16 @@
 
 #define GPIO_IRQ_BASE	IRQ_PF0
 
-#define NR_MACH_IRQS	(IRQ_PH15 + 1)
+#define IRQ_MAC_PHYINT		98 /* PHY_INT Interrupt */
+#define IRQ_MAC_MMCINT		99 /* MMC Counter Interrupt */
+#define IRQ_MAC_RXFSINT		100 /* RX Frame-Status Interrupt */
+#define IRQ_MAC_TXFSINT		101 /* TX Frame-Status Interrupt */
+#define IRQ_MAC_WAKEDET		102 /* Wake-Up Interrupt */
+#define IRQ_MAC_RXDMAERR	103 /* RX DMA Direction Error Interrupt */
+#define IRQ_MAC_TXDMAERR	104 /* TX DMA Direction Error Interrupt */
+#define IRQ_MAC_STMDONE		105 /* Station Mgt. Transfer Done Interrupt */
+
+#define NR_MACH_IRQS	(IRQ_MAC_STMDONE + 1)
 #define NR_IRQS		(NR_MACH_IRQS + NR_SPARE_IRQS)
 
 #define IVG7            7
diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c
index ebf8860..11c05f3 100644
--- a/arch/blackfin/mach-common/ints-priority.c
+++ b/arch/blackfin/mach-common/ints-priority.c
@@ -325,7 +325,6 @@
 static void bfin_generic_error_mask_irq(unsigned int irq)
 {
 	error_int_mask &= ~(1L << (irq - IRQ_PPI_ERROR));
-
 	if (!error_int_mask)
 		bfin_internal_mask_irq(IRQ_GENERIC_ERROR);
 }
@@ -416,6 +415,127 @@
 }
 #endif				/* BF537_GENERIC_ERROR_INT_DEMUX */
 
+#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)
+static int mac_stat_int_mask;
+
+static void bfin_mac_status_ack_irq(unsigned int irq)
+{
+	switch (irq) {
+	case IRQ_MAC_MMCINT:
+		bfin_write_EMAC_MMC_TIRQS(
+			bfin_read_EMAC_MMC_TIRQE() &
+			bfin_read_EMAC_MMC_TIRQS());
+		bfin_write_EMAC_MMC_RIRQS(
+			bfin_read_EMAC_MMC_RIRQE() &
+			bfin_read_EMAC_MMC_RIRQS());
+		break;
+	case IRQ_MAC_RXFSINT:
+		bfin_write_EMAC_RX_STKY(
+			bfin_read_EMAC_RX_IRQE() &
+			bfin_read_EMAC_RX_STKY());
+		break;
+	case IRQ_MAC_TXFSINT:
+		bfin_write_EMAC_TX_STKY(
+			bfin_read_EMAC_TX_IRQE() &
+			bfin_read_EMAC_TX_STKY());
+		break;
+	case IRQ_MAC_WAKEDET:
+		 bfin_write_EMAC_WKUP_CTL(
+			bfin_read_EMAC_WKUP_CTL() | MPKS | RWKS);
+		break;
+	default:
+		/* These bits are W1C */
+		bfin_write_EMAC_SYSTAT(1L << (irq - IRQ_MAC_PHYINT));
+		break;
+	}
+}
+
+static void bfin_mac_status_mask_irq(unsigned int irq)
+{
+	mac_stat_int_mask &= ~(1L << (irq - IRQ_MAC_PHYINT));
+#ifdef BF537_GENERIC_ERROR_INT_DEMUX
+	switch (irq) {
+	case IRQ_MAC_PHYINT:
+		bfin_write_EMAC_SYSCTL(bfin_read_EMAC_SYSCTL() & ~PHYIE);
+		break;
+	default:
+		break;
+	}
+#else
+	if (!mac_stat_int_mask)
+		bfin_internal_mask_irq(IRQ_MAC_ERROR);
+#endif
+	bfin_mac_status_ack_irq(irq);
+}
+
+static void bfin_mac_status_unmask_irq(unsigned int irq)
+{
+#ifdef BF537_GENERIC_ERROR_INT_DEMUX
+	switch (irq) {
+	case IRQ_MAC_PHYINT:
+		bfin_write_EMAC_SYSCTL(bfin_read_EMAC_SYSCTL() | PHYIE);
+		break;
+	default:
+		break;
+	}
+#else
+	if (!mac_stat_int_mask)
+		bfin_internal_unmask_irq(IRQ_MAC_ERROR);
+#endif
+	mac_stat_int_mask |= 1L << (irq - IRQ_MAC_PHYINT);
+}
+
+#ifdef CONFIG_PM
+int bfin_mac_status_set_wake(unsigned int irq, unsigned int state)
+{
+#ifdef BF537_GENERIC_ERROR_INT_DEMUX
+	return bfin_internal_set_wake(IRQ_GENERIC_ERROR, state);
+#else
+	return bfin_internal_set_wake(IRQ_MAC_ERROR, state);
+#endif
+}
+#endif
+
+static struct irq_chip bfin_mac_status_irqchip = {
+	.name = "MACST",
+	.ack = bfin_ack_noop,
+	.mask_ack = bfin_mac_status_mask_irq,
+	.mask = bfin_mac_status_mask_irq,
+	.unmask = bfin_mac_status_unmask_irq,
+#ifdef CONFIG_PM
+	.set_wake = bfin_mac_status_set_wake,
+#endif
+};
+
+static void bfin_demux_mac_status_irq(unsigned int int_err_irq,
+				 struct irq_desc *inta_desc)
+{
+	int i, irq = 0;
+	u32 status = bfin_read_EMAC_SYSTAT();
+
+	for (i = 0; i < (IRQ_MAC_STMDONE - IRQ_MAC_PHYINT); i++)
+		if (status & (1L << i)) {
+			irq = IRQ_MAC_PHYINT + i;
+			break;
+		}
+
+	if (irq) {
+		if (mac_stat_int_mask & (1L << (irq - IRQ_MAC_PHYINT))) {
+			bfin_handle_irq(irq);
+		} else {
+			bfin_mac_status_ack_irq(irq);
+			pr_debug("IRQ %d:"
+				 " MASKED MAC ERROR INTERRUPT ASSERTED\n",
+				 irq);
+		}
+	} else
+		printk(KERN_ERR
+		       "%s : %s : LINE %d :\nIRQ ?: MAC ERROR"
+		       " INTERRUPT ASSERTED BUT NO SOURCE FOUND\n",
+		       __func__, __FILE__, __LINE__);
+}
+#endif
+
 static inline void bfin_set_irq_handler(unsigned irq, irq_flow_handler_t handle)
 {
 #ifdef CONFIG_IPIPE
@@ -1070,7 +1190,11 @@
 			set_irq_chained_handler(irq, bfin_demux_error_irq);
 			break;
 #endif
-
+#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)
+		case IRQ_MAC_ERROR:
+			set_irq_chained_handler(irq, bfin_demux_mac_status_irq);
+			break;
+#endif
 #ifdef CONFIG_SMP
 		case IRQ_SUPPLE_0:
 		case IRQ_SUPPLE_1:
@@ -1111,14 +1235,22 @@
 	for (irq = IRQ_PPI_ERROR; irq <= IRQ_UART1_ERROR; irq++)
 		set_irq_chip_and_handler(irq, &bfin_generic_error_irqchip,
 					 handle_level_irq);
+#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)
+	set_irq_chained_handler(IRQ_MAC_ERROR, bfin_demux_mac_status_irq);
+#endif
 #endif
 
+#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)
+	for (irq = IRQ_MAC_PHYINT; irq <= IRQ_MAC_STMDONE; irq++)
+		set_irq_chip_and_handler(irq, &bfin_mac_status_irqchip,
+					 handle_level_irq);
+#endif
 	/* if configured as edge, then will be changed to do_edge_IRQ */
-	for (irq = GPIO_IRQ_BASE; irq < NR_MACH_IRQS; irq++)
+	for (irq = GPIO_IRQ_BASE;
+		irq < (GPIO_IRQ_BASE + MAX_BLACKFIN_GPIOS); irq++)
 		set_irq_chip_and_handler(irq, &bfin_gpio_irqchip,
 					 handle_level_irq);
 
-
 	bfin_write_IMASK(0);
 	CSYNC();
 	ilat = bfin_read_ILAT();