linux/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
   4 *
   5 * Author: John Rigby, <jrigby@freescale.com>
   6 *
   7 * Description:
   8 * MPC5121ADS CPLD irq handling
   9 */
  10
  11#undef DEBUG
  12
  13#include <linux/kernel.h>
  14#include <linux/interrupt.h>
  15#include <linux/irq.h>
  16#include <linux/io.h>
  17#include <asm/prom.h>
  18
  19static struct device_node *cpld_pic_node;
  20static struct irq_domain *cpld_pic_host;
  21
  22/*
  23 * Bits to ignore in the misc_status register
  24 * 0x10 touch screen pendown is hard routed to irq1
  25 * 0x02 pci status is read from pci status register
  26 */
  27#define MISC_IGNORE 0x12
  28
  29/*
  30 * Nothing to ignore in pci status register
  31 */
  32#define PCI_IGNORE 0x00
  33
  34struct cpld_pic {
  35        u8 pci_mask;
  36        u8 pci_status;
  37        u8 route;
  38        u8 misc_mask;
  39        u8 misc_status;
  40        u8 misc_control;
  41};
  42
  43static struct cpld_pic __iomem *cpld_regs;
  44
  45static void __iomem *
  46irq_to_pic_mask(unsigned int irq)
  47{
  48        return irq <= 7 ? &cpld_regs->pci_mask : &cpld_regs->misc_mask;
  49}
  50
  51static unsigned int
  52irq_to_pic_bit(unsigned int irq)
  53{
  54        return 1 << (irq & 0x7);
  55}
  56
  57static void
  58cpld_mask_irq(struct irq_data *d)
  59{
  60        unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
  61        void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
  62
  63        out_8(pic_mask,
  64              in_8(pic_mask) | irq_to_pic_bit(cpld_irq));
  65}
  66
  67static void
  68cpld_unmask_irq(struct irq_data *d)
  69{
  70        unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
  71        void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
  72
  73        out_8(pic_mask,
  74              in_8(pic_mask) & ~irq_to_pic_bit(cpld_irq));
  75}
  76
  77static struct irq_chip cpld_pic = {
  78        .name = "CPLD PIC",
  79        .irq_mask = cpld_mask_irq,
  80        .irq_ack = cpld_mask_irq,
  81        .irq_unmask = cpld_unmask_irq,
  82};
  83
  84static unsigned int
  85cpld_pic_get_irq(int offset, u8 ignore, u8 __iomem *statusp,
  86                            u8 __iomem *maskp)
  87{
  88        u8 status = in_8(statusp);
  89        u8 mask = in_8(maskp);
  90
  91        /* ignore don't cares and masked irqs */
  92        status |= (ignore | mask);
  93
  94        if (status == 0xff)
  95                return ~0;
  96
  97        return ffz(status) + offset;
  98}
  99
 100static void cpld_pic_cascade(struct irq_desc *desc)
 101{
 102        unsigned int hwirq;
 103
 104        hwirq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status,
 105                &cpld_regs->pci_mask);
 106        if (hwirq != ~0) {
 107                generic_handle_domain_irq(cpld_pic_host, hwirq);
 108                return;
 109        }
 110
 111        hwirq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status,
 112                &cpld_regs->misc_mask);
 113        if (hwirq != ~0) {
 114                generic_handle_domain_irq(cpld_pic_host, hwirq);
 115                return;
 116        }
 117}
 118
 119static int
 120cpld_pic_host_match(struct irq_domain *h, struct device_node *node,
 121                    enum irq_domain_bus_token bus_token)
 122{
 123        return cpld_pic_node == node;
 124}
 125
 126static int
 127cpld_pic_host_map(struct irq_domain *h, unsigned int virq,
 128                             irq_hw_number_t hw)
 129{
 130        irq_set_status_flags(virq, IRQ_LEVEL);
 131        irq_set_chip_and_handler(virq, &cpld_pic, handle_level_irq);
 132        return 0;
 133}
 134
 135static const struct irq_domain_ops cpld_pic_host_ops = {
 136        .match = cpld_pic_host_match,
 137        .map = cpld_pic_host_map,
 138};
 139
 140void __init
 141mpc5121_ads_cpld_map(void)
 142{
 143        struct device_node *np = NULL;
 144
 145        np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
 146        if (!np) {
 147                printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
 148                return;
 149        }
 150
 151        cpld_regs = of_iomap(np, 0);
 152        of_node_put(np);
 153}
 154
 155void __init
 156mpc5121_ads_cpld_pic_init(void)
 157{
 158        unsigned int cascade_irq;
 159        struct device_node *np = NULL;
 160
 161        pr_debug("cpld_ic_init\n");
 162
 163        np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
 164        if (!np) {
 165                printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
 166                return;
 167        }
 168
 169        if (!cpld_regs)
 170                goto end;
 171
 172        cascade_irq = irq_of_parse_and_map(np, 0);
 173        if (!cascade_irq)
 174                goto end;
 175
 176        /*
 177         * statically route touch screen pendown through 1
 178         * and ignore it here
 179         * route all others through our cascade irq
 180         */
 181        out_8(&cpld_regs->route, 0xfd);
 182        out_8(&cpld_regs->pci_mask, 0xff);
 183        /* unmask pci ints in misc mask */
 184        out_8(&cpld_regs->misc_mask, ~(MISC_IGNORE));
 185
 186        cpld_pic_node = of_node_get(np);
 187
 188        cpld_pic_host = irq_domain_add_linear(np, 16, &cpld_pic_host_ops, NULL);
 189        if (!cpld_pic_host) {
 190                printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n");
 191                goto end;
 192        }
 193
 194        irq_set_chained_handler(cascade_irq, cpld_pic_cascade);
 195end:
 196        of_node_put(np);
 197}
 198