18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Interrupt handling for GE FPGA based PIC
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author: Martyn Welch <martyn.welch@ge.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License
98c2ecf20Sopenharmony_ci * version 2.  This program is licensed "as is" without any warranty of any
108c2ecf20Sopenharmony_ci * kind, whether express or implied.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/stddef.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/irq.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
218c2ecf20Sopenharmony_ci#include <asm/io.h>
228c2ecf20Sopenharmony_ci#include <asm/prom.h>
238c2ecf20Sopenharmony_ci#include <asm/irq.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "ge_pic.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define DEBUG
288c2ecf20Sopenharmony_ci#undef DEBUG
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#ifdef DEBUG
318c2ecf20Sopenharmony_ci#define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0)
328c2ecf20Sopenharmony_ci#else
338c2ecf20Sopenharmony_ci#define DBG(fmt...) do { } while (0)
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define GEF_PIC_NUM_IRQS	32
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Interrupt Controller Interface Registers */
398c2ecf20Sopenharmony_ci#define GEF_PIC_INTR_STATUS	0x0000
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define GEF_PIC_INTR_MASK(cpu)	(0x0010 + (0x4 * cpu))
428c2ecf20Sopenharmony_ci#define GEF_PIC_CPU0_INTR_MASK	GEF_PIC_INTR_MASK(0)
438c2ecf20Sopenharmony_ci#define GEF_PIC_CPU1_INTR_MASK	GEF_PIC_INTR_MASK(1)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define GEF_PIC_MCP_MASK(cpu)	(0x0018 + (0x4 * cpu))
468c2ecf20Sopenharmony_ci#define GEF_PIC_CPU0_MCP_MASK	GEF_PIC_MCP_MASK(0)
478c2ecf20Sopenharmony_ci#define GEF_PIC_CPU1_MCP_MASK	GEF_PIC_MCP_MASK(1)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(gef_pic_lock);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void __iomem *gef_pic_irq_reg_base;
538c2ecf20Sopenharmony_cistatic struct irq_domain *gef_pic_irq_host;
548c2ecf20Sopenharmony_cistatic int gef_pic_cascade_irq;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Interrupt Controller Handling
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * The interrupt controller handles interrupts for most on board interrupts,
608c2ecf20Sopenharmony_ci * apart from PCI interrupts. For example on SBC610:
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * 17:31 RO Reserved
638c2ecf20Sopenharmony_ci * 16    RO PCI Express Doorbell 3 Status
648c2ecf20Sopenharmony_ci * 15    RO PCI Express Doorbell 2 Status
658c2ecf20Sopenharmony_ci * 14    RO PCI Express Doorbell 1 Status
668c2ecf20Sopenharmony_ci * 13    RO PCI Express Doorbell 0 Status
678c2ecf20Sopenharmony_ci * 12    RO Real Time Clock Interrupt Status
688c2ecf20Sopenharmony_ci * 11    RO Temperature Interrupt Status
698c2ecf20Sopenharmony_ci * 10    RO Temperature Critical Interrupt Status
708c2ecf20Sopenharmony_ci * 9     RO Ethernet PHY1 Interrupt Status
718c2ecf20Sopenharmony_ci * 8     RO Ethernet PHY3 Interrupt Status
728c2ecf20Sopenharmony_ci * 7     RO PEX8548 Interrupt Status
738c2ecf20Sopenharmony_ci * 6     RO Reserved
748c2ecf20Sopenharmony_ci * 5     RO Watchdog 0 Interrupt Status
758c2ecf20Sopenharmony_ci * 4     RO Watchdog 1 Interrupt Status
768c2ecf20Sopenharmony_ci * 3     RO AXIS Message FIFO A Interrupt Status
778c2ecf20Sopenharmony_ci * 2     RO AXIS Message FIFO B Interrupt Status
788c2ecf20Sopenharmony_ci * 1     RO AXIS Message FIFO C Interrupt Status
798c2ecf20Sopenharmony_ci * 0     RO AXIS Message FIFO D Interrupt Status
808c2ecf20Sopenharmony_ci *
818c2ecf20Sopenharmony_ci * Interrupts can be forwarded to one of two output lines. Nothing
828c2ecf20Sopenharmony_ci * clever is done, so if the masks are incorrectly set, a single input
838c2ecf20Sopenharmony_ci * interrupt could generate interrupts on both output lines!
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci * The dual lines are there to allow the chained interrupts to be easily
868c2ecf20Sopenharmony_ci * passed into two different cores. We currently do not use this functionality
878c2ecf20Sopenharmony_ci * in this driver.
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci * Controller can also be configured to generate Machine checks (MCP), again on
908c2ecf20Sopenharmony_ci * two lines, to be attached to two different cores. It is suggested that these
918c2ecf20Sopenharmony_ci * should be masked out.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void gef_pic_cascade(struct irq_desc *desc)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
978c2ecf20Sopenharmony_ci	unsigned int cascade_irq;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/*
1008c2ecf20Sopenharmony_ci	 * See if we actually have an interrupt, call generic handling code if
1018c2ecf20Sopenharmony_ci	 * we do.
1028c2ecf20Sopenharmony_ci	 */
1038c2ecf20Sopenharmony_ci	cascade_irq = gef_pic_get_irq();
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (cascade_irq)
1068c2ecf20Sopenharmony_ci		generic_handle_irq(cascade_irq);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	chip->irq_eoi(&desc->irq_data);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic void gef_pic_mask(struct irq_data *d)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	unsigned long flags;
1148c2ecf20Sopenharmony_ci	unsigned int hwirq = irqd_to_hwirq(d);
1158c2ecf20Sopenharmony_ci	u32 mask;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&gef_pic_lock, flags);
1188c2ecf20Sopenharmony_ci	mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
1198c2ecf20Sopenharmony_ci	mask &= ~(1 << hwirq);
1208c2ecf20Sopenharmony_ci	out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask);
1218c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void gef_pic_mask_ack(struct irq_data *d)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	/* Don't think we actually have to do anything to ack an interrupt,
1278c2ecf20Sopenharmony_ci	 * we just need to clear down the devices interrupt and it will go away
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	gef_pic_mask(d);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void gef_pic_unmask(struct irq_data *d)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	unsigned long flags;
1358c2ecf20Sopenharmony_ci	unsigned int hwirq = irqd_to_hwirq(d);
1368c2ecf20Sopenharmony_ci	u32 mask;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&gef_pic_lock, flags);
1398c2ecf20Sopenharmony_ci	mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
1408c2ecf20Sopenharmony_ci	mask |= (1 << hwirq);
1418c2ecf20Sopenharmony_ci	out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask);
1428c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic struct irq_chip gef_pic_chip = {
1468c2ecf20Sopenharmony_ci	.name		= "gefp",
1478c2ecf20Sopenharmony_ci	.irq_mask	= gef_pic_mask,
1488c2ecf20Sopenharmony_ci	.irq_mask_ack	= gef_pic_mask_ack,
1498c2ecf20Sopenharmony_ci	.irq_unmask	= gef_pic_unmask,
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/* When an interrupt is being configured, this call allows some flexibilty
1548c2ecf20Sopenharmony_ci * in deciding which irq_chip structure is used
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic int gef_pic_host_map(struct irq_domain *h, unsigned int virq,
1578c2ecf20Sopenharmony_ci			  irq_hw_number_t hwirq)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	/* All interrupts are LEVEL sensitive */
1608c2ecf20Sopenharmony_ci	irq_set_status_flags(virq, IRQ_LEVEL);
1618c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int gef_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
1678c2ecf20Sopenharmony_ci			    const u32 *intspec, unsigned int intsize,
1688c2ecf20Sopenharmony_ci			    irq_hw_number_t *out_hwirq, unsigned int *out_flags)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	*out_hwirq = intspec[0];
1728c2ecf20Sopenharmony_ci	if (intsize > 1)
1738c2ecf20Sopenharmony_ci		*out_flags = intspec[1];
1748c2ecf20Sopenharmony_ci	else
1758c2ecf20Sopenharmony_ci		*out_flags = IRQ_TYPE_LEVEL_HIGH;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic const struct irq_domain_ops gef_pic_host_ops = {
1818c2ecf20Sopenharmony_ci	.map	= gef_pic_host_map,
1828c2ecf20Sopenharmony_ci	.xlate	= gef_pic_host_xlate,
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/*
1878c2ecf20Sopenharmony_ci * Initialisation of PIC, this should be called in BSP
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_civoid __init gef_pic_init(struct device_node *np)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	unsigned long flags;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* Map the devices registers into memory */
1948c2ecf20Sopenharmony_ci	gef_pic_irq_reg_base = of_iomap(np, 0);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&gef_pic_lock, flags);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* Initialise everything as masked. */
1998c2ecf20Sopenharmony_ci	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0);
2008c2ecf20Sopenharmony_ci	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0);
2038c2ecf20Sopenharmony_ci	out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* Map controller */
2088c2ecf20Sopenharmony_ci	gef_pic_cascade_irq = irq_of_parse_and_map(np, 0);
2098c2ecf20Sopenharmony_ci	if (!gef_pic_cascade_irq) {
2108c2ecf20Sopenharmony_ci		printk(KERN_ERR "SBC610: failed to map cascade interrupt");
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Setup an irq_domain structure */
2158c2ecf20Sopenharmony_ci	gef_pic_irq_host = irq_domain_add_linear(np, GEF_PIC_NUM_IRQS,
2168c2ecf20Sopenharmony_ci					  &gef_pic_host_ops, NULL);
2178c2ecf20Sopenharmony_ci	if (gef_pic_irq_host == NULL)
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Chain with parent controller */
2218c2ecf20Sopenharmony_ci	irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/*
2258c2ecf20Sopenharmony_ci * This is called when we receive an interrupt with apparently comes from this
2268c2ecf20Sopenharmony_ci * chip - check, returning the highest interrupt generated or return 0.
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_ciunsigned int gef_pic_get_irq(void)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	u32 cause, mask, active;
2318c2ecf20Sopenharmony_ci	unsigned int virq = 0;
2328c2ecf20Sopenharmony_ci	int hwirq;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	active = cause & mask;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (active) {
2418c2ecf20Sopenharmony_ci		for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) {
2428c2ecf20Sopenharmony_ci			if (active & (0x1 << hwirq))
2438c2ecf20Sopenharmony_ci				break;
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci		virq = irq_linear_revmap(gef_pic_irq_host,
2468c2ecf20Sopenharmony_ci			(irq_hw_number_t)hwirq);
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return virq;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
252