linux/drivers/irqchip/irq-lpc32xx.c
<<
>>
Prefs
   1/*
   2 * Copyright 2015-2016 Vladimir Zapolskiy <vz@mleia.com>
   3 *
   4 * The code contained herein is licensed under the GNU General Public
   5 * License. You may obtain a copy of the GNU General Public License
   6 * Version 2 or later at the following locations:
   7 *
   8 * http://www.opensource.org/licenses/gpl-license.html
   9 * http://www.gnu.org/copyleft/gpl.html
  10 */
  11
  12#define pr_fmt(fmt) "%s: " fmt, __func__
  13
  14#include <linux/io.h>
  15#include <linux/irqchip.h>
  16#include <linux/irqchip/chained_irq.h>
  17#include <linux/of_address.h>
  18#include <linux/of_irq.h>
  19#include <linux/of_platform.h>
  20#include <linux/slab.h>
  21#include <asm/exception.h>
  22
  23#define LPC32XX_INTC_MASK               0x00
  24#define LPC32XX_INTC_RAW                0x04
  25#define LPC32XX_INTC_STAT               0x08
  26#define LPC32XX_INTC_POL                0x0C
  27#define LPC32XX_INTC_TYPE               0x10
  28#define LPC32XX_INTC_FIQ                0x14
  29
  30#define NR_LPC32XX_IC_IRQS              32
  31
  32struct lpc32xx_irq_chip {
  33        void __iomem *base;
  34        struct irq_domain *domain;
  35        struct irq_chip chip;
  36};
  37
  38static struct lpc32xx_irq_chip *lpc32xx_mic_irqc;
  39
  40static inline u32 lpc32xx_ic_read(struct lpc32xx_irq_chip *ic, u32 reg)
  41{
  42        return readl_relaxed(ic->base + reg);
  43}
  44
  45static inline void lpc32xx_ic_write(struct lpc32xx_irq_chip *ic,
  46                                    u32 reg, u32 val)
  47{
  48        writel_relaxed(val, ic->base + reg);
  49}
  50
  51static void lpc32xx_irq_mask(struct irq_data *d)
  52{
  53        struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
  54        u32 val, mask = BIT(d->hwirq);
  55
  56        val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) & ~mask;
  57        lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val);
  58}
  59
  60static void lpc32xx_irq_unmask(struct irq_data *d)
  61{
  62        struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
  63        u32 val, mask = BIT(d->hwirq);
  64
  65        val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) | mask;
  66        lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val);
  67}
  68
  69static void lpc32xx_irq_ack(struct irq_data *d)
  70{
  71        struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
  72        u32 mask = BIT(d->hwirq);
  73
  74        lpc32xx_ic_write(ic, LPC32XX_INTC_RAW, mask);
  75}
  76
  77static int lpc32xx_irq_set_type(struct irq_data *d, unsigned int type)
  78{
  79        struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
  80        u32 val, mask = BIT(d->hwirq);
  81        bool high, edge;
  82
  83        switch (type) {
  84        case IRQ_TYPE_EDGE_RISING:
  85                edge = true;
  86                high = true;
  87                break;
  88        case IRQ_TYPE_EDGE_FALLING:
  89                edge = true;
  90                high = false;
  91                break;
  92        case IRQ_TYPE_LEVEL_HIGH:
  93                edge = false;
  94                high = true;
  95                break;
  96        case IRQ_TYPE_LEVEL_LOW:
  97                edge = false;
  98                high = false;
  99                break;
 100        default:
 101                pr_info("unsupported irq type %d\n", type);
 102                return -EINVAL;
 103        }
 104
 105        irqd_set_trigger_type(d, type);
 106
 107        val = lpc32xx_ic_read(ic, LPC32XX_INTC_POL);
 108        if (high)
 109                val |= mask;
 110        else
 111                val &= ~mask;
 112        lpc32xx_ic_write(ic, LPC32XX_INTC_POL, val);
 113
 114        val = lpc32xx_ic_read(ic, LPC32XX_INTC_TYPE);
 115        if (edge) {
 116                val |= mask;
 117                irq_set_handler_locked(d, handle_edge_irq);
 118        } else {
 119                val &= ~mask;
 120                irq_set_handler_locked(d, handle_level_irq);
 121        }
 122        lpc32xx_ic_write(ic, LPC32XX_INTC_TYPE, val);
 123
 124        return 0;
 125}
 126
 127static void __exception_irq_entry lpc32xx_handle_irq(struct pt_regs *regs)
 128{
 129        struct lpc32xx_irq_chip *ic = lpc32xx_mic_irqc;
 130        u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq;
 131
 132        while (hwirq) {
 133                irq = __ffs(hwirq);
 134                hwirq &= ~BIT(irq);
 135                handle_domain_irq(lpc32xx_mic_irqc->domain, irq, regs);
 136        }
 137}
 138
 139static void lpc32xx_sic_handler(struct irq_desc *desc)
 140{
 141        struct lpc32xx_irq_chip *ic = irq_desc_get_handler_data(desc);
 142        struct irq_chip *chip = irq_desc_get_chip(desc);
 143        u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq;
 144
 145        chained_irq_enter(chip, desc);
 146
 147        while (hwirq) {
 148                irq = __ffs(hwirq);
 149                hwirq &= ~BIT(irq);
 150                generic_handle_irq(irq_find_mapping(ic->domain, irq));
 151        }
 152
 153        chained_irq_exit(chip, desc);
 154}
 155
 156static int lpc32xx_irq_domain_map(struct irq_domain *id, unsigned int virq,
 157                                  irq_hw_number_t hw)
 158{
 159        struct lpc32xx_irq_chip *ic = id->host_data;
 160
 161        irq_set_chip_data(virq, ic);
 162        irq_set_chip_and_handler(virq, &ic->chip, handle_level_irq);
 163        irq_set_status_flags(virq, IRQ_LEVEL);
 164        irq_set_noprobe(virq);
 165
 166        return 0;
 167}
 168
 169static void lpc32xx_irq_domain_unmap(struct irq_domain *id, unsigned int virq)
 170{
 171        irq_set_chip_and_handler(virq, NULL, NULL);
 172}
 173
 174static const struct irq_domain_ops lpc32xx_irq_domain_ops = {
 175        .map    = lpc32xx_irq_domain_map,
 176        .unmap  = lpc32xx_irq_domain_unmap,
 177        .xlate  = irq_domain_xlate_twocell,
 178};
 179
 180static int __init lpc32xx_of_ic_init(struct device_node *node,
 181                                     struct device_node *parent)
 182{
 183        struct lpc32xx_irq_chip *irqc;
 184        bool is_mic = of_device_is_compatible(node, "nxp,lpc3220-mic");
 185        const __be32 *reg = of_get_property(node, "reg", NULL);
 186        u32 parent_irq, i, addr = reg ? be32_to_cpu(*reg) : 0;
 187
 188        irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
 189        if (!irqc)
 190                return -ENOMEM;
 191
 192        irqc->base = of_iomap(node, 0);
 193        if (!irqc->base) {
 194                pr_err("%s: unable to map registers\n", node->full_name);
 195                kfree(irqc);
 196                return -EINVAL;
 197        }
 198
 199        irqc->chip.irq_ack = lpc32xx_irq_ack;
 200        irqc->chip.irq_mask = lpc32xx_irq_mask;
 201        irqc->chip.irq_unmask = lpc32xx_irq_unmask;
 202        irqc->chip.irq_set_type = lpc32xx_irq_set_type;
 203        if (is_mic)
 204                irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.mic", addr);
 205        else
 206                irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.sic", addr);
 207
 208        irqc->domain = irq_domain_add_linear(node, NR_LPC32XX_IC_IRQS,
 209                                             &lpc32xx_irq_domain_ops, irqc);
 210        if (!irqc->domain) {
 211                pr_err("unable to add irq domain\n");
 212                iounmap(irqc->base);
 213                kfree(irqc->chip.name);
 214                kfree(irqc);
 215                return -ENODEV;
 216        }
 217
 218        if (is_mic) {
 219                lpc32xx_mic_irqc = irqc;
 220                set_handle_irq(lpc32xx_handle_irq);
 221        } else {
 222                for (i = 0; i < of_irq_count(node); i++) {
 223                        parent_irq = irq_of_parse_and_map(node, i);
 224                        if (parent_irq)
 225                                irq_set_chained_handler_and_data(parent_irq,
 226                                                 lpc32xx_sic_handler, irqc);
 227                }
 228        }
 229
 230        lpc32xx_ic_write(irqc, LPC32XX_INTC_MASK, 0x00);
 231        lpc32xx_ic_write(irqc, LPC32XX_INTC_POL,  0x00);
 232        lpc32xx_ic_write(irqc, LPC32XX_INTC_TYPE, 0x00);
 233
 234        return 0;
 235}
 236
 237IRQCHIP_DECLARE(nxp_lpc32xx_mic, "nxp,lpc3220-mic", lpc32xx_of_ic_init);
 238IRQCHIP_DECLARE(nxp_lpc32xx_sic, "nxp,lpc3220-sic", lpc32xx_of_ic_init);
 239