18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Platform information definitions.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copied from arch/ppc/syslib/cpm2_pic.c with minor subsequent updates
58c2ecf20Sopenharmony_ci * to make in work in arch/powerpc/. Original (c) belongs to Dan Malek.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author:  Vitaly Bordug <vbordug@ru.mvista.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * 1999-2001 (c) Dan Malek <dan@embeddedalley.com>
108c2ecf20Sopenharmony_ci * 2006 (c) MontaVista Software, Inc.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License
138c2ecf20Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any
148c2ecf20Sopenharmony_ci * kind, whether express or implied.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* The CPM2 internal interrupt controller.  It is usually
188c2ecf20Sopenharmony_ci * the only interrupt controller.
198c2ecf20Sopenharmony_ci * There are two 32-bit registers (high/low) for up to 64
208c2ecf20Sopenharmony_ci * possible interrupts.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Now, the fun starts.....Interrupt Numbers DO NOT MAP
238c2ecf20Sopenharmony_ci * in a simple arithmetic fashion to mask or pending registers.
248c2ecf20Sopenharmony_ci * That is, interrupt 4 does not map to bit position 4.
258c2ecf20Sopenharmony_ci * We create two tables, indexed by vector number, to indicate
268c2ecf20Sopenharmony_ci * which register to use and which bit in the register to use.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/stddef.h>
308c2ecf20Sopenharmony_ci#include <linux/sched.h>
318c2ecf20Sopenharmony_ci#include <linux/signal.h>
328c2ecf20Sopenharmony_ci#include <linux/irq.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <asm/immap_cpm2.h>
358c2ecf20Sopenharmony_ci#include <asm/mpc8260.h>
368c2ecf20Sopenharmony_ci#include <asm/io.h>
378c2ecf20Sopenharmony_ci#include <asm/prom.h>
388c2ecf20Sopenharmony_ci#include <asm/fs_pd.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "cpm2_pic.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* External IRQS */
438c2ecf20Sopenharmony_ci#define CPM2_IRQ_EXT1		19
448c2ecf20Sopenharmony_ci#define CPM2_IRQ_EXT7		25
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* Port C IRQS */
478c2ecf20Sopenharmony_ci#define CPM2_IRQ_PORTC15	48
488c2ecf20Sopenharmony_ci#define CPM2_IRQ_PORTC0		63
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic intctl_cpm2_t __iomem *cpm2_intctl;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic struct irq_domain *cpm2_pic_host;
538c2ecf20Sopenharmony_cistatic unsigned long ppc_cached_irq_mask[2]; /* 2 32-bit registers */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const u_char irq_to_siureg[] = {
568c2ecf20Sopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1,
578c2ecf20Sopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1,
588c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0,
598c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0,
608c2ecf20Sopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1,
618c2ecf20Sopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1,
628c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0,
638c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* bit numbers do not match the docs, these are precomputed so the bit for
678c2ecf20Sopenharmony_ci * a given irq is (1 << irq_to_siubit[irq]) */
688c2ecf20Sopenharmony_cistatic const u_char irq_to_siubit[] = {
698c2ecf20Sopenharmony_ci	 0, 15, 14, 13, 12, 11, 10,  9,
708c2ecf20Sopenharmony_ci	 8,  7,  6,  5,  4,  3,  2,  1,
718c2ecf20Sopenharmony_ci	 2,  1,  0, 14, 13, 12, 11, 10,
728c2ecf20Sopenharmony_ci	 9,  8,  7,  6,  5,  4,  3,  0,
738c2ecf20Sopenharmony_ci	31, 30, 29, 28, 27, 26, 25, 24,
748c2ecf20Sopenharmony_ci	23, 22, 21, 20, 19, 18, 17, 16,
758c2ecf20Sopenharmony_ci	16, 17, 18, 19, 20, 21, 22, 23,
768c2ecf20Sopenharmony_ci	24, 25, 26, 27, 28, 29, 30, 31,
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void cpm2_mask_irq(struct irq_data *d)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int	bit, word;
828c2ecf20Sopenharmony_ci	unsigned int irq_nr = irqd_to_hwirq(d);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	bit = irq_to_siubit[irq_nr];
858c2ecf20Sopenharmony_ci	word = irq_to_siureg[irq_nr];
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	ppc_cached_irq_mask[word] &= ~(1 << bit);
888c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void cpm2_unmask_irq(struct irq_data *d)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int	bit, word;
948c2ecf20Sopenharmony_ci	unsigned int irq_nr = irqd_to_hwirq(d);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	bit = irq_to_siubit[irq_nr];
978c2ecf20Sopenharmony_ci	word = irq_to_siureg[irq_nr];
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	ppc_cached_irq_mask[word] |= 1 << bit;
1008c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void cpm2_ack(struct irq_data *d)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int	bit, word;
1068c2ecf20Sopenharmony_ci	unsigned int irq_nr = irqd_to_hwirq(d);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	bit = irq_to_siubit[irq_nr];
1098c2ecf20Sopenharmony_ci	word = irq_to_siureg[irq_nr];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_sipnrh + word, 1 << bit);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void cpm2_end_irq(struct irq_data *d)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	int	bit, word;
1178c2ecf20Sopenharmony_ci	unsigned int irq_nr = irqd_to_hwirq(d);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	bit = irq_to_siubit[irq_nr];
1208c2ecf20Sopenharmony_ci	word = irq_to_siureg[irq_nr];
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	ppc_cached_irq_mask[word] |= 1 << bit;
1238c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * Work around large numbers of spurious IRQs on PowerPC 82xx
1278c2ecf20Sopenharmony_ci	 * systems.
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	mb();
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int cpm2_set_irq_type(struct irq_data *d, unsigned int flow_type)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	unsigned int src = irqd_to_hwirq(d);
1358c2ecf20Sopenharmony_ci	unsigned int vold, vnew, edibit;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* Port C interrupts are either IRQ_TYPE_EDGE_FALLING or
1388c2ecf20Sopenharmony_ci	 * IRQ_TYPE_EDGE_BOTH (default).  All others are IRQ_TYPE_EDGE_FALLING
1398c2ecf20Sopenharmony_ci	 * or IRQ_TYPE_LEVEL_LOW (default)
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) {
1428c2ecf20Sopenharmony_ci		if (flow_type == IRQ_TYPE_NONE)
1438c2ecf20Sopenharmony_ci			flow_type = IRQ_TYPE_EDGE_BOTH;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		if (flow_type != IRQ_TYPE_EDGE_BOTH &&
1468c2ecf20Sopenharmony_ci		    flow_type != IRQ_TYPE_EDGE_FALLING)
1478c2ecf20Sopenharmony_ci			goto err_sense;
1488c2ecf20Sopenharmony_ci	} else {
1498c2ecf20Sopenharmony_ci		if (flow_type == IRQ_TYPE_NONE)
1508c2ecf20Sopenharmony_ci			flow_type = IRQ_TYPE_LEVEL_LOW;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
1538c2ecf20Sopenharmony_ci			goto err_sense;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	irqd_set_trigger_type(d, flow_type);
1578c2ecf20Sopenharmony_ci	if (flow_type & IRQ_TYPE_LEVEL_LOW)
1588c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
1598c2ecf20Sopenharmony_ci	else
1608c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* internal IRQ senses are LEVEL_LOW
1638c2ecf20Sopenharmony_ci	 * EXT IRQ and Port C IRQ senses are programmable
1648c2ecf20Sopenharmony_ci	 */
1658c2ecf20Sopenharmony_ci	if (src >= CPM2_IRQ_EXT1 && src <= CPM2_IRQ_EXT7)
1668c2ecf20Sopenharmony_ci			edibit = (14 - (src - CPM2_IRQ_EXT1));
1678c2ecf20Sopenharmony_ci	else
1688c2ecf20Sopenharmony_ci		if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0)
1698c2ecf20Sopenharmony_ci			edibit = (31 - (CPM2_IRQ_PORTC0 - src));
1708c2ecf20Sopenharmony_ci		else
1718c2ecf20Sopenharmony_ci			return (flow_type & IRQ_TYPE_LEVEL_LOW) ?
1728c2ecf20Sopenharmony_ci				IRQ_SET_MASK_OK_NOCOPY : -EINVAL;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	vold = in_be32(&cpm2_intctl->ic_siexr);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING)
1778c2ecf20Sopenharmony_ci		vnew = vold | (1 << edibit);
1788c2ecf20Sopenharmony_ci	else
1798c2ecf20Sopenharmony_ci		vnew = vold & ~(1 << edibit);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (vold != vnew)
1828c2ecf20Sopenharmony_ci		out_be32(&cpm2_intctl->ic_siexr, vnew);
1838c2ecf20Sopenharmony_ci	return IRQ_SET_MASK_OK_NOCOPY;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cierr_sense:
1868c2ecf20Sopenharmony_ci	pr_err("CPM2 PIC: sense type 0x%x not supported\n", flow_type);
1878c2ecf20Sopenharmony_ci	return -EINVAL;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic struct irq_chip cpm2_pic = {
1918c2ecf20Sopenharmony_ci	.name = "CPM2 SIU",
1928c2ecf20Sopenharmony_ci	.irq_mask = cpm2_mask_irq,
1938c2ecf20Sopenharmony_ci	.irq_unmask = cpm2_unmask_irq,
1948c2ecf20Sopenharmony_ci	.irq_ack = cpm2_ack,
1958c2ecf20Sopenharmony_ci	.irq_eoi = cpm2_end_irq,
1968c2ecf20Sopenharmony_ci	.irq_set_type = cpm2_set_irq_type,
1978c2ecf20Sopenharmony_ci	.flags = IRQCHIP_EOI_IF_HANDLED,
1988c2ecf20Sopenharmony_ci};
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciunsigned int cpm2_get_irq(void)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int irq;
2038c2ecf20Sopenharmony_ci	unsigned long bits;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci       /* For CPM2, read the SIVEC register and shift the bits down
2068c2ecf20Sopenharmony_ci         * to get the irq number.         */
2078c2ecf20Sopenharmony_ci        bits = in_be32(&cpm2_intctl->ic_sivec);
2088c2ecf20Sopenharmony_ci        irq = bits >> 26;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (irq == 0)
2118c2ecf20Sopenharmony_ci		return(-1);
2128c2ecf20Sopenharmony_ci	return irq_linear_revmap(cpm2_pic_host, irq);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int cpm2_pic_host_map(struct irq_domain *h, unsigned int virq,
2168c2ecf20Sopenharmony_ci			  irq_hw_number_t hw)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	irq_set_status_flags(virq, IRQ_LEVEL);
2218c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, &cpm2_pic, handle_level_irq);
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic const struct irq_domain_ops cpm2_pic_host_ops = {
2268c2ecf20Sopenharmony_ci	.map = cpm2_pic_host_map,
2278c2ecf20Sopenharmony_ci	.xlate = irq_domain_xlate_onetwocell,
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_civoid cpm2_pic_init(struct device_node *node)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	int i;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	cpm2_intctl = cpm2_map(im_intctl);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Clear the CPM IRQ controller, in case it has any bits set
2378c2ecf20Sopenharmony_ci	 * from the bootloader
2388c2ecf20Sopenharmony_ci	 */
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Mask out everything */
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_simrh, 0x00000000);
2438c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_simrl, 0x00000000);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	wmb();
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* Ack everything */
2488c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_sipnrh, 0xffffffff);
2498c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_sipnrl, 0xffffffff);
2508c2ecf20Sopenharmony_ci	wmb();
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Dummy read of the vector */
2538c2ecf20Sopenharmony_ci	i = in_be32(&cpm2_intctl->ic_sivec);
2548c2ecf20Sopenharmony_ci	rmb();
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* Initialize the default interrupt mapping priorities,
2578c2ecf20Sopenharmony_ci	 * in case the boot rom changed something on us.
2588c2ecf20Sopenharmony_ci	 */
2598c2ecf20Sopenharmony_ci	out_be16(&cpm2_intctl->ic_sicr, 0);
2608c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_scprrh, 0x05309770);
2618c2ecf20Sopenharmony_ci	out_be32(&cpm2_intctl->ic_scprrl, 0x05309770);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* create a legacy host */
2648c2ecf20Sopenharmony_ci	cpm2_pic_host = irq_domain_add_linear(node, 64, &cpm2_pic_host_ops, NULL);
2658c2ecf20Sopenharmony_ci	if (cpm2_pic_host == NULL) {
2668c2ecf20Sopenharmony_ci		printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
2678c2ecf20Sopenharmony_ci		return;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci}
270