Blame view

kernel/irq/generic-chip.c 15 KB
7d8280624   Thomas Gleixner   genirq: Implement...
1
2
3
4
5
6
7
8
  /*
   * Library implementing the most common irq chip callback functions
   *
   * Copyright (C) 2011, Thomas Gleixner
   */
  #include <linux/io.h>
  #include <linux/irq.h>
  #include <linux/slab.h>
6e5fdeedc   Paul Gortmaker   kernel: Fix files...
9
  #include <linux/export.h>
088f40b7b   Thomas Gleixner   genirq: Generic c...
10
  #include <linux/irqdomain.h>
7d8280624   Thomas Gleixner   genirq: Implement...
11
12
  #include <linux/interrupt.h>
  #include <linux/kernel_stat.h>
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
13
  #include <linux/syscore_ops.h>
7d8280624   Thomas Gleixner   genirq: Implement...
14
15
  
  #include "internals.h"
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
16
17
  static LIST_HEAD(gc_list);
  static DEFINE_RAW_SPINLOCK(gc_lock);
7d8280624   Thomas Gleixner   genirq: Implement...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  /**
   * irq_gc_noop - NOOP function
   * @d: irq_data
   */
  void irq_gc_noop(struct irq_data *d)
  {
  }
  
  /**
   * irq_gc_mask_disable_reg - Mask chip via disable register
   * @d: irq_data
   *
   * Chip has separate enable/disable registers instead of a single mask
   * register.
   */
  void irq_gc_mask_disable_reg(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
36
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
37
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
38
39
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
40
  	irq_reg_writel(gc, mask, ct->regs.disable);
899f0e66f   Gerlando Falauto   genirq: Generic c...
41
  	*ct->mask_cache &= ~mask;
7d8280624   Thomas Gleixner   genirq: Implement...
42
43
44
45
  	irq_gc_unlock(gc);
  }
  
  /**
ccc414f83   Thomas Gleixner   genirq: Add the g...
46
   * irq_gc_mask_set_bit - Mask chip via setting bit in mask register
7d8280624   Thomas Gleixner   genirq: Implement...
47
48
49
50
51
52
53
54
   * @d: irq_data
   *
   * Chip has a single mask register. Values of this register are cached
   * and protected by gc->lock
   */
  void irq_gc_mask_set_bit(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
55
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
56
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
57
58
  
  	irq_gc_lock(gc);
899f0e66f   Gerlando Falauto   genirq: Generic c...
59
  	*ct->mask_cache |= mask;
332fd7c4f   Kevin Cernekee   genirq: Generic c...
60
  	irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
7d8280624   Thomas Gleixner   genirq: Implement...
61
62
  	irq_gc_unlock(gc);
  }
d55f0cc4c   Fabio Estevam   genirq: generic-c...
63
  EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit);
7d8280624   Thomas Gleixner   genirq: Implement...
64
65
  
  /**
ccc414f83   Thomas Gleixner   genirq: Add the g...
66
   * irq_gc_mask_clr_bit - Mask chip via clearing bit in mask register
7d8280624   Thomas Gleixner   genirq: Implement...
67
68
69
70
71
72
73
74
   * @d: irq_data
   *
   * Chip has a single mask register. Values of this register are cached
   * and protected by gc->lock
   */
  void irq_gc_mask_clr_bit(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
75
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
76
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
77
78
  
  	irq_gc_lock(gc);
899f0e66f   Gerlando Falauto   genirq: Generic c...
79
  	*ct->mask_cache &= ~mask;
332fd7c4f   Kevin Cernekee   genirq: Generic c...
80
  	irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
7d8280624   Thomas Gleixner   genirq: Implement...
81
82
  	irq_gc_unlock(gc);
  }
d55f0cc4c   Fabio Estevam   genirq: generic-c...
83
  EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit);
7d8280624   Thomas Gleixner   genirq: Implement...
84
85
86
87
88
89
90
91
92
93
94
  
  /**
   * irq_gc_unmask_enable_reg - Unmask chip via enable register
   * @d: irq_data
   *
   * Chip has separate enable/disable registers instead of a single mask
   * register.
   */
  void irq_gc_unmask_enable_reg(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
95
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
96
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
97
98
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
99
  	irq_reg_writel(gc, mask, ct->regs.enable);
899f0e66f   Gerlando Falauto   genirq: Generic c...
100
  	*ct->mask_cache |= mask;
7d8280624   Thomas Gleixner   genirq: Implement...
101
102
103
104
  	irq_gc_unlock(gc);
  }
  
  /**
659fb32d1   Simon Guinot   genirq: replace i...
105
   * irq_gc_ack_set_bit - Ack pending interrupt via setting bit
7d8280624   Thomas Gleixner   genirq: Implement...
106
107
   * @d: irq_data
   */
659fb32d1   Simon Guinot   genirq: replace i...
108
  void irq_gc_ack_set_bit(struct irq_data *d)
7d8280624   Thomas Gleixner   genirq: Implement...
109
110
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
111
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
112
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
113
114
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
115
  	irq_reg_writel(gc, mask, ct->regs.ack);
7d8280624   Thomas Gleixner   genirq: Implement...
116
117
  	irq_gc_unlock(gc);
  }
d55f0cc4c   Fabio Estevam   genirq: generic-c...
118
  EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit);
7d8280624   Thomas Gleixner   genirq: Implement...
119
120
  
  /**
659fb32d1   Simon Guinot   genirq: replace i...
121
122
123
124
125
126
   * irq_gc_ack_clr_bit - Ack pending interrupt via clearing bit
   * @d: irq_data
   */
  void irq_gc_ack_clr_bit(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
127
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
128
  	u32 mask = ~d->mask;
659fb32d1   Simon Guinot   genirq: replace i...
129
130
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
131
  	irq_reg_writel(gc, mask, ct->regs.ack);
659fb32d1   Simon Guinot   genirq: replace i...
132
133
134
135
  	irq_gc_unlock(gc);
  }
  
  /**
37074c5a1   Uwe Kleine-König   irq/generic-chip:...
136
   * irq_gc_mask_disable_reg_and_ack - Mask and ack pending interrupt
7d8280624   Thomas Gleixner   genirq: Implement...
137
138
139
140
141
   * @d: irq_data
   */
  void irq_gc_mask_disable_reg_and_ack(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
142
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
143
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
144
145
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
146
147
  	irq_reg_writel(gc, mask, ct->regs.mask);
  	irq_reg_writel(gc, mask, ct->regs.ack);
7d8280624   Thomas Gleixner   genirq: Implement...
148
149
150
151
152
153
154
155
156
157
  	irq_gc_unlock(gc);
  }
  
  /**
   * irq_gc_eoi - EOI interrupt
   * @d: irq_data
   */
  void irq_gc_eoi(struct irq_data *d)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
cfeaa93f8   Gerlando Falauto   genirq: Generic c...
158
  	struct irq_chip_type *ct = irq_data_get_chip_type(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
159
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
160
161
  
  	irq_gc_lock(gc);
332fd7c4f   Kevin Cernekee   genirq: Generic c...
162
  	irq_reg_writel(gc, mask, ct->regs.eoi);
7d8280624   Thomas Gleixner   genirq: Implement...
163
164
165
166
167
  	irq_gc_unlock(gc);
  }
  
  /**
   * irq_gc_set_wake - Set/clr wake bit for an interrupt
ccc414f83   Thomas Gleixner   genirq: Add the g...
168
169
   * @d:  irq_data
   * @on: Indicates whether the wake bit should be set or cleared
7d8280624   Thomas Gleixner   genirq: Implement...
170
171
172
173
174
175
176
177
   *
   * For chips where the wake from suspend functionality is not
   * configured in a separate register and the wakeup active state is
   * just stored in a bitmask.
   */
  int irq_gc_set_wake(struct irq_data *d, unsigned int on)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
966dc736b   Thomas Gleixner   genirq: Generic c...
178
  	u32 mask = d->mask;
7d8280624   Thomas Gleixner   genirq: Implement...
179
180
181
182
183
184
185
186
187
188
189
190
  
  	if (!(mask & gc->wake_enabled))
  		return -EINVAL;
  
  	irq_gc_lock(gc);
  	if (on)
  		gc->wake_active |= mask;
  	else
  		gc->wake_active &= ~mask;
  	irq_gc_unlock(gc);
  	return 0;
  }
b79055952   Kevin Cernekee   genirq: Generic c...
191
192
193
194
195
196
197
198
199
  static u32 irq_readl_be(void __iomem *addr)
  {
  	return ioread32be(addr);
  }
  
  static void irq_writel_be(u32 val, void __iomem *addr)
  {
  	iowrite32be(val, addr);
  }
3528d82b6   Thomas Gleixner   genirq: Generic c...
200
201
202
203
204
205
206
207
208
209
210
211
  static void
  irq_init_generic_chip(struct irq_chip_generic *gc, const char *name,
  		      int num_ct, unsigned int irq_base,
  		      void __iomem *reg_base, irq_flow_handler_t handler)
  {
  	raw_spin_lock_init(&gc->lock);
  	gc->num_ct = num_ct;
  	gc->irq_base = irq_base;
  	gc->reg_base = reg_base;
  	gc->chip_types->chip.name = name;
  	gc->chip_types->handler = handler;
  }
7d8280624   Thomas Gleixner   genirq: Implement...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  /**
   * irq_alloc_generic_chip - Allocate a generic chip and initialize it
   * @name:	Name of the irq chip
   * @num_ct:	Number of irq_chip_type instances associated with this
   * @irq_base:	Interrupt base nr for this chip
   * @reg_base:	Register base address (virtual)
   * @handler:	Default flow handler associated with this chip
   *
   * Returns an initialized irq_chip_generic structure. The chip defaults
   * to the primary (index 0) irq_chip_type and @handler
   */
  struct irq_chip_generic *
  irq_alloc_generic_chip(const char *name, int num_ct, unsigned int irq_base,
  		       void __iomem *reg_base, irq_flow_handler_t handler)
  {
  	struct irq_chip_generic *gc;
  	unsigned long sz = sizeof(*gc) + num_ct * sizeof(struct irq_chip_type);
  
  	gc = kzalloc(sz, GFP_KERNEL);
  	if (gc) {
3528d82b6   Thomas Gleixner   genirq: Generic c...
232
233
  		irq_init_generic_chip(gc, name, num_ct, irq_base, reg_base,
  				      handler);
7d8280624   Thomas Gleixner   genirq: Implement...
234
235
236
  	}
  	return gc;
  }
825de2e90   Nobuhiro Iwamatsu   irq: Add EXPORT_S...
237
  EXPORT_SYMBOL_GPL(irq_alloc_generic_chip);
7d8280624   Thomas Gleixner   genirq: Implement...
238

3528d82b6   Thomas Gleixner   genirq: Generic c...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  static void
  irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
  {
  	struct irq_chip_type *ct = gc->chip_types;
  	u32 *mskptr = &gc->mask_cache, mskreg = ct->regs.mask;
  	int i;
  
  	for (i = 0; i < gc->num_ct; i++) {
  		if (flags & IRQ_GC_MASK_CACHE_PER_TYPE) {
  			mskptr = &ct[i].mask_cache_priv;
  			mskreg = ct[i].regs.mask;
  		}
  		ct[i].mask_cache = mskptr;
  		if (flags & IRQ_GC_INIT_MASK_CACHE)
332fd7c4f   Kevin Cernekee   genirq: Generic c...
253
  			*mskptr = irq_reg_readl(gc, mskreg);
3528d82b6   Thomas Gleixner   genirq: Generic c...
254
255
  	}
  }
088f40b7b   Thomas Gleixner   genirq: Generic c...
256
257
258
259
260
261
262
263
264
  /**
   * irq_alloc_domain_generic_chip - Allocate generic chips for an irq domain
   * @d:			irq domain for which to allocate chips
   * @irqs_per_chip:	Number of interrupts each chip handles
   * @num_ct:		Number of irq_chip_type instances associated with this
   * @name:		Name of the irq chip
   * @handler:		Default flow handler associated with these chips
   * @clr:		IRQ_* bits to clear in the mapping function
   * @set:		IRQ_* bits to set in the mapping function
6fff83140   James Hogan   genirq: Irqchip: ...
265
   * @gcflags:		Generic chip specific setup flags
088f40b7b   Thomas Gleixner   genirq: Generic c...
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
   */
  int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
  				   int num_ct, const char *name,
  				   irq_flow_handler_t handler,
  				   unsigned int clr, unsigned int set,
  				   enum irq_gc_flags gcflags)
  {
  	struct irq_domain_chip_generic *dgc;
  	struct irq_chip_generic *gc;
  	int numchips, sz, i;
  	unsigned long flags;
  	void *tmp;
  
  	if (d->gc)
  		return -EBUSY;
505608d2b   Linus Torvalds   Merge branch 'irq...
281
  	numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip);
088f40b7b   Thomas Gleixner   genirq: Generic c...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  	if (!numchips)
  		return -EINVAL;
  
  	/* Allocate a pointer, generic chip and chiptypes for each chip */
  	sz = sizeof(*dgc) + numchips * sizeof(gc);
  	sz += numchips * (sizeof(*gc) + num_ct * sizeof(struct irq_chip_type));
  
  	tmp = dgc = kzalloc(sz, GFP_KERNEL);
  	if (!dgc)
  		return -ENOMEM;
  	dgc->irqs_per_chip = irqs_per_chip;
  	dgc->num_chips = numchips;
  	dgc->irq_flags_to_set = set;
  	dgc->irq_flags_to_clear = clr;
  	dgc->gc_flags = gcflags;
  	d->gc = dgc;
  
  	/* Calc pointer to the first generic chip */
  	tmp += sizeof(*dgc) + numchips * sizeof(gc);
  	for (i = 0; i < numchips; i++) {
  		/* Store the pointer to the generic chip */
  		dgc->gc[i] = gc = tmp;
  		irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip,
  				      NULL, handler);
b79055952   Kevin Cernekee   genirq: Generic c...
306

088f40b7b   Thomas Gleixner   genirq: Generic c...
307
  		gc->domain = d;
b79055952   Kevin Cernekee   genirq: Generic c...
308
309
310
311
  		if (gcflags & IRQ_GC_BE_IO) {
  			gc->reg_readl = &irq_readl_be;
  			gc->reg_writel = &irq_writel_be;
  		}
088f40b7b   Thomas Gleixner   genirq: Generic c...
312
313
314
315
316
317
  		raw_spin_lock_irqsave(&gc_lock, flags);
  		list_add_tail(&gc->list, &gc_list);
  		raw_spin_unlock_irqrestore(&gc_lock, flags);
  		/* Calc pointer to the next generic chip */
  		tmp += sizeof(*gc) + num_ct * sizeof(struct irq_chip_type);
  	}
0bb4afb45   Grant Likely   irqdomain: Add a ...
318
  	d->name = name;
088f40b7b   Thomas Gleixner   genirq: Generic c...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  	return 0;
  }
  EXPORT_SYMBOL_GPL(irq_alloc_domain_generic_chips);
  
  /**
   * irq_get_domain_generic_chip - Get a pointer to the generic chip of a hw_irq
   * @d:			irq domain pointer
   * @hw_irq:		Hardware interrupt number
   */
  struct irq_chip_generic *
  irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq)
  {
  	struct irq_domain_chip_generic *dgc = d->gc;
  	int idx;
  
  	if (!dgc)
  		return NULL;
  	idx = hw_irq / dgc->irqs_per_chip;
  	if (idx >= dgc->num_chips)
  		return NULL;
  	return dgc->gc[idx];
  }
  EXPORT_SYMBOL_GPL(irq_get_domain_generic_chip);
7d8280624   Thomas Gleixner   genirq: Implement...
342
343
344
345
346
  /*
   * Separate lockdep class for interrupt chip which can nest irq_desc
   * lock.
   */
  static struct lock_class_key irq_nested_lock_class;
ccc414f83   Thomas Gleixner   genirq: Add the g...
347
  /*
088f40b7b   Thomas Gleixner   genirq: Generic c...
348
349
   * irq_map_generic_chip - Map a generic chip for an irq domain
   */
a5152c8a1   Boris BREZILLON   genirq: generic c...
350
351
  int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
  			 irq_hw_number_t hw_irq)
088f40b7b   Thomas Gleixner   genirq: Generic c...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  {
  	struct irq_data *data = irq_get_irq_data(virq);
  	struct irq_domain_chip_generic *dgc = d->gc;
  	struct irq_chip_generic *gc;
  	struct irq_chip_type *ct;
  	struct irq_chip *chip;
  	unsigned long flags;
  	int idx;
  
  	if (!d->gc)
  		return -ENODEV;
  
  	idx = hw_irq / dgc->irqs_per_chip;
  	if (idx >= dgc->num_chips)
  		return -EINVAL;
  	gc = dgc->gc[idx];
  
  	idx = hw_irq % dgc->irqs_per_chip;
e8bd834f7   Grant Likely   genirq: irqchip: ...
370
371
  	if (test_bit(idx, &gc->unused))
  		return -ENOTSUPP;
088f40b7b   Thomas Gleixner   genirq: Generic c...
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
  	if (test_bit(idx, &gc->installed))
  		return -EBUSY;
  
  	ct = gc->chip_types;
  	chip = &ct->chip;
  
  	/* We only init the cache for the first mapping of a generic chip */
  	if (!gc->installed) {
  		raw_spin_lock_irqsave(&gc->lock, flags);
  		irq_gc_init_mask_cache(gc, dgc->gc_flags);
  		raw_spin_unlock_irqrestore(&gc->lock, flags);
  	}
  
  	/* Mark the interrupt as installed */
  	set_bit(idx, &gc->installed);
  
  	if (dgc->gc_flags & IRQ_GC_INIT_NESTED_LOCK)
  		irq_set_lockdep_class(virq, &irq_nested_lock_class);
  
  	if (chip->irq_calc_mask)
  		chip->irq_calc_mask(data);
  	else
  		data->mask = 1 << idx;
  
  	irq_set_chip_and_handler(virq, chip, ct->handler);
  	irq_set_chip_data(virq, gc);
  	irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set);
  	return 0;
  }
a5152c8a1   Boris BREZILLON   genirq: generic c...
401
  EXPORT_SYMBOL_GPL(irq_map_generic_chip);
088f40b7b   Thomas Gleixner   genirq: Generic c...
402
403
404
405
406
407
408
409
  
  struct irq_domain_ops irq_generic_chip_ops = {
  	.map	= irq_map_generic_chip,
  	.xlate	= irq_domain_xlate_onetwocell,
  };
  EXPORT_SYMBOL_GPL(irq_generic_chip_ops);
  
  /**
7d8280624   Thomas Gleixner   genirq: Implement...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
   * irq_setup_generic_chip - Setup a range of interrupts with a generic chip
   * @gc:		Generic irq chip holding all data
   * @msk:	Bitmask holding the irqs to initialize relative to gc->irq_base
   * @flags:	Flags for initialization
   * @clr:	IRQ_* bits to clear
   * @set:	IRQ_* bits to set
   *
   * Set up max. 32 interrupts starting from gc->irq_base. Note, this
   * initializes all interrupts to the primary irq_chip_type and its
   * associated handler.
   */
  void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk,
  			    enum irq_gc_flags flags, unsigned int clr,
  			    unsigned int set)
  {
  	struct irq_chip_type *ct = gc->chip_types;
d0051816e   Thomas Gleixner   genirq: irqchip: ...
426
  	struct irq_chip *chip = &ct->chip;
7d8280624   Thomas Gleixner   genirq: Implement...
427
  	unsigned int i;
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
428
429
430
  	raw_spin_lock(&gc_lock);
  	list_add_tail(&gc->list, &gc_list);
  	raw_spin_unlock(&gc_lock);
3528d82b6   Thomas Gleixner   genirq: Generic c...
431
  	irq_gc_init_mask_cache(gc, flags);
899f0e66f   Gerlando Falauto   genirq: Generic c...
432

7d8280624   Thomas Gleixner   genirq: Implement...
433
  	for (i = gc->irq_base; msk; msk >>= 1, i++) {
1dd75f91a   jhbird.choi@samsung.com   genirq: Fix wrong...
434
  		if (!(msk & 0x01))
7d8280624   Thomas Gleixner   genirq: Implement...
435
436
437
438
  			continue;
  
  		if (flags & IRQ_GC_INIT_NESTED_LOCK)
  			irq_set_lockdep_class(i, &irq_nested_lock_class);
966dc736b   Thomas Gleixner   genirq: Generic c...
439
440
  		if (!(flags & IRQ_GC_NO_MASK)) {
  			struct irq_data *d = irq_get_irq_data(i);
d0051816e   Thomas Gleixner   genirq: irqchip: ...
441
442
443
444
  			if (chip->irq_calc_mask)
  				chip->irq_calc_mask(d);
  			else
  				d->mask = 1 << (i - gc->irq_base);
966dc736b   Thomas Gleixner   genirq: Generic c...
445
  		}
d0051816e   Thomas Gleixner   genirq: irqchip: ...
446
  		irq_set_chip_and_handler(i, chip, ct->handler);
7d8280624   Thomas Gleixner   genirq: Implement...
447
448
449
450
451
  		irq_set_chip_data(i, gc);
  		irq_modify_status(i, clr, set);
  	}
  	gc->irq_cnt = i - gc->irq_base;
  }
825de2e90   Nobuhiro Iwamatsu   irq: Add EXPORT_S...
452
  EXPORT_SYMBOL_GPL(irq_setup_generic_chip);
7d8280624   Thomas Gleixner   genirq: Implement...
453
454
455
456
  
  /**
   * irq_setup_alt_chip - Switch to alternative chip
   * @d:		irq_data for this interrupt
ccc414f83   Thomas Gleixner   genirq: Add the g...
457
   * @type:	Flow type to be initialized
7d8280624   Thomas Gleixner   genirq: Implement...
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
   *
   * Only to be called from chip->irq_set_type() callbacks.
   */
  int irq_setup_alt_chip(struct irq_data *d, unsigned int type)
  {
  	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
  	struct irq_chip_type *ct = gc->chip_types;
  	unsigned int i;
  
  	for (i = 0; i < gc->num_ct; i++, ct++) {
  		if (ct->type & type) {
  			d->chip = &ct->chip;
  			irq_data_to_desc(d)->handle_irq = ct->handler;
  			return 0;
  		}
  	}
  	return -EINVAL;
  }
825de2e90   Nobuhiro Iwamatsu   irq: Add EXPORT_S...
476
  EXPORT_SYMBOL_GPL(irq_setup_alt_chip);
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  
  /**
   * irq_remove_generic_chip - Remove a chip
   * @gc:		Generic irq chip holding all data
   * @msk:	Bitmask holding the irqs to initialize relative to gc->irq_base
   * @clr:	IRQ_* bits to clear
   * @set:	IRQ_* bits to set
   *
   * Remove up to 32 interrupts starting from gc->irq_base.
   */
  void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk,
  			     unsigned int clr, unsigned int set)
  {
  	unsigned int i = gc->irq_base;
  
  	raw_spin_lock(&gc_lock);
  	list_del(&gc->list);
  	raw_spin_unlock(&gc_lock);
  
  	for (; msk; msk >>= 1, i++) {
1dd75f91a   jhbird.choi@samsung.com   genirq: Fix wrong...
497
  		if (!(msk & 0x01))
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
498
499
500
501
502
503
504
505
506
  			continue;
  
  		/* Remove handler first. That will mask the irq line */
  		irq_set_handler(i, NULL);
  		irq_set_chip(i, &no_irq_chip);
  		irq_set_chip_data(i, NULL);
  		irq_modify_status(i, clr, set);
  	}
  }
825de2e90   Nobuhiro Iwamatsu   irq: Add EXPORT_S...
507
  EXPORT_SYMBOL_GPL(irq_remove_generic_chip);
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
508

088f40b7b   Thomas Gleixner   genirq: Generic c...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  static struct irq_data *irq_gc_get_irq_data(struct irq_chip_generic *gc)
  {
  	unsigned int virq;
  
  	if (!gc->domain)
  		return irq_get_irq_data(gc->irq_base);
  
  	/*
  	 * We don't know which of the irqs has been actually
  	 * installed. Use the first one.
  	 */
  	if (!gc->installed)
  		return NULL;
  
  	virq = irq_find_mapping(gc->domain, gc->irq_base + __ffs(gc->installed));
  	return virq ? irq_get_irq_data(virq) : NULL;
  }
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
526
527
528
529
530
531
532
  #ifdef CONFIG_PM
  static int irq_gc_suspend(void)
  {
  	struct irq_chip_generic *gc;
  
  	list_for_each_entry(gc, &gc_list, list) {
  		struct irq_chip_type *ct = gc->chip_types;
088f40b7b   Thomas Gleixner   genirq: Generic c...
533
534
535
536
537
538
  		if (ct->chip.irq_suspend) {
  			struct irq_data *data = irq_gc_get_irq_data(gc);
  
  			if (data)
  				ct->chip.irq_suspend(data);
  		}
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
539
540
541
542
543
544
545
546
547
548
  	}
  	return 0;
  }
  
  static void irq_gc_resume(void)
  {
  	struct irq_chip_generic *gc;
  
  	list_for_each_entry(gc, &gc_list, list) {
  		struct irq_chip_type *ct = gc->chip_types;
088f40b7b   Thomas Gleixner   genirq: Generic c...
549
550
551
552
553
554
  		if (ct->chip.irq_resume) {
  			struct irq_data *data = irq_gc_get_irq_data(gc);
  
  			if (data)
  				ct->chip.irq_resume(data);
  		}
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
555
556
557
558
559
560
561
562
563
564
565
566
567
  	}
  }
  #else
  #define irq_gc_suspend NULL
  #define irq_gc_resume NULL
  #endif
  
  static void irq_gc_shutdown(void)
  {
  	struct irq_chip_generic *gc;
  
  	list_for_each_entry(gc, &gc_list, list) {
  		struct irq_chip_type *ct = gc->chip_types;
088f40b7b   Thomas Gleixner   genirq: Generic c...
568
569
570
571
572
573
  		if (ct->chip.irq_pm_shutdown) {
  			struct irq_data *data = irq_gc_get_irq_data(gc);
  
  			if (data)
  				ct->chip.irq_pm_shutdown(data);
  		}
cfefd21e6   Thomas Gleixner   genirq: Add chip ...
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  	}
  }
  
  static struct syscore_ops irq_gc_syscore_ops = {
  	.suspend = irq_gc_suspend,
  	.resume = irq_gc_resume,
  	.shutdown = irq_gc_shutdown,
  };
  
  static int __init irq_gc_init_ops(void)
  {
  	register_syscore_ops(&irq_gc_syscore_ops);
  	return 0;
  }
  device_initcall(irq_gc_init_ops);