linux/arch/arm/mach-pxa/irq.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-pxa/irq.c
   3 *
   4 *  Generic PXA IRQ handling
   5 *
   6 *  Author:     Nicolas Pitre
   7 *  Created:    Jun 15, 2001
   8 *  Copyright:  MontaVista Software Inc.
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License version 2 as
  12 *  published by the Free Software Foundation.
  13 */
  14#include <linux/bitops.h>
  15#include <linux/init.h>
  16#include <linux/module.h>
  17#include <linux/interrupt.h>
  18#include <linux/syscore_ops.h>
  19#include <linux/io.h>
  20#include <linux/irq.h>
  21#include <linux/of_address.h>
  22#include <linux/of_irq.h>
  23
  24#include <asm/exception.h>
  25
  26#include <mach/hardware.h>
  27#include <mach/irqs.h>
  28
  29#include "generic.h"
  30
  31#define ICIP                    (0x000)
  32#define ICMR                    (0x004)
  33#define ICLR                    (0x008)
  34#define ICFR                    (0x00c)
  35#define ICPR                    (0x010)
  36#define ICCR                    (0x014)
  37#define ICHP                    (0x018)
  38#define IPR(i)                  (((i) < 32) ? (0x01c + ((i) << 2)) :            \
  39                                ((i) < 64) ? (0x0b0 + (((i) - 32) << 2)) :      \
  40                                      (0x144 + (((i) - 64) << 2)))
  41#define ICHP_VAL_IRQ            (1 << 31)
  42#define ICHP_IRQ(i)             (((i) >> 16) & 0x7fff)
  43#define IPR_VALID               (1 << 31)
  44
  45#define MAX_INTERNAL_IRQS       128
  46
  47/*
  48 * This is for peripheral IRQs internal to the PXA chip.
  49 */
  50
  51static void __iomem *pxa_irq_base;
  52static int pxa_internal_irq_nr;
  53static bool cpu_has_ipr;
  54static struct irq_domain *pxa_irq_domain;
  55
  56static inline void __iomem *irq_base(int i)
  57{
  58        static unsigned long phys_base_offset[] = {
  59                0x0,
  60                0x9c,
  61                0x130,
  62        };
  63
  64        return pxa_irq_base + phys_base_offset[i];
  65}
  66
  67void pxa_mask_irq(struct irq_data *d)
  68{
  69        void __iomem *base = irq_data_get_irq_chip_data(d);
  70        irq_hw_number_t irq = irqd_to_hwirq(d);
  71        uint32_t icmr = __raw_readl(base + ICMR);
  72
  73        icmr &= ~BIT(irq & 0x1f);
  74        __raw_writel(icmr, base + ICMR);
  75}
  76
  77void pxa_unmask_irq(struct irq_data *d)
  78{
  79        void __iomem *base = irq_data_get_irq_chip_data(d);
  80        irq_hw_number_t irq = irqd_to_hwirq(d);
  81        uint32_t icmr = __raw_readl(base + ICMR);
  82
  83        icmr |= BIT(irq & 0x1f);
  84        __raw_writel(icmr, base + ICMR);
  85}
  86
  87static struct irq_chip pxa_internal_irq_chip = {
  88        .name           = "SC",
  89        .irq_ack        = pxa_mask_irq,
  90        .irq_mask       = pxa_mask_irq,
  91        .irq_unmask     = pxa_unmask_irq,
  92};
  93
  94asmlinkage void __exception_irq_entry icip_handle_irq(struct pt_regs *regs)
  95{
  96        uint32_t icip, icmr, mask;
  97
  98        do {
  99                icip = __raw_readl(pxa_irq_base + ICIP);
 100                icmr = __raw_readl(pxa_irq_base + ICMR);
 101                mask = icip & icmr;
 102
 103                if (mask == 0)
 104                        break;
 105
 106                handle_IRQ(PXA_IRQ(fls(mask) - 1), regs);
 107        } while (1);
 108}
 109
 110asmlinkage void __exception_irq_entry ichp_handle_irq(struct pt_regs *regs)
 111{
 112        uint32_t ichp;
 113
 114        do {
 115                __asm__ __volatile__("mrc p6, 0, %0, c5, c0, 0\n": "=r"(ichp));
 116
 117                if ((ichp & ICHP_VAL_IRQ) == 0)
 118                        break;
 119
 120                handle_IRQ(PXA_IRQ(ICHP_IRQ(ichp)), regs);
 121        } while (1);
 122}
 123
 124static int pxa_irq_map(struct irq_domain *h, unsigned int virq,
 125                       irq_hw_number_t hw)
 126{
 127        void __iomem *base = irq_base(hw / 32);
 128
 129        /* initialize interrupt priority */
 130        if (cpu_has_ipr)
 131                __raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw));
 132
 133        irq_set_chip_and_handler(virq, &pxa_internal_irq_chip,
 134                                 handle_level_irq);
 135        irq_set_chip_data(virq, base);
 136
 137        return 0;
 138}
 139
 140static const struct irq_domain_ops pxa_irq_ops = {
 141        .map    = pxa_irq_map,
 142        .xlate  = irq_domain_xlate_onecell,
 143};
 144
 145static __init void
 146pxa_init_irq_common(struct device_node *node, int irq_nr,
 147                    int (*fn)(struct irq_data *, unsigned int))
 148{
 149        int n;
 150
 151        pxa_internal_irq_nr = irq_nr;
 152        pxa_irq_domain = irq_domain_add_legacy(node, irq_nr,
 153                                               PXA_IRQ(0), 0,
 154                                               &pxa_irq_ops, NULL);
 155        if (!pxa_irq_domain)
 156                panic("Unable to add PXA IRQ domain\n");
 157        irq_set_default_host(pxa_irq_domain);
 158
 159        for (n = 0; n < irq_nr; n += 32) {
 160                void __iomem *base = irq_base(n >> 5);
 161
 162                __raw_writel(0, base + ICMR);   /* disable all IRQs */
 163                __raw_writel(0, base + ICLR);   /* all IRQs are IRQ, not FIQ */
 164        }
 165        /* only unmasked interrupts kick us out of idle */
 166        __raw_writel(1, irq_base(0) + ICCR);
 167
 168        pxa_internal_irq_chip.irq_set_wake = fn;
 169}
 170
 171void __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
 172{
 173        BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
 174
 175        pxa_irq_base = io_p2v(0x40d00000);
 176        cpu_has_ipr = !cpu_is_pxa25x();
 177        pxa_init_irq_common(NULL, irq_nr, fn);
 178}
 179
 180#ifdef CONFIG_PM
 181static unsigned long saved_icmr[MAX_INTERNAL_IRQS/32];
 182static unsigned long saved_ipr[MAX_INTERNAL_IRQS];
 183
 184static int pxa_irq_suspend(void)
 185{
 186        int i;
 187
 188        for (i = 0; i < pxa_internal_irq_nr / 32; i++) {
 189                void __iomem *base = irq_base(i);
 190
 191                saved_icmr[i] = __raw_readl(base + ICMR);
 192                __raw_writel(0, base + ICMR);
 193        }
 194
 195        if (cpu_has_ipr) {
 196                for (i = 0; i < pxa_internal_irq_nr; i++)
 197                        saved_ipr[i] = __raw_readl(pxa_irq_base + IPR(i));
 198        }
 199
 200        return 0;
 201}
 202
 203static void pxa_irq_resume(void)
 204{
 205        int i;
 206
 207        for (i = 0; i < pxa_internal_irq_nr / 32; i++) {
 208                void __iomem *base = irq_base(i);
 209
 210                __raw_writel(saved_icmr[i], base + ICMR);
 211                __raw_writel(0, base + ICLR);
 212        }
 213
 214        if (cpu_has_ipr)
 215                for (i = 0; i < pxa_internal_irq_nr; i++)
 216                        __raw_writel(saved_ipr[i], pxa_irq_base + IPR(i));
 217
 218        __raw_writel(1, pxa_irq_base + ICCR);
 219}
 220#else
 221#define pxa_irq_suspend         NULL
 222#define pxa_irq_resume          NULL
 223#endif
 224
 225struct syscore_ops pxa_irq_syscore_ops = {
 226        .suspend        = pxa_irq_suspend,
 227        .resume         = pxa_irq_resume,
 228};
 229
 230#ifdef CONFIG_OF
 231static const struct of_device_id intc_ids[] __initconst = {
 232        { .compatible = "marvell,pxa-intc", },
 233        {}
 234};
 235
 236void __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int))
 237{
 238        struct device_node *node;
 239        struct resource res;
 240        int ret;
 241
 242        node = of_find_matching_node(NULL, intc_ids);
 243        if (!node) {
 244                pr_err("Failed to find interrupt controller in arch-pxa\n");
 245                return;
 246        }
 247
 248        ret = of_property_read_u32(node, "marvell,intc-nr-irqs",
 249                                   &pxa_internal_irq_nr);
 250        if (ret) {
 251                pr_err("Not found marvell,intc-nr-irqs property\n");
 252                return;
 253        }
 254
 255        ret = of_address_to_resource(node, 0, &res);
 256        if (ret < 0) {
 257                pr_err("No registers defined for node\n");
 258                return;
 259        }
 260        pxa_irq_base = io_p2v(res.start);
 261
 262        if (of_find_property(node, "marvell,intc-priority", NULL))
 263                cpu_has_ipr = 1;
 264
 265        ret = irq_alloc_descs(-1, 0, pxa_internal_irq_nr, 0);
 266        if (ret < 0) {
 267                pr_err("Failed to allocate IRQ numbers\n");
 268                return;
 269        }
 270
 271        pxa_init_irq_common(node, pxa_internal_irq_nr, fn);
 272}
 273#endif /* CONFIG_OF */
 274