linux/drivers/irqchip/exynos-combiner.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
   3 *              http://www.samsung.com
   4 *
   5 * Combiner irqchip for EXYNOS
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11#include <linux/err.h>
  12#include <linux/export.h>
  13#include <linux/init.h>
  14#include <linux/io.h>
  15#include <linux/irqdomain.h>
  16#include <linux/of_address.h>
  17#include <linux/of_irq.h>
  18#include <asm/mach/irq.h>
  19
  20#include <plat/cpu.h>
  21
  22#include "irqchip.h"
  23
  24#define COMBINER_ENABLE_SET     0x0
  25#define COMBINER_ENABLE_CLEAR   0x4
  26#define COMBINER_INT_STATUS     0xC
  27
  28static DEFINE_SPINLOCK(irq_controller_lock);
  29
  30struct combiner_chip_data {
  31        unsigned int irq_offset;
  32        unsigned int irq_mask;
  33        void __iomem *base;
  34};
  35
  36static struct irq_domain *combiner_irq_domain;
  37static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
  38
  39static inline void __iomem *combiner_base(struct irq_data *data)
  40{
  41        struct combiner_chip_data *combiner_data =
  42                irq_data_get_irq_chip_data(data);
  43
  44        return combiner_data->base;
  45}
  46
  47static void combiner_mask_irq(struct irq_data *data)
  48{
  49        u32 mask = 1 << (data->hwirq % 32);
  50
  51        __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR);
  52}
  53
  54static void combiner_unmask_irq(struct irq_data *data)
  55{
  56        u32 mask = 1 << (data->hwirq % 32);
  57
  58        __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET);
  59}
  60
  61static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
  62{
  63        struct combiner_chip_data *chip_data = irq_get_handler_data(irq);
  64        struct irq_chip *chip = irq_get_chip(irq);
  65        unsigned int cascade_irq, combiner_irq;
  66        unsigned long status;
  67
  68        chained_irq_enter(chip, desc);
  69
  70        spin_lock(&irq_controller_lock);
  71        status = __raw_readl(chip_data->base + COMBINER_INT_STATUS);
  72        spin_unlock(&irq_controller_lock);
  73        status &= chip_data->irq_mask;
  74
  75        if (status == 0)
  76                goto out;
  77
  78        combiner_irq = __ffs(status);
  79
  80        cascade_irq = combiner_irq + (chip_data->irq_offset & ~31);
  81        if (unlikely(cascade_irq >= NR_IRQS))
  82                do_bad_IRQ(cascade_irq, desc);
  83        else
  84                generic_handle_irq(cascade_irq);
  85
  86 out:
  87        chained_irq_exit(chip, desc);
  88}
  89
  90static struct irq_chip combiner_chip = {
  91        .name           = "COMBINER",
  92        .irq_mask       = combiner_mask_irq,
  93        .irq_unmask     = combiner_unmask_irq,
  94};
  95
  96static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq)
  97{
  98        unsigned int max_nr;
  99
 100        if (soc_is_exynos5250())
 101                max_nr = EXYNOS5_MAX_COMBINER_NR;
 102        else
 103                max_nr = EXYNOS4_MAX_COMBINER_NR;
 104
 105        if (combiner_nr >= max_nr)
 106                BUG();
 107        if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
 108                BUG();
 109        irq_set_chained_handler(irq, combiner_handle_cascade_irq);
 110}
 111
 112static void __init combiner_init_one(unsigned int combiner_nr,
 113                                     void __iomem *base)
 114{
 115        combiner_data[combiner_nr].base = base;
 116        combiner_data[combiner_nr].irq_offset = irq_find_mapping(
 117                combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER);
 118        combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3);
 119
 120        /* Disable all interrupts */
 121        __raw_writel(combiner_data[combiner_nr].irq_mask,
 122                     base + COMBINER_ENABLE_CLEAR);
 123}
 124
 125#ifdef CONFIG_OF
 126static int combiner_irq_domain_xlate(struct irq_domain *d,
 127                                     struct device_node *controller,
 128                                     const u32 *intspec, unsigned int intsize,
 129                                     unsigned long *out_hwirq,
 130                                     unsigned int *out_type)
 131{
 132        if (d->of_node != controller)
 133                return -EINVAL;
 134
 135        if (intsize < 2)
 136                return -EINVAL;
 137
 138        *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1];
 139        *out_type = 0;
 140
 141        return 0;
 142}
 143#else
 144static int combiner_irq_domain_xlate(struct irq_domain *d,
 145                                     struct device_node *controller,
 146                                     const u32 *intspec, unsigned int intsize,
 147                                     unsigned long *out_hwirq,
 148                                     unsigned int *out_type)
 149{
 150        return -EINVAL;
 151}
 152#endif
 153
 154static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
 155                                   irq_hw_number_t hw)
 156{
 157        irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
 158        irq_set_chip_data(irq, &combiner_data[hw >> 3]);
 159        set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 160
 161        return 0;
 162}
 163
 164static struct irq_domain_ops combiner_irq_domain_ops = {
 165        .xlate  = combiner_irq_domain_xlate,
 166        .map    = combiner_irq_domain_map,
 167};
 168
 169void __init combiner_init(void __iomem *combiner_base,
 170                          struct device_node *np)
 171{
 172        int i, irq, irq_base;
 173        unsigned int max_nr, nr_irq;
 174
 175        if (np) {
 176                if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
 177                        pr_warning("%s: number of combiners not specified, "
 178                                "setting default as %d.\n",
 179                                __func__, EXYNOS4_MAX_COMBINER_NR);
 180                        max_nr = EXYNOS4_MAX_COMBINER_NR;
 181                }
 182        } else {
 183                max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR :
 184                                                EXYNOS4_MAX_COMBINER_NR;
 185        }
 186        nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
 187
 188        irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
 189        if (IS_ERR_VALUE(irq_base)) {
 190                irq_base = COMBINER_IRQ(0, 0);
 191                pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base);
 192        }
 193
 194        combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0,
 195                                &combiner_irq_domain_ops, &combiner_data);
 196        if (WARN_ON(!combiner_irq_domain)) {
 197                pr_warning("%s: irq domain init failed\n", __func__);
 198                return;
 199        }
 200
 201        for (i = 0; i < max_nr; i++) {
 202                combiner_init_one(i, combiner_base + (i >> 2) * 0x10);
 203                irq = IRQ_SPI(i);
 204#ifdef CONFIG_OF
 205                if (np)
 206                        irq = irq_of_parse_and_map(np, i);
 207#endif
 208                combiner_cascade_irq(i, irq);
 209        }
 210}
 211
 212#ifdef CONFIG_OF
 213static int __init combiner_of_init(struct device_node *np,
 214                                   struct device_node *parent)
 215{
 216        void __iomem *combiner_base;
 217
 218        combiner_base = of_iomap(np, 0);
 219        if (!combiner_base) {
 220                pr_err("%s: failed to map combiner registers\n", __func__);
 221                return -ENXIO;
 222        }
 223
 224        combiner_init(combiner_base, np);
 225
 226        return 0;
 227}
 228IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner",
 229                combiner_of_init);
 230#endif
 231
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.