Blame view

drivers/irqchip/irq-sunxi-nmi.c 5.89 KB
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
1
2
3
4
5
6
7
8
9
  /*
   * Allwinner A20/A31 SoCs NMI IRQ chip driver.
   *
   * Carlo Caione <carlo.caione@gmail.com>
   *
   * 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.
   */
2d6caaed0   Chen-Yu Tsai   irqchip/sunxi-nmi...
10
11
  #define DRV_NAME	"sunxi-nmi"
  #define pr_fmt(fmt)	DRV_NAME ": " fmt
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
12
13
14
15
16
17
18
19
20
  #include <linux/bitops.h>
  #include <linux/device.h>
  #include <linux/io.h>
  #include <linux/irq.h>
  #include <linux/interrupt.h>
  #include <linux/irqdomain.h>
  #include <linux/of_irq.h>
  #include <linux/of_address.h>
  #include <linux/of_platform.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
21
  #include <linux/irqchip.h>
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
22
  #include <linux/irqchip/chained_irq.h>
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  
  #define SUNXI_NMI_SRC_TYPE_MASK	0x00000003
  
  enum {
  	SUNXI_SRC_TYPE_LEVEL_LOW = 0,
  	SUNXI_SRC_TYPE_EDGE_FALLING,
  	SUNXI_SRC_TYPE_LEVEL_HIGH,
  	SUNXI_SRC_TYPE_EDGE_RISING,
  };
  
  struct sunxi_sc_nmi_reg_offs {
  	u32 ctrl;
  	u32 pend;
  	u32 enable;
  };
  
  static struct sunxi_sc_nmi_reg_offs sun7i_reg_offs = {
  	.ctrl	= 0x00,
  	.pend	= 0x04,
  	.enable	= 0x08,
  };
  
  static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = {
  	.ctrl	= 0x00,
  	.pend	= 0x04,
  	.enable	= 0x34,
  };
bbbb03c1a   Chen-Yu Tsai   irqchip/sunxi-nmi...
50
51
52
53
54
  static struct sunxi_sc_nmi_reg_offs sun9i_reg_offs = {
  	.ctrl	= 0x00,
  	.pend	= 0x08,
  	.enable	= 0x04,
  };
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
55
56
57
  static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
  				      u32 val)
  {
332fd7c4f   Kevin Cernekee   genirq: Generic c...
58
  	irq_reg_writel(gc, val, off);
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
59
60
61
62
  }
  
  static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off)
  {
332fd7c4f   Kevin Cernekee   genirq: Generic c...
63
  	return irq_reg_readl(gc, off);
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
64
  }
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
65
  static void sunxi_sc_nmi_handle_irq(struct irq_desc *desc)
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
66
67
  {
  	struct irq_domain *domain = irq_desc_get_handler_data(desc);
5b29264c6   Jiang Liu   irqchip: Use irq_...
68
  	struct irq_chip *chip = irq_desc_get_chip(desc);
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
69
70
71
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
  	unsigned int virq = irq_find_mapping(domain, 0);
  
  	chained_irq_enter(chip, desc);
  	generic_handle_irq(virq);
  	chained_irq_exit(chip, desc);
  }
  
  static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
  	struct irq_chip_type *ct = gc->chip_types;
  	u32 src_type_reg;
  	u32 ctrl_off = ct->regs.type;
  	unsigned int src_type;
  	unsigned int i;
  
  	irq_gc_lock(gc);
  
  	switch (flow_type & IRQF_TRIGGER_MASK) {
  	case IRQ_TYPE_EDGE_FALLING:
  		src_type = SUNXI_SRC_TYPE_EDGE_FALLING;
  		break;
  	case IRQ_TYPE_EDGE_RISING:
  		src_type = SUNXI_SRC_TYPE_EDGE_RISING;
  		break;
  	case IRQ_TYPE_LEVEL_HIGH:
  		src_type = SUNXI_SRC_TYPE_LEVEL_HIGH;
  		break;
  	case IRQ_TYPE_NONE:
  	case IRQ_TYPE_LEVEL_LOW:
  		src_type = SUNXI_SRC_TYPE_LEVEL_LOW;
  		break;
  	default:
  		irq_gc_unlock(gc);
2d6caaed0   Chen-Yu Tsai   irqchip/sunxi-nmi...
103
104
105
  		pr_err("Cannot assign multiple trigger modes to IRQ %d.
  ",
  			data->irq);
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
106
107
108
109
110
  		return -EBADR;
  	}
  
  	irqd_set_trigger_type(data, flow_type);
  	irq_setup_alt_chip(data, flow_type);
febe06962   Axel Lin   irqchip: sunxi-nm...
111
  	for (i = 0; i < gc->num_ct; i++, ct++)
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  		if (ct->type & flow_type)
  			ctrl_off = ct->regs.type;
  
  	src_type_reg = sunxi_sc_nmi_read(gc, ctrl_off);
  	src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK;
  	src_type_reg |= src_type;
  	sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg);
  
  	irq_gc_unlock(gc);
  
  	return IRQ_SET_MASK_OK;
  }
  
  static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
  					struct sunxi_sc_nmi_reg_offs *reg_offs)
  {
  	struct irq_domain *domain;
  	struct irq_chip_generic *gc;
  	unsigned int irq;
  	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
  	int ret;
  
  
  	domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL);
  	if (!domain) {
2d6caaed0   Chen-Yu Tsai   irqchip/sunxi-nmi...
137
138
  		pr_err("Could not register interrupt domain.
  ");
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
139
140
  		return -ENOMEM;
  	}
2d6caaed0   Chen-Yu Tsai   irqchip/sunxi-nmi...
141
  	ret = irq_alloc_domain_generic_chips(domain, 1, 2, DRV_NAME,
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
142
143
144
  					     handle_fasteoi_irq, clr, 0,
  					     IRQ_GC_INIT_MASK_CACHE);
  	if (ret) {
2d6caaed0   Chen-Yu Tsai   irqchip/sunxi-nmi...
145
146
147
  		pr_err("Could not allocate generic interrupt chip.
  ");
  		goto fail_irqd_remove;
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
148
149
150
151
  	}
  
  	irq = irq_of_parse_and_map(node, 0);
  	if (irq <= 0) {
2d6caaed0   Chen-Yu Tsai   irqchip/sunxi-nmi...
152
153
  		pr_err("unable to parse irq
  ");
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
154
155
156
157
158
  		ret = -EINVAL;
  		goto fail_irqd_remove;
  	}
  
  	gc = irq_get_domain_generic_chip(domain, 0);
0e841b04c   Chen-Yu Tsai   irqchip/sunxi-nmi...
159
  	gc->reg_base = of_io_request_and_map(node, 0, of_node_full_name(node));
cfe199afe   Vladimir Zapolskiy   irqchip/sunxi-nmi...
160
  	if (IS_ERR(gc->reg_base)) {
2d6caaed0   Chen-Yu Tsai   irqchip/sunxi-nmi...
161
162
  		pr_err("unable to map resource
  ");
cfe199afe   Vladimir Zapolskiy   irqchip/sunxi-nmi...
163
  		ret = PTR_ERR(gc->reg_base);
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  		goto fail_irqd_remove;
  	}
  
  	gc->chip_types[0].type			= IRQ_TYPE_LEVEL_MASK;
  	gc->chip_types[0].chip.irq_mask		= irq_gc_mask_clr_bit;
  	gc->chip_types[0].chip.irq_unmask	= irq_gc_mask_set_bit;
  	gc->chip_types[0].chip.irq_eoi		= irq_gc_ack_set_bit;
  	gc->chip_types[0].chip.irq_set_type	= sunxi_sc_nmi_set_type;
  	gc->chip_types[0].chip.flags		= IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED;
  	gc->chip_types[0].regs.ack		= reg_offs->pend;
  	gc->chip_types[0].regs.mask		= reg_offs->enable;
  	gc->chip_types[0].regs.type		= reg_offs->ctrl;
  
  	gc->chip_types[1].type			= IRQ_TYPE_EDGE_BOTH;
  	gc->chip_types[1].chip.name		= gc->chip_types[0].chip.name;
  	gc->chip_types[1].chip.irq_ack		= irq_gc_ack_set_bit;
  	gc->chip_types[1].chip.irq_mask		= irq_gc_mask_clr_bit;
  	gc->chip_types[1].chip.irq_unmask	= irq_gc_mask_set_bit;
  	gc->chip_types[1].chip.irq_set_type	= sunxi_sc_nmi_set_type;
  	gc->chip_types[1].regs.ack		= reg_offs->pend;
  	gc->chip_types[1].regs.mask		= reg_offs->enable;
  	gc->chip_types[1].regs.type		= reg_offs->ctrl;
  	gc->chip_types[1].handler		= handle_edge_irq;
6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
187
188
  	sunxi_sc_nmi_write(gc, reg_offs->enable, 0);
  	sunxi_sc_nmi_write(gc, reg_offs->pend, 0x1);
3200a7120   Thomas Gleixner   irqchip/sunxi-nmi...
189
  	irq_set_chained_handler_and_data(irq, sunxi_sc_nmi_handle_irq, domain);
1b422ecd2   Hans de Goede   irqchip: sun7i/su...
190

6058bb362   Carlo Caione   ARM: sun7i/sun6i:...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  	return 0;
  
  fail_irqd_remove:
  	irq_domain_remove(domain);
  
  	return ret;
  }
  
  static int __init sun6i_sc_nmi_irq_init(struct device_node *node,
  					struct device_node *parent)
  {
  	return sunxi_sc_nmi_irq_init(node, &sun6i_reg_offs);
  }
  IRQCHIP_DECLARE(sun6i_sc_nmi, "allwinner,sun6i-a31-sc-nmi", sun6i_sc_nmi_irq_init);
  
  static int __init sun7i_sc_nmi_irq_init(struct device_node *node,
  					struct device_node *parent)
  {
  	return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs);
  }
  IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init);
bbbb03c1a   Chen-Yu Tsai   irqchip/sunxi-nmi...
212
213
214
215
216
217
218
  
  static int __init sun9i_nmi_irq_init(struct device_node *node,
  				     struct device_node *parent)
  {
  	return sunxi_sc_nmi_irq_init(node, &sun9i_reg_offs);
  }
  IRQCHIP_DECLARE(sun9i_nmi, "allwinner,sun9i-a80-nmi", sun9i_nmi_irq_init);