1717#include <linux/irqchip/chained_irq.h>
1818#include <linux/of_address.h>
1919#include <linux/of_irq.h>
20+ #include <linux/interrupt.h>
2021
2122#define APB_INT_ENABLE_L 0x00
2223#define APB_INT_ENABLE_H 0x04
2627#define APB_INT_FINALSTATUS_H 0x34
2728#define APB_INT_BASE_OFFSET 0x04
2829
29- static void dw_apb_ictl_handler (struct irq_desc * desc )
30+ /* irq domain of the primary interrupt controller. */
31+ static struct irq_domain * dw_apb_ictl_irq_domain ;
32+
33+ static void __irq_entry dw_apb_ictl_handle_irq (struct pt_regs * regs )
34+ {
35+ struct irq_domain * d = dw_apb_ictl_irq_domain ;
36+ int n ;
37+
38+ for (n = 0 ; n < d -> revmap_size ; n += 32 ) {
39+ struct irq_chip_generic * gc = irq_get_domain_generic_chip (d , n );
40+ u32 stat = readl_relaxed (gc -> reg_base + APB_INT_FINALSTATUS_L );
41+
42+ while (stat ) {
43+ u32 hwirq = ffs (stat ) - 1 ;
44+
45+ handle_domain_irq (d , hwirq , regs );
46+ stat &= ~BIT (hwirq );
47+ }
48+ }
49+ }
50+
51+ static void dw_apb_ictl_handle_irq_cascaded (struct irq_desc * desc )
3052{
3153 struct irq_domain * d = irq_desc_get_handler_data (desc );
3254 struct irq_chip * chip = irq_desc_get_chip (desc );
@@ -43,13 +65,37 @@ static void dw_apb_ictl_handler(struct irq_desc *desc)
4365 u32 virq = irq_find_mapping (d , gc -> irq_base + hwirq );
4466
4567 generic_handle_irq (virq );
46- stat &= ~( 1 << hwirq );
68+ stat &= ~BIT ( hwirq );
4769 }
4870 }
4971
5072 chained_irq_exit (chip , desc );
5173}
5274
75+ static int dw_apb_ictl_irq_domain_alloc (struct irq_domain * domain , unsigned int virq ,
76+ unsigned int nr_irqs , void * arg )
77+ {
78+ int i , ret ;
79+ irq_hw_number_t hwirq ;
80+ unsigned int type = IRQ_TYPE_NONE ;
81+ struct irq_fwspec * fwspec = arg ;
82+
83+ ret = irq_domain_translate_onecell (domain , fwspec , & hwirq , & type );
84+ if (ret )
85+ return ret ;
86+
87+ for (i = 0 ; i < nr_irqs ; i ++ )
88+ irq_map_generic_chip (domain , virq + i , hwirq + i );
89+
90+ return 0 ;
91+ }
92+
93+ static const struct irq_domain_ops dw_apb_ictl_irq_domain_ops = {
94+ .translate = irq_domain_translate_onecell ,
95+ .alloc = dw_apb_ictl_irq_domain_alloc ,
96+ .free = irq_domain_free_irqs_top ,
97+ };
98+
5399#ifdef CONFIG_PM
54100static void dw_apb_ictl_resume (struct irq_data * d )
55101{
@@ -68,19 +114,27 @@ static void dw_apb_ictl_resume(struct irq_data *d)
68114static int __init dw_apb_ictl_init (struct device_node * np ,
69115 struct device_node * parent )
70116{
117+ const struct irq_domain_ops * domain_ops ;
71118 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN ;
72119 struct resource r ;
73120 struct irq_domain * domain ;
74121 struct irq_chip_generic * gc ;
75122 void __iomem * iobase ;
76- int ret , nrirqs , irq , i ;
123+ int ret , nrirqs , parent_irq , i ;
77124 u32 reg ;
78125
79- /* Map the parent interrupt for the chained handler */
80- irq = irq_of_parse_and_map (np , 0 );
81- if (irq <= 0 ) {
82- pr_err ("%pOF: unable to parse irq\n" , np );
83- return - EINVAL ;
126+ if (!parent ) {
127+ /* Used as the primary interrupt controller */
128+ parent_irq = 0 ;
129+ domain_ops = & dw_apb_ictl_irq_domain_ops ;
130+ } else {
131+ /* Map the parent interrupt for the chained handler */
132+ parent_irq = irq_of_parse_and_map (np , 0 );
133+ if (parent_irq <= 0 ) {
134+ pr_err ("%pOF: unable to parse irq\n" , np );
135+ return - EINVAL ;
136+ }
137+ domain_ops = & irq_generic_chip_ops ;
84138 }
85139
86140 ret = of_address_to_resource (np , 0 , & r );
@@ -120,8 +174,7 @@ static int __init dw_apb_ictl_init(struct device_node *np,
120174 else
121175 nrirqs = fls (readl_relaxed (iobase + APB_INT_ENABLE_L ));
122176
123- domain = irq_domain_add_linear (np , nrirqs ,
124- & irq_generic_chip_ops , NULL );
177+ domain = irq_domain_add_linear (np , nrirqs , domain_ops , NULL );
125178 if (!domain ) {
126179 pr_err ("%pOF: unable to add irq domain\n" , np );
127180 ret = - ENOMEM ;
@@ -146,7 +199,13 @@ static int __init dw_apb_ictl_init(struct device_node *np,
146199 gc -> chip_types [0 ].chip .irq_resume = dw_apb_ictl_resume ;
147200 }
148201
149- irq_set_chained_handler_and_data (irq , dw_apb_ictl_handler , domain );
202+ if (parent_irq ) {
203+ irq_set_chained_handler_and_data (parent_irq ,
204+ dw_apb_ictl_handle_irq_cascaded , domain );
205+ } else {
206+ dw_apb_ictl_irq_domain = domain ;
207+ set_handle_irq (dw_apb_ictl_handle_irq );
208+ }
150209
151210 return 0 ;
152211
0 commit comments