1 /* linux/arch/arm/mach-s5pv310/irq-combiner.c
3 * Copyright (c) 2010 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
= get_irq_data(irq
);
58 struct irq_chip
*chip
= get_irq_chip(irq
);
59 unsigned int cascade_irq
, combiner_irq
;
62 /* primary controller ack'ing */
63 chip
->irq_ack(&desc
->irq_data
);
65 spin_lock(&irq_controller_lock
);
66 status
= __raw_readl(chip_data
->base
+ COMBINER_INT_STATUS
);
67 spin_unlock(&irq_controller_lock
);
68 status
&= chip_data
->irq_mask
;
73 combiner_irq
= __ffs(status
);
75 cascade_irq
= combiner_irq
+ (chip_data
->irq_offset
& ~31);
76 if (unlikely(cascade_irq
>= NR_IRQS
))
77 do_bad_IRQ(cascade_irq
, desc
);
79 generic_handle_irq(cascade_irq
);
82 /* primary controller unmasking */
83 chip
->irq_unmask(&desc
->irq_data
);
86 static struct irq_chip combiner_chip
= {
88 .irq_mask
= combiner_mask_irq
,
89 .irq_unmask
= combiner_unmask_irq
,
92 void __init
combiner_cascade_irq(unsigned int combiner_nr
, unsigned int irq
)
94 if (combiner_nr
>= MAX_COMBINER_NR
)
96 if (set_irq_data(irq
, &combiner_data
[combiner_nr
]) != 0)
98 set_irq_chained_handler(irq
, combiner_handle_cascade_irq
);
101 void __init
combiner_init(unsigned int combiner_nr
, void __iomem
*base
,
102 unsigned int irq_start
)
106 if (combiner_nr
>= MAX_COMBINER_NR
)
109 combiner_data
[combiner_nr
].base
= base
;
110 combiner_data
[combiner_nr
].irq_offset
= irq_start
;
111 combiner_data
[combiner_nr
].irq_mask
= 0xff << ((combiner_nr
% 4) << 3);
113 /* Disable all interrupts */
115 __raw_writel(combiner_data
[combiner_nr
].irq_mask
,
116 base
+ COMBINER_ENABLE_CLEAR
);
118 /* Setup the Linux IRQ subsystem */
120 for (i
= irq_start
; i
< combiner_data
[combiner_nr
].irq_offset
121 + MAX_IRQ_IN_COMBINER
; i
++) {
122 set_irq_chip(i
, &combiner_chip
);
123 set_irq_chip_data(i
, &combiner_data
[combiner_nr
]);
124 set_irq_handler(i
, handle_level_irq
);
125 set_irq_flags(i
, IRQF_VALID
| IRQF_PROBE
);