Path: blob/master/arch/arm/mach-exynos4/irq-combiner.c
10817 views
/* linux/arch/arm/mach-exynos4/irq-combiner.c1*2* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.3* http://www.samsung.com4*5* Based on arch/arm/common/gic.c6*7* IRQ COMBINER support8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License version 2 as11* published by the Free Software Foundation.12*/1314#include <linux/io.h>1516#include <asm/mach/irq.h>1718#define COMBINER_ENABLE_SET 0x019#define COMBINER_ENABLE_CLEAR 0x420#define COMBINER_INT_STATUS 0xC2122static DEFINE_SPINLOCK(irq_controller_lock);2324struct combiner_chip_data {25unsigned int irq_offset;26unsigned int irq_mask;27void __iomem *base;28};2930static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];3132static inline void __iomem *combiner_base(struct irq_data *data)33{34struct combiner_chip_data *combiner_data =35irq_data_get_irq_chip_data(data);3637return combiner_data->base;38}3940static void combiner_mask_irq(struct irq_data *data)41{42u32 mask = 1 << (data->irq % 32);4344__raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR);45}4647static void combiner_unmask_irq(struct irq_data *data)48{49u32 mask = 1 << (data->irq % 32);5051__raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET);52}5354static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)55{56struct combiner_chip_data *chip_data = irq_get_handler_data(irq);57struct irq_chip *chip = irq_get_chip(irq);58unsigned int cascade_irq, combiner_irq;59unsigned long status;6061chained_irq_enter(chip, desc);6263spin_lock(&irq_controller_lock);64status = __raw_readl(chip_data->base + COMBINER_INT_STATUS);65spin_unlock(&irq_controller_lock);66status &= chip_data->irq_mask;6768if (status == 0)69goto out;7071combiner_irq = __ffs(status);7273cascade_irq = combiner_irq + (chip_data->irq_offset & ~31);74if (unlikely(cascade_irq >= NR_IRQS))75do_bad_IRQ(cascade_irq, desc);76else77generic_handle_irq(cascade_irq);7879out:80chained_irq_exit(chip, desc);81}8283static struct irq_chip combiner_chip = {84.name = "COMBINER",85.irq_mask = combiner_mask_irq,86.irq_unmask = combiner_unmask_irq,87};8889void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq)90{91if (combiner_nr >= MAX_COMBINER_NR)92BUG();93if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)94BUG();95irq_set_chained_handler(irq, combiner_handle_cascade_irq);96}9798void __init combiner_init(unsigned int combiner_nr, void __iomem *base,99unsigned int irq_start)100{101unsigned int i;102103if (combiner_nr >= MAX_COMBINER_NR)104BUG();105106combiner_data[combiner_nr].base = base;107combiner_data[combiner_nr].irq_offset = irq_start;108combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3);109110/* Disable all interrupts */111112__raw_writel(combiner_data[combiner_nr].irq_mask,113base + COMBINER_ENABLE_CLEAR);114115/* Setup the Linux IRQ subsystem */116117for (i = irq_start; i < combiner_data[combiner_nr].irq_offset118+ MAX_IRQ_IN_COMBINER; i++) {119irq_set_chip_and_handler(i, &combiner_chip, handle_level_irq);120irq_set_chip_data(i, &combiner_data[combiner_nr]);121set_irq_flags(i, IRQF_VALID | IRQF_PROBE);122}123}124125126