1ea2305f6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21879f711SJohn Rigby /*
31879f711SJohn Rigby * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
41879f711SJohn Rigby *
51879f711SJohn Rigby * Author: John Rigby, <jrigby@freescale.com>
61879f711SJohn Rigby *
71879f711SJohn Rigby * Description:
81879f711SJohn Rigby * MPC5121ADS CPLD irq handling
91879f711SJohn Rigby */
101879f711SJohn Rigby
111879f711SJohn Rigby #undef DEBUG
121879f711SJohn Rigby
131879f711SJohn Rigby #include <linux/kernel.h>
141879f711SJohn Rigby #include <linux/interrupt.h>
151879f711SJohn Rigby #include <linux/irq.h>
161879f711SJohn Rigby #include <linux/io.h>
17*e6f6390aSChristophe Leroy #include <linux/of_address.h>
18*e6f6390aSChristophe Leroy #include <linux/of_irq.h>
191879f711SJohn Rigby
201879f711SJohn Rigby static struct device_node *cpld_pic_node;
21bae1d8f1SGrant Likely static struct irq_domain *cpld_pic_host;
221879f711SJohn Rigby
231879f711SJohn Rigby /*
241879f711SJohn Rigby * Bits to ignore in the misc_status register
251879f711SJohn Rigby * 0x10 touch screen pendown is hard routed to irq1
261879f711SJohn Rigby * 0x02 pci status is read from pci status register
271879f711SJohn Rigby */
281879f711SJohn Rigby #define MISC_IGNORE 0x12
291879f711SJohn Rigby
301879f711SJohn Rigby /*
311879f711SJohn Rigby * Nothing to ignore in pci status register
321879f711SJohn Rigby */
331879f711SJohn Rigby #define PCI_IGNORE 0x00
341879f711SJohn Rigby
351879f711SJohn Rigby struct cpld_pic {
361879f711SJohn Rigby u8 pci_mask;
371879f711SJohn Rigby u8 pci_status;
381879f711SJohn Rigby u8 route;
391879f711SJohn Rigby u8 misc_mask;
401879f711SJohn Rigby u8 misc_status;
411879f711SJohn Rigby u8 misc_control;
421879f711SJohn Rigby };
431879f711SJohn Rigby
441879f711SJohn Rigby static struct cpld_pic __iomem *cpld_regs;
451879f711SJohn Rigby
461879f711SJohn Rigby static void __iomem *
irq_to_pic_mask(unsigned int irq)471879f711SJohn Rigby irq_to_pic_mask(unsigned int irq)
481879f711SJohn Rigby {
491879f711SJohn Rigby return irq <= 7 ? &cpld_regs->pci_mask : &cpld_regs->misc_mask;
501879f711SJohn Rigby }
511879f711SJohn Rigby
521879f711SJohn Rigby static unsigned int
irq_to_pic_bit(unsigned int irq)531879f711SJohn Rigby irq_to_pic_bit(unsigned int irq)
541879f711SJohn Rigby {
551879f711SJohn Rigby return 1 << (irq & 0x7);
561879f711SJohn Rigby }
571879f711SJohn Rigby
581879f711SJohn Rigby static void
cpld_mask_irq(struct irq_data * d)590eb31577SLennert Buytenhek cpld_mask_irq(struct irq_data *d)
601879f711SJohn Rigby {
61476eb491SGrant Likely unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
621879f711SJohn Rigby void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
631879f711SJohn Rigby
641879f711SJohn Rigby out_8(pic_mask,
651879f711SJohn Rigby in_8(pic_mask) | irq_to_pic_bit(cpld_irq));
661879f711SJohn Rigby }
671879f711SJohn Rigby
681879f711SJohn Rigby static void
cpld_unmask_irq(struct irq_data * d)690eb31577SLennert Buytenhek cpld_unmask_irq(struct irq_data *d)
701879f711SJohn Rigby {
71476eb491SGrant Likely unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
721879f711SJohn Rigby void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
731879f711SJohn Rigby
741879f711SJohn Rigby out_8(pic_mask,
751879f711SJohn Rigby in_8(pic_mask) & ~irq_to_pic_bit(cpld_irq));
761879f711SJohn Rigby }
771879f711SJohn Rigby
781879f711SJohn Rigby static struct irq_chip cpld_pic = {
79b27df672SThomas Gleixner .name = "CPLD PIC",
800eb31577SLennert Buytenhek .irq_mask = cpld_mask_irq,
810eb31577SLennert Buytenhek .irq_ack = cpld_mask_irq,
820eb31577SLennert Buytenhek .irq_unmask = cpld_unmask_irq,
831879f711SJohn Rigby };
841879f711SJohn Rigby
852c899658SMarc Zyngier static unsigned int
cpld_pic_get_irq(int offset,u8 ignore,u8 __iomem * statusp,u8 __iomem * maskp)861879f711SJohn Rigby cpld_pic_get_irq(int offset, u8 ignore, u8 __iomem *statusp,
871879f711SJohn Rigby u8 __iomem *maskp)
881879f711SJohn Rigby {
891879f711SJohn Rigby u8 status = in_8(statusp);
901879f711SJohn Rigby u8 mask = in_8(maskp);
911879f711SJohn Rigby
921879f711SJohn Rigby /* ignore don't cares and masked irqs */
931879f711SJohn Rigby status |= (ignore | mask);
941879f711SJohn Rigby
951879f711SJohn Rigby if (status == 0xff)
962c899658SMarc Zyngier return ~0;
971879f711SJohn Rigby
982c899658SMarc Zyngier return ffz(status) + offset;
991879f711SJohn Rigby }
1001879f711SJohn Rigby
cpld_pic_cascade(struct irq_desc * desc)101bd0b9ac4SThomas Gleixner static void cpld_pic_cascade(struct irq_desc *desc)
1021879f711SJohn Rigby {
1032c899658SMarc Zyngier unsigned int hwirq;
1045aac2d33SThomas Gleixner
1052c899658SMarc Zyngier hwirq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status,
1061879f711SJohn Rigby &cpld_regs->pci_mask);
1072c899658SMarc Zyngier if (hwirq != ~0) {
1082c899658SMarc Zyngier generic_handle_domain_irq(cpld_pic_host, hwirq);
1091879f711SJohn Rigby return;
1101879f711SJohn Rigby }
1111879f711SJohn Rigby
1122c899658SMarc Zyngier hwirq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status,
1131879f711SJohn Rigby &cpld_regs->misc_mask);
1142c899658SMarc Zyngier if (hwirq != ~0) {
1152c899658SMarc Zyngier generic_handle_domain_irq(cpld_pic_host, hwirq);
1161879f711SJohn Rigby return;
1171879f711SJohn Rigby }
1181879f711SJohn Rigby }
1191879f711SJohn Rigby
1201879f711SJohn Rigby static int
cpld_pic_host_match(struct irq_domain * h,struct device_node * node,enum irq_domain_bus_token bus_token)121ad3aedfbSMarc Zyngier cpld_pic_host_match(struct irq_domain *h, struct device_node *node,
122ad3aedfbSMarc Zyngier enum irq_domain_bus_token bus_token)
1231879f711SJohn Rigby {
1241879f711SJohn Rigby return cpld_pic_node == node;
1251879f711SJohn Rigby }
1261879f711SJohn Rigby
1271879f711SJohn Rigby static int
cpld_pic_host_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hw)128bae1d8f1SGrant Likely cpld_pic_host_map(struct irq_domain *h, unsigned int virq,
1291879f711SJohn Rigby irq_hw_number_t hw)
1301879f711SJohn Rigby {
13198488db9SThomas Gleixner irq_set_status_flags(virq, IRQ_LEVEL);
132ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, &cpld_pic, handle_level_irq);
1331879f711SJohn Rigby return 0;
1341879f711SJohn Rigby }
1351879f711SJohn Rigby
1369f70b8ebSGrant Likely static const struct irq_domain_ops cpld_pic_host_ops = {
1371879f711SJohn Rigby .match = cpld_pic_host_match,
1381879f711SJohn Rigby .map = cpld_pic_host_map,
1391879f711SJohn Rigby };
1401879f711SJohn Rigby
1411879f711SJohn Rigby void __init
mpc5121_ads_cpld_map(void)1421879f711SJohn Rigby mpc5121_ads_cpld_map(void)
1431879f711SJohn Rigby {
1441879f711SJohn Rigby struct device_node *np = NULL;
1451879f711SJohn Rigby
1461879f711SJohn Rigby np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
1471879f711SJohn Rigby if (!np) {
1481879f711SJohn Rigby printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
1491879f711SJohn Rigby return;
1501879f711SJohn Rigby }
1511879f711SJohn Rigby
1521879f711SJohn Rigby cpld_regs = of_iomap(np, 0);
1531879f711SJohn Rigby of_node_put(np);
1541879f711SJohn Rigby }
1551879f711SJohn Rigby
1561879f711SJohn Rigby void __init
mpc5121_ads_cpld_pic_init(void)1571879f711SJohn Rigby mpc5121_ads_cpld_pic_init(void)
1581879f711SJohn Rigby {
1591879f711SJohn Rigby unsigned int cascade_irq;
1601879f711SJohn Rigby struct device_node *np = NULL;
1611879f711SJohn Rigby
1621879f711SJohn Rigby pr_debug("cpld_ic_init\n");
1631879f711SJohn Rigby
1641879f711SJohn Rigby np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
1651879f711SJohn Rigby if (!np) {
1661879f711SJohn Rigby printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
1671879f711SJohn Rigby return;
1681879f711SJohn Rigby }
1691879f711SJohn Rigby
1701879f711SJohn Rigby if (!cpld_regs)
1711879f711SJohn Rigby goto end;
1721879f711SJohn Rigby
1731879f711SJohn Rigby cascade_irq = irq_of_parse_and_map(np, 0);
174ef24ba70SMichael Ellerman if (!cascade_irq)
1751879f711SJohn Rigby goto end;
1761879f711SJohn Rigby
1771879f711SJohn Rigby /*
1781879f711SJohn Rigby * statically route touch screen pendown through 1
1791879f711SJohn Rigby * and ignore it here
1801879f711SJohn Rigby * route all others through our cascade irq
1811879f711SJohn Rigby */
1821879f711SJohn Rigby out_8(&cpld_regs->route, 0xfd);
1831879f711SJohn Rigby out_8(&cpld_regs->pci_mask, 0xff);
1841879f711SJohn Rigby /* unmask pci ints in misc mask */
1851879f711SJohn Rigby out_8(&cpld_regs->misc_mask, ~(MISC_IGNORE));
1861879f711SJohn Rigby
1871879f711SJohn Rigby cpld_pic_node = of_node_get(np);
1881879f711SJohn Rigby
189a8db8cf0SGrant Likely cpld_pic_host = irq_domain_add_linear(np, 16, &cpld_pic_host_ops, NULL);
1901879f711SJohn Rigby if (!cpld_pic_host) {
1911879f711SJohn Rigby printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n");
1921879f711SJohn Rigby goto end;
1931879f711SJohn Rigby }
1941879f711SJohn Rigby
195ec775d0eSThomas Gleixner irq_set_chained_handler(cascade_irq, cpld_pic_cascade);
1961879f711SJohn Rigby end:
1971879f711SJohn Rigby of_node_put(np);
1981879f711SJohn Rigby }
199