Blame view

drivers/irqchip/irq-dw-apb-ictl.c 4.33 KB
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * Synopsys DW APB ICTL irqchip driver.
   *
   * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
   *
   * based on GPL'ed 2.6 kernel sources
   *  (c) Marvell International Ltd.
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2.  This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
  
  #include <linux/io.h>
  #include <linux/irq.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
16
  #include <linux/irqchip.h>
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
17
18
19
  #include <linux/irqchip/chained_irq.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
20
21
22
23
24
25
  #define APB_INT_ENABLE_L	0x00
  #define APB_INT_ENABLE_H	0x04
  #define APB_INT_MASK_L		0x08
  #define APB_INT_MASK_H		0x0c
  #define APB_INT_FINALSTATUS_L	0x30
  #define APB_INT_FINALSTATUS_H	0x34
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
26
  #define APB_INT_BASE_OFFSET	0x04
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
27

bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
28
  static void dw_apb_ictl_handler(struct irq_desc *desc)
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
29
  {
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
30
31
  	struct irq_domain *d = irq_desc_get_handler_data(desc);
  	struct irq_chip *chip = irq_desc_get_chip(desc);
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
32
33
34
  	int n;
  
  	chained_irq_enter(chip, desc);
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
35
36
37
  	for (n = 0; n < d->revmap_size; n += 32) {
  		struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, n);
  		u32 stat = readl_relaxed(gc->reg_base + APB_INT_FINALSTATUS_L);
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
38
39
  		while (stat) {
  			u32 hwirq = ffs(stat) - 1;
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
40
41
42
  			u32 virq = irq_find_mapping(d, gc->irq_base + hwirq);
  
  			generic_handle_irq(virq);
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
43
44
45
46
47
48
  			stat &= ~(1 << hwirq);
  		}
  	}
  
  	chained_irq_exit(chip, desc);
  }
1655b0530   Jisheng Zhang   irqchip: dw-apb-i...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  #ifdef CONFIG_PM
  static void dw_apb_ictl_resume(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
  
  	irq_gc_lock(gc);
  	writel_relaxed(~0, gc->reg_base + ct->regs.enable);
  	writel_relaxed(*ct->mask_cache, gc->reg_base + ct->regs.mask);
  	irq_gc_unlock(gc);
  }
  #else
  #define dw_apb_ictl_resume	NULL
  #endif /* CONFIG_PM */
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
63
64
65
66
67
68
69
70
  static int __init dw_apb_ictl_init(struct device_node *np,
  				   struct device_node *parent)
  {
  	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
  	struct resource r;
  	struct irq_domain *domain;
  	struct irq_chip_generic *gc;
  	void __iomem *iobase;
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
71
  	int ret, nrirqs, irq, i;
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  	u32 reg;
  
  	/* Map the parent interrupt for the chained handler */
  	irq = irq_of_parse_and_map(np, 0);
  	if (irq <= 0) {
  		pr_err("%s: unable to parse irq
  ", np->full_name);
  		return -EINVAL;
  	}
  
  	ret = of_address_to_resource(np, 0, &r);
  	if (ret) {
  		pr_err("%s: unable to get resource
  ", np->full_name);
  		return ret;
  	}
  
  	if (!request_mem_region(r.start, resource_size(&r), np->full_name)) {
  		pr_err("%s: unable to request mem region
  ", np->full_name);
  		return -ENOMEM;
  	}
  
  	iobase = ioremap(r.start, resource_size(&r));
  	if (!iobase) {
  		pr_err("%s: unable to map resource
  ", np->full_name);
  		ret = -ENOMEM;
  		goto err_release;
  	}
  
  	/*
  	 * DW IP can be configured to allow 2-64 irqs. We can determine
  	 * the number of irqs supported by writing into enable register
  	 * and look for bits not set, as corresponding flip-flops will
  	 * have been removed by sythesis tool.
  	 */
  
  	/* mask and enable all interrupts */
8876ce7d1   Jisheng Zhang   irqchip: dw-apb-i...
111
112
113
114
  	writel_relaxed(~0, iobase + APB_INT_MASK_L);
  	writel_relaxed(~0, iobase + APB_INT_MASK_H);
  	writel_relaxed(~0, iobase + APB_INT_ENABLE_L);
  	writel_relaxed(~0, iobase + APB_INT_ENABLE_H);
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
115

8876ce7d1   Jisheng Zhang   irqchip: dw-apb-i...
116
  	reg = readl_relaxed(iobase + APB_INT_ENABLE_H);
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
117
118
119
  	if (reg)
  		nrirqs = 32 + fls(reg);
  	else
8876ce7d1   Jisheng Zhang   irqchip: dw-apb-i...
120
  		nrirqs = fls(readl_relaxed(iobase + APB_INT_ENABLE_L));
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
121
122
123
124
125
126
127
128
129
  
  	domain = irq_domain_add_linear(np, nrirqs,
  				       &irq_generic_chip_ops, NULL);
  	if (!domain) {
  		pr_err("%s: unable to add irq domain
  ", np->full_name);
  		ret = -ENOMEM;
  		goto err_unmap;
  	}
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
130
131
  	ret = irq_alloc_domain_generic_chips(domain, 32, 1, np->name,
  					     handle_level_irq, clr, 0,
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
132
133
134
135
136
137
  					     IRQ_GC_INIT_MASK_CACHE);
  	if (ret) {
  		pr_err("%s: unable to alloc irq domain gc
  ", np->full_name);
  		goto err_unmap;
  	}
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
138
139
140
141
142
143
144
145
  	for (i = 0; i < DIV_ROUND_UP(nrirqs, 32); i++) {
  		gc = irq_get_domain_generic_chip(domain, i * 32);
  		gc->reg_base = iobase + i * APB_INT_BASE_OFFSET;
  		gc->chip_types[0].regs.mask = APB_INT_MASK_L;
  		gc->chip_types[0].regs.enable = APB_INT_ENABLE_L;
  		gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
  		gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
  		gc->chip_types[0].chip.irq_resume = dw_apb_ictl_resume;
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
146
  	}
b66231183   Thomas Gleixner   irqchip/dw-apb-ic...
147
  	irq_set_chained_handler_and_data(irq, dw_apb_ictl_handler, domain);
350d71b94   Sebastian Hesselbarth   irqchip: add Desi...
148
149
150
151
152
153
154
155
156
157
158
  
  	return 0;
  
  err_unmap:
  	iounmap(iobase);
  err_release:
  	release_mem_region(r.start, resource_size(&r));
  	return ret;
  }
  IRQCHIP_DECLARE(dw_apb_ictl,
  		"snps,dw-apb-ictl", dw_apb_ictl_init);