| Kukjin Kim | c81a24f | 2011-02-14 16:10:55 +0900 | [diff] [blame] | 1 | /* linux/arch/arm/mach-exynos4/irq-combiner.c | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 2 |  * | 
| Kukjin Kim | c81a24f | 2011-02-14 16:10:55 +0900 | [diff] [blame] | 3 |  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 4 |  *		http://www.samsung.com | 
 | 5 |  * | 
 | 6 |  * Based on arch/arm/common/gic.c | 
 | 7 |  * | 
 | 8 |  * IRQ COMBINER support | 
 | 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 |  | 
 | 15 | #include <linux/io.h> | 
 | 16 |  | 
 | 17 | #include <asm/mach/irq.h> | 
 | 18 |  | 
 | 19 | #define COMBINER_ENABLE_SET	0x0 | 
 | 20 | #define COMBINER_ENABLE_CLEAR	0x4 | 
 | 21 | #define COMBINER_INT_STATUS	0xC | 
 | 22 |  | 
 | 23 | static DEFINE_SPINLOCK(irq_controller_lock); | 
 | 24 |  | 
 | 25 | struct combiner_chip_data { | 
 | 26 | 	unsigned int irq_offset; | 
| Changhwan Youn | 85140ad | 2010-11-29 17:05:16 +0900 | [diff] [blame] | 27 | 	unsigned int irq_mask; | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 28 | 	void __iomem *base; | 
 | 29 | }; | 
 | 30 |  | 
 | 31 | static struct combiner_chip_data combiner_data[MAX_COMBINER_NR]; | 
 | 32 |  | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 33 | static inline void __iomem *combiner_base(struct irq_data *data) | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 34 | { | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 35 | 	struct combiner_chip_data *combiner_data = | 
 | 36 | 		irq_data_get_irq_chip_data(data); | 
 | 37 |  | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 38 | 	return combiner_data->base; | 
 | 39 | } | 
 | 40 |  | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 41 | static void combiner_mask_irq(struct irq_data *data) | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 42 | { | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 43 | 	u32 mask = 1 << (data->irq % 32); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 44 |  | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 45 | 	__raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 46 | } | 
 | 47 |  | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 48 | static void combiner_unmask_irq(struct irq_data *data) | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 49 | { | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 50 | 	u32 mask = 1 << (data->irq % 32); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 51 |  | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 52 | 	__raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 53 | } | 
 | 54 |  | 
 | 55 | static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) | 
 | 56 | { | 
| Thomas Gleixner | 6845664a | 2011-03-24 13:25:22 +0100 | [diff] [blame] | 57 | 	struct combiner_chip_data *chip_data = irq_get_handler_data(irq); | 
 | 58 | 	struct irq_chip *chip = irq_get_chip(irq); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 59 | 	unsigned int cascade_irq, combiner_irq; | 
 | 60 | 	unsigned long status; | 
 | 61 |  | 
| Will Deacon | 0f43563 | 2011-02-21 14:37:43 +0000 | [diff] [blame] | 62 | 	chained_irq_enter(chip, desc); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 63 |  | 
 | 64 | 	spin_lock(&irq_controller_lock); | 
 | 65 | 	status = __raw_readl(chip_data->base + COMBINER_INT_STATUS); | 
 | 66 | 	spin_unlock(&irq_controller_lock); | 
| Changhwan Youn | 85140ad | 2010-11-29 17:05:16 +0900 | [diff] [blame] | 67 | 	status &= chip_data->irq_mask; | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 68 |  | 
 | 69 | 	if (status == 0) | 
 | 70 | 		goto out; | 
 | 71 |  | 
| Changhwan Youn | 0c0f909 | 2010-09-29 20:31:42 +0900 | [diff] [blame] | 72 | 	combiner_irq = __ffs(status); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 73 |  | 
 | 74 | 	cascade_irq = combiner_irq + (chip_data->irq_offset & ~31); | 
 | 75 | 	if (unlikely(cascade_irq >= NR_IRQS)) | 
 | 76 | 		do_bad_IRQ(cascade_irq, desc); | 
 | 77 | 	else | 
 | 78 | 		generic_handle_irq(cascade_irq); | 
 | 79 |  | 
 | 80 |  out: | 
| Will Deacon | 0f43563 | 2011-02-21 14:37:43 +0000 | [diff] [blame] | 81 | 	chained_irq_exit(chip, desc); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 82 | } | 
 | 83 |  | 
 | 84 | static struct irq_chip combiner_chip = { | 
 | 85 | 	.name		= "COMBINER", | 
| Lennert Buytenhek | bb0b237 | 2010-12-14 22:55:26 +0100 | [diff] [blame] | 86 | 	.irq_mask	= combiner_mask_irq, | 
 | 87 | 	.irq_unmask	= combiner_unmask_irq, | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 88 | }; | 
 | 89 |  | 
 | 90 | void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq) | 
 | 91 | { | 
 | 92 | 	if (combiner_nr >= MAX_COMBINER_NR) | 
 | 93 | 		BUG(); | 
| Thomas Gleixner | 6845664a | 2011-03-24 13:25:22 +0100 | [diff] [blame] | 94 | 	if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0) | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 95 | 		BUG(); | 
| Thomas Gleixner | 6845664a | 2011-03-24 13:25:22 +0100 | [diff] [blame] | 96 | 	irq_set_chained_handler(irq, combiner_handle_cascade_irq); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 97 | } | 
 | 98 |  | 
 | 99 | void __init combiner_init(unsigned int combiner_nr, void __iomem *base, | 
 | 100 | 			  unsigned int irq_start) | 
 | 101 | { | 
 | 102 | 	unsigned int i; | 
 | 103 |  | 
 | 104 | 	if (combiner_nr >= MAX_COMBINER_NR) | 
 | 105 | 		BUG(); | 
 | 106 |  | 
 | 107 | 	combiner_data[combiner_nr].base = base; | 
 | 108 | 	combiner_data[combiner_nr].irq_offset = irq_start; | 
| Changhwan Youn | 85140ad | 2010-11-29 17:05:16 +0900 | [diff] [blame] | 109 | 	combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 110 |  | 
 | 111 | 	/* Disable all interrupts */ | 
 | 112 |  | 
| Changhwan Youn | 85140ad | 2010-11-29 17:05:16 +0900 | [diff] [blame] | 113 | 	__raw_writel(combiner_data[combiner_nr].irq_mask, | 
 | 114 | 		     base + COMBINER_ENABLE_CLEAR); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 115 |  | 
 | 116 | 	/* Setup the Linux IRQ subsystem */ | 
 | 117 |  | 
 | 118 | 	for (i = irq_start; i < combiner_data[combiner_nr].irq_offset | 
 | 119 | 				+ MAX_IRQ_IN_COMBINER; i++) { | 
| Thomas Gleixner | f38c02f | 2011-03-24 13:35:09 +0100 | [diff] [blame] | 120 | 		irq_set_chip_and_handler(i, &combiner_chip, handle_level_irq); | 
| Thomas Gleixner | 9323f261 | 2011-03-24 13:29:39 +0100 | [diff] [blame] | 121 | 		irq_set_chip_data(i, &combiner_data[combiner_nr]); | 
| Changhwan Youn | 84bbc16 | 2010-07-16 12:12:07 +0900 | [diff] [blame] | 122 | 		set_irq_flags(i, IRQF_VALID | IRQF_PROBE); | 
 | 123 | 	} | 
 | 124 | } |