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        set_irq_flags(virq, IRQF_VALID);
 137
 138        return 0;
 139}
 140
 141static struct irq_domain_ops pxa_irq_ops = {
 142        .map    = pxa_irq_map,
 143        .xlate  = irq_domain_xlate_onecell,
 144};
 145
 146static __init void
 147pxa_init_irq_common(struct device_node *node, int irq_nr,
 148                    int (*fn)(struct irq_data *, unsigned int))
 149{
 150        int n;
 151
 152        pxa_internal_irq_nr = irq_nr;
 153        pxa_irq_domain = irq_domain_add_legacy(node, irq_nr,
 154                                               PXA_IRQ(0), 0,
 155                                               &pxa_irq_ops, NULL);
 156        if (!pxa_irq_domain)
 157                panic("Unable to add PXA IRQ domain\n");
 158        irq_set_default_host(pxa_irq_domain);
 159
 160        for (n = 0; n < irq_nr; n += 32) {
 161                void __iomem *base = irq_base(n >> 5);
 162
 163                __raw_writel(0, base + ICMR);   /* disable all IRQs */
 164                __raw_writel(0, base + ICLR);   /* all IRQs are IRQ, not FIQ */
 165        }
 166        /* only unmasked interrupts kick us out of idle */
 167        __raw_writel(1, irq_base(0) + ICCR);
 168
 169        pxa_internal_irq_chip.irq_set_wake = fn;
 170}
 171
 172void __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
 173{
 174        BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
 175
 176        pxa_irq_base = io_p2v(0x40d00000);
 177        cpu_has_ipr = !cpu_is_pxa25x();
 178        pxa_init_irq_common(NULL, irq_nr, fn);
 179}
 180
 181#ifdef CONFIG_PM
 182static unsigned long saved_icmr[MAX_INTERNAL_IRQS/32];
 183static unsigned long saved_ipr[MAX_INTERNAL_IRQS];
 184
 185static int pxa_irq_suspend(void)
 186{
 187        int i;
 188
 189        for (i = 0; i < pxa_internal_irq_nr / 32; i++) {
 190                void __iomem *base = irq_base(i);
 191
 192                saved_icmr[i] = __raw_readl(base + ICMR);
 193                __raw_writel(0, base + ICMR);
 194        }
 195
 196        if (cpu_has_ipr) {
 197                for (i = 0; i < pxa_internal_irq_nr; i++)
 198                        saved_ipr[i] = __raw_readl(pxa_irq_base + IPR(i));
 199        }
 200
 201        return 0;
 202}
 203
 204static void pxa_irq_resume(void)
 205{
 206        int i;
 207
 208        for (i = 0; i < pxa_internal_irq_nr / 32; i++) {
 209                void __iomem *base = irq_base(i);
 210
 211                __raw_writel(saved_icmr[i], base + ICMR);
 212                __raw_writel(0, base + ICLR);
 213        }
 214
 215        if (cpu_has_ipr)
 216                for (i = 0; i < pxa_internal_irq_nr; i++)
 217                        __raw_writel(saved_ipr[i], pxa_irq_base + IPR(i));
 218
 219        __raw_writel(1, pxa_irq_base + ICCR);
 220}
 221#else
 222#define pxa_irq_suspend         NULL
 223#define pxa_irq_resume          NULL
 224#endif
 225
 226struct syscore_ops pxa_irq_syscore_ops = {
 227        .suspend        = pxa_irq_suspend,
 228        .resume         = pxa_irq_resume,
 229};
 230
 231#ifdef CONFIG_OF
 232static const struct of_device_id intc_ids[] __initconst = {
 233        { .compatible = "marvell,pxa-intc", },
 234        {}
 235};
 236
 237void __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int))
 238{
 239        struct device_node *node;
 240        struct resource res;
 241        int ret;
 242
 243        node = of_find_matching_node(NULL, intc_ids);
 244        if (!node) {
 245                pr_err("Failed to find interrupt controller in arch-pxa\n");
 246                return;
 247        }
 248
 249        ret = of_property_read_u32(node, "marvell,intc-nr-irqs",
 250                                   &pxa_internal_irq_nr);
 251        if (ret) {
 252                pr_err("Not found marvell,intc-nr-irqs property\n");
 253                return;
 254        }
 255
 256        ret = of_address_to_resource(node, 0, &res);
 257        if (ret < 0) {
 258                pr_err("No registers defined for node\n");
 259                return;
 260        }
 261        pxa_irq_base = io_p2v(res.start);
 262
 263        if (of_find_property(node, "marvell,intc-priority", NULL))
 264                cpu_has_ipr = 1;
 265
 266        ret = irq_alloc_descs(-1, 0, pxa_internal_irq_nr, 0);
 267        if (ret < 0) {
 268                pr_err("Failed to allocate IRQ numbers\n");
 269                return;
 270        }
 271
 272        pxa_init_irq_common(node, pxa_internal_irq_nr, fn);
 273}
 274#endif /* CONFIG_OF */
 275