1 /* linux/arch/arm/mach-exynos4/irq-combiner.c
3 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
6 * Based on arch/arm/common/gic.c
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.
17 #include <asm/mach/irq.h>
19 #define COMBINER_ENABLE_SET 0x0
20 #define COMBINER_ENABLE_CLEAR 0x4
21 #define COMBINER_INT_STATUS 0xC
23 static DEFINE_SPINLOCK(irq_controller_lock
);
25 struct combiner_chip_data
{
26 unsigned int irq_offset
;
27 unsigned int irq_mask
;
31 static struct combiner_chip_data combiner_data
[MAX_COMBINER_NR
];
33 static inline void __iomem
*combiner_base(struct irq_data
*data
)
35 struct combiner_chip_data
*combiner_data
=
36 irq_data_get_irq_chip_data(data
);
38 return combiner_data
->base
;
41 static void combiner_mask_irq(struct irq_data
*data
)
43 u32 mask
= 1 << (data
->irq
% 32);
45 __raw_writel(mask
, combiner_base(data
) + COMBINER_ENABLE_CLEAR
);
48 static void combiner_unmask_irq(struct irq_data
*data
)
50 u32 mask
= 1 << (data
->irq
% 32);
52 __raw_writel(mask
, combiner_base(data
) + COMBINER_ENABLE_SET
);
55 static void combiner_handle_cascade_irq(unsigned int irq
, struct irq_desc
*desc
)
57 struct combiner_chip_data
*chip_data
= irq_get_handler_data(irq
);
58 struct irq_chip
*chip
= irq_get_chip(irq
);
59 unsigned int cascade_irq
, combiner_irq
;
62 chained_irq_enter(chip
, desc
);
64 spin_lock(&irq_controller_lock
);
65 status
= __raw_readl(chip_data
->base
+ COMBINER_INT_STATUS
);
66 spin_unlock(&irq_controller_lock
);
67 status
&= chip_data
->irq_mask
;
72 combiner_irq
= __ffs(status
);
74 cascade_irq
= combiner_irq
+ (chip_data
->irq_offset
& ~31);
75 if (unlikely(cascade_irq
>= NR_IRQS
))
76 do_bad_IRQ(cascade_irq
, desc
);
78 generic_handle_irq(cascade_irq
);
81 chained_irq_exit(chip
, desc
);
84 static struct irq_chip combiner_chip
= {
86 .irq_mask
= combiner_mask_irq
,
87 .irq_unmask
= combiner_unmask_irq
,
90 void __init
combiner_cascade_irq(unsigned int combiner_nr
, unsigned int irq
)
92 if (combiner_nr
>= MAX_COMBINER_NR
)
94 if (irq_set_handler_data(irq
, &combiner_data
[combiner_nr
]) != 0)
96 irq_set_chained_handler(irq
, combiner_handle_cascade_irq
);
99 void __init
combiner_init(unsigned int combiner_nr
, void __iomem
*base
,
100 unsigned int irq_start
)
104 if (combiner_nr
>= MAX_COMBINER_NR
)
107 combiner_data
[combiner_nr
].base
= base
;
108 combiner_data
[combiner_nr
].irq_offset
= irq_start
;
109 combiner_data
[combiner_nr
].irq_mask
= 0xff << ((combiner_nr
% 4) << 3);
111 /* Disable all interrupts */
113 __raw_writel(combiner_data
[combiner_nr
].irq_mask
,
114 base
+ COMBINER_ENABLE_CLEAR
);
116 /* Setup the Linux IRQ subsystem */
118 for (i
= irq_start
; i
< combiner_data
[combiner_nr
].irq_offset
119 + MAX_IRQ_IN_COMBINER
; i
++) {
120 irq_set_chip_and_handler(i
, &combiner_chip
, handle_level_irq
);
121 irq_set_chip_data(i
, &combiner_data
[combiner_nr
]);
122 set_irq_flags(i
, IRQF_VALID
| IRQF_PROBE
);