linux/drivers/irqchip/irq-orion.c
<<
>>
Prefs
   1/*
   2 * Marvell Orion SoCs IRQ chip driver.
   3 *
   4 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
   5 *
   6 * This file is licensed under the terms of the GNU General Public
   7 * License version 2.  This program is licensed "as is" without any
   8 * warranty of any kind, whether express or implied.
   9 */
  10
  11#include <linux/io.h>
  12#include <linux/irq.h>
  13#include <linux/of.h>
  14#include <linux/of_address.h>
  15#include <linux/of_irq.h>
  16#include <asm/exception.h>
  17#include <asm/mach/irq.h>
  18
  19#include "irqchip.h"
  20
  21/*
  22 * Orion SoC main interrupt controller
  23 */
  24#define ORION_IRQS_PER_CHIP             32
  25
  26#define ORION_IRQ_CAUSE                 0x00
  27#define ORION_IRQ_MASK                  0x04
  28#define ORION_IRQ_FIQ_MASK              0x08
  29#define ORION_IRQ_ENDP_MASK             0x0c
  30
  31static struct irq_domain *orion_irq_domain;
  32
  33static asmlinkage void
  34__exception_irq_entry orion_handle_irq(struct pt_regs *regs)
  35{
  36        struct irq_domain_chip_generic *dgc = orion_irq_domain->gc;
  37        int n, base = 0;
  38
  39        for (n = 0; n < dgc->num_chips; n++, base += ORION_IRQS_PER_CHIP) {
  40                struct irq_chip_generic *gc =
  41                        irq_get_domain_generic_chip(orion_irq_domain, base);
  42                u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) &
  43                        gc->mask_cache;
  44                while (stat) {
  45                        u32 hwirq = ffs(stat) - 1;
  46                        u32 irq = irq_find_mapping(orion_irq_domain,
  47                                                   gc->irq_base + hwirq);
  48                        handle_IRQ(irq, regs);
  49                        stat &= ~(1 << hwirq);
  50                }
  51        }
  52}
  53
  54static int __init orion_irq_init(struct device_node *np,
  55                                 struct device_node *parent)
  56{
  57        unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
  58        int n, ret, base, num_chips = 0;
  59        struct resource r;
  60
  61        /* count number of irq chips by valid reg addresses */
  62        while (of_address_to_resource(np, num_chips, &r) == 0)
  63                num_chips++;
  64
  65        orion_irq_domain = irq_domain_add_linear(np,
  66                                num_chips * ORION_IRQS_PER_CHIP,
  67                                &irq_generic_chip_ops, NULL);
  68        if (!orion_irq_domain)
  69                panic("%s: unable to add irq domain\n", np->name);
  70
  71        ret = irq_alloc_domain_generic_chips(orion_irq_domain,
  72                                ORION_IRQS_PER_CHIP, 1, np->name,
  73                                handle_level_irq, clr, 0,
  74                                IRQ_GC_INIT_MASK_CACHE);
  75        if (ret)
  76                panic("%s: unable to alloc irq domain gc\n", np->name);
  77
  78        for (n = 0, base = 0; n < num_chips; n++, base += ORION_IRQS_PER_CHIP) {
  79                struct irq_chip_generic *gc =
  80                        irq_get_domain_generic_chip(orion_irq_domain, base);
  81
  82                of_address_to_resource(np, n, &r);
  83
  84                if (!request_mem_region(r.start, resource_size(&r), np->name))
  85                        panic("%s: unable to request mem region %d",
  86                              np->name, n);
  87
  88                gc->reg_base = ioremap(r.start, resource_size(&r));
  89                if (!gc->reg_base)
  90                        panic("%s: unable to map resource %d", np->name, n);
  91
  92                gc->chip_types[0].regs.mask = ORION_IRQ_MASK;
  93                gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
  94                gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
  95
  96                /* mask all interrupts */
  97                writel(0, gc->reg_base + ORION_IRQ_MASK);
  98        }
  99
 100        set_handle_irq(orion_handle_irq);
 101        return 0;
 102}
 103IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init);
 104
 105/*
 106 * Orion SoC bridge interrupt controller
 107 */
 108#define ORION_BRIDGE_IRQ_CAUSE  0x00
 109#define ORION_BRIDGE_IRQ_MASK   0x04
 110
 111static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc)
 112{
 113        struct irq_domain *d = irq_get_handler_data(irq);
 114        struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, irq);
 115        u32 stat = readl_relaxed(gc->reg_base + ORION_BRIDGE_IRQ_CAUSE) &
 116                   gc->mask_cache;
 117
 118        while (stat) {
 119                u32 hwirq = ffs(stat) - 1;
 120
 121                generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq));
 122                stat &= ~(1 << hwirq);
 123        }
 124}
 125
 126static int __init orion_bridge_irq_init(struct device_node *np,
 127                                        struct device_node *parent)
 128{
 129        unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
 130        struct resource r;
 131        struct irq_domain *domain;
 132        struct irq_chip_generic *gc;
 133        int ret, irq, nrirqs = 32;
 134
 135        /* get optional number of interrupts provided */
 136        of_property_read_u32(np, "marvell,#interrupts", &nrirqs);
 137
 138        domain = irq_domain_add_linear(np, nrirqs,
 139                                       &irq_generic_chip_ops, NULL);
 140        if (!domain) {
 141                pr_err("%s: unable to add irq domain\n", np->name);
 142                return -ENOMEM;
 143        }
 144
 145        ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name,
 146                             handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
 147        if (ret) {
 148                pr_err("%s: unable to alloc irq domain gc\n", np->name);
 149                return ret;
 150        }
 151
 152        ret = of_address_to_resource(np, 0, &r);
 153        if (ret) {
 154                pr_err("%s: unable to get resource\n", np->name);
 155                return ret;
 156        }
 157
 158        if (!request_mem_region(r.start, resource_size(&r), np->name)) {
 159                pr_err("%s: unable to request mem region\n", np->name);
 160                return -ENOMEM;
 161        }
 162
 163        /* Map the parent interrupt for the chained handler */
 164        irq = irq_of_parse_and_map(np, 0);
 165        if (irq <= 0) {
 166                pr_err("%s: unable to parse irq\n", np->name);
 167                return -EINVAL;
 168        }
 169
 170        gc = irq_get_domain_generic_chip(domain, 0);
 171        gc->reg_base = ioremap(r.start, resource_size(&r));
 172        if (!gc->reg_base) {
 173                pr_err("%s: unable to map resource\n", np->name);
 174                return -ENOMEM;
 175        }
 176
 177        gc->chip_types[0].regs.ack = ORION_BRIDGE_IRQ_CAUSE;
 178        gc->chip_types[0].regs.mask = ORION_BRIDGE_IRQ_MASK;
 179        gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
 180        gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
 181        gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
 182
 183        /* mask all interrupts */
 184        writel(0, gc->reg_base + ORION_BRIDGE_IRQ_MASK);
 185
 186        irq_set_handler_data(irq, domain);
 187        irq_set_chained_handler(irq, orion_bridge_irq_handler);
 188
 189        return 0;
 190}
 191IRQCHIP_DECLARE(orion_bridge_intc,
 192                "marvell,orion-bridge-intc", orion_bridge_irq_init);
 193
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.