Blame view

drivers/irqchip/irq-xtensa-mx.c 4.49 KB
26a8e96a8   Max Filippov   xtensa: add MX ir...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * Xtensa MX interrupt distributor
   *
   * Copyright (C) 2002 - 2013 Tensilica, Inc.
   *
   * This file is subject to the terms and conditions of the GNU General Public
   * License.  See the file "COPYING" in the main directory of this archive
   * for more details.
   */
  
  #include <linux/interrupt.h>
  #include <linux/irqdomain.h>
  #include <linux/irq.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
14
  #include <linux/irqchip.h>
26a8e96a8   Max Filippov   xtensa: add MX ir...
15
16
17
  #include <linux/of.h>
  
  #include <asm/mxregs.h>
26a8e96a8   Max Filippov   xtensa: add MX ir...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  #define HW_IRQ_IPI_COUNT 2
  #define HW_IRQ_MX_BASE 2
  #define HW_IRQ_EXTERN_BASE 3
  
  static DEFINE_PER_CPU(unsigned int, cached_irq_mask);
  
  static int xtensa_mx_irq_map(struct irq_domain *d, unsigned int irq,
  		irq_hw_number_t hw)
  {
  	if (hw < HW_IRQ_IPI_COUNT) {
  		struct irq_chip *irq_chip = d->host_data;
  		irq_set_chip_and_handler_name(irq, irq_chip,
  				handle_percpu_irq, "ipi");
  		irq_set_status_flags(irq, IRQ_LEVEL);
  		return 0;
  	}
500912121   Marc Zyngier   irqchip/xtensa-mx...
34
  	irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
26a8e96a8   Max Filippov   xtensa: add MX ir...
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  	return xtensa_irq_map(d, irq, hw);
  }
  
  /*
   * Device Tree IRQ specifier translation function which works with one or
   * two cell bindings. First cell value maps directly to the hwirq number.
   * Second cell if present specifies whether hwirq number is external (1) or
   * internal (0).
   */
  static int xtensa_mx_irq_domain_xlate(struct irq_domain *d,
  		struct device_node *ctrlr,
  		const u32 *intspec, unsigned int intsize,
  		unsigned long *out_hwirq, unsigned int *out_type)
  {
  	return xtensa_irq_domain_xlate(intspec, intsize,
  			intspec[0], intspec[0] + HW_IRQ_EXTERN_BASE,
  			out_hwirq, out_type);
  }
  
  static const struct irq_domain_ops xtensa_mx_irq_domain_ops = {
  	.xlate = xtensa_mx_irq_domain_xlate,
  	.map = xtensa_mx_irq_map,
  };
  
  void secondary_init_irq(void)
  {
  	__this_cpu_write(cached_irq_mask,
  			XCHAL_INTTYPE_MASK_EXTERN_EDGE |
  			XCHAL_INTTYPE_MASK_EXTERN_LEVEL);
cad6fade6   Max Filippov   xtensa: clean up ...
64
  	xtensa_set_sr(XCHAL_INTTYPE_MASK_EXTERN_EDGE |
26a8e96a8   Max Filippov   xtensa: add MX ir...
65
66
67
68
69
70
71
72
  			XCHAL_INTTYPE_MASK_EXTERN_LEVEL, intenable);
  }
  
  static void xtensa_mx_irq_mask(struct irq_data *d)
  {
  	unsigned int mask = 1u << d->hwirq;
  
  	if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
eb271710e   Max Filippov   drivers/irqchip: ...
73
74
75
76
77
78
79
  		    XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
  		unsigned int ext_irq = xtensa_get_ext_irq_no(d->hwirq);
  
  		if (ext_irq >= HW_IRQ_MX_BASE) {
  			set_er(1u << (ext_irq - HW_IRQ_MX_BASE), MIENG);
  			return;
  		}
26a8e96a8   Max Filippov   xtensa: add MX ir...
80
  	}
eb271710e   Max Filippov   drivers/irqchip: ...
81
82
83
  	mask = __this_cpu_read(cached_irq_mask) & ~mask;
  	__this_cpu_write(cached_irq_mask, mask);
  	xtensa_set_sr(mask, intenable);
26a8e96a8   Max Filippov   xtensa: add MX ir...
84
85
86
87
88
89
90
  }
  
  static void xtensa_mx_irq_unmask(struct irq_data *d)
  {
  	unsigned int mask = 1u << d->hwirq;
  
  	if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
eb271710e   Max Filippov   drivers/irqchip: ...
91
92
93
94
95
96
97
  		    XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) {
  		unsigned int ext_irq = xtensa_get_ext_irq_no(d->hwirq);
  
  		if (ext_irq >= HW_IRQ_MX_BASE) {
  			set_er(1u << (ext_irq - HW_IRQ_MX_BASE), MIENGSET);
  			return;
  		}
26a8e96a8   Max Filippov   xtensa: add MX ir...
98
  	}
eb271710e   Max Filippov   drivers/irqchip: ...
99
100
101
  	mask |= __this_cpu_read(cached_irq_mask);
  	__this_cpu_write(cached_irq_mask, mask);
  	xtensa_set_sr(mask, intenable);
26a8e96a8   Max Filippov   xtensa: add MX ir...
102
103
104
105
  }
  
  static void xtensa_mx_irq_enable(struct irq_data *d)
  {
26a8e96a8   Max Filippov   xtensa: add MX ir...
106
107
108
109
110
111
  	xtensa_mx_irq_unmask(d);
  }
  
  static void xtensa_mx_irq_disable(struct irq_data *d)
  {
  	xtensa_mx_irq_mask(d);
26a8e96a8   Max Filippov   xtensa: add MX ir...
112
113
114
115
  }
  
  static void xtensa_mx_irq_ack(struct irq_data *d)
  {
cad6fade6   Max Filippov   xtensa: clean up ...
116
  	xtensa_set_sr(1 << d->hwirq, intclear);
26a8e96a8   Max Filippov   xtensa: add MX ir...
117
118
119
120
  }
  
  static int xtensa_mx_irq_retrigger(struct irq_data *d)
  {
bb6652363   Max Filippov   drivers/irqchip: ...
121
122
123
124
125
  	unsigned int mask = 1u << d->hwirq;
  
  	if (WARN_ON(mask & ~XCHAL_INTTYPE_MASK_SOFTWARE))
  		return 0;
  	xtensa_set_sr(mask, intset);
26a8e96a8   Max Filippov   xtensa: add MX ir...
126
127
128
129
130
131
  	return 1;
  }
  
  static int xtensa_mx_irq_set_affinity(struct irq_data *d,
  		const struct cpumask *dest, bool force)
  {
500912121   Marc Zyngier   irqchip/xtensa-mx...
132
133
  	int cpu = cpumask_any_and(dest, cpu_online_mask);
  	unsigned mask = 1u << cpu;
26a8e96a8   Max Filippov   xtensa: add MX ir...
134
135
  
  	set_er(mask, MIROUT(d->hwirq - HW_IRQ_MX_BASE));
500912121   Marc Zyngier   irqchip/xtensa-mx...
136
  	irq_data_update_effective_affinity(d, cpumask_of(cpu));
26a8e96a8   Max Filippov   xtensa: add MX ir...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  	return 0;
  
  }
  
  static struct irq_chip xtensa_mx_irq_chip = {
  	.name		= "xtensa-mx",
  	.irq_enable	= xtensa_mx_irq_enable,
  	.irq_disable	= xtensa_mx_irq_disable,
  	.irq_mask	= xtensa_mx_irq_mask,
  	.irq_unmask	= xtensa_mx_irq_unmask,
  	.irq_ack	= xtensa_mx_irq_ack,
  	.irq_retrigger	= xtensa_mx_irq_retrigger,
  	.irq_set_affinity = xtensa_mx_irq_set_affinity,
  };
  
  int __init xtensa_mx_init_legacy(struct device_node *interrupt_parent)
  {
  	struct irq_domain *root_domain =
e5c86679d   Max Filippov   xtensa: don't use...
155
  		irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0,
26a8e96a8   Max Filippov   xtensa: add MX ir...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  				&xtensa_mx_irq_domain_ops,
  				&xtensa_mx_irq_chip);
  	irq_set_default_host(root_domain);
  	secondary_init_irq();
  	return 0;
  }
  
  static int __init xtensa_mx_init(struct device_node *np,
  		struct device_node *interrupt_parent)
  {
  	struct irq_domain *root_domain =
  		irq_domain_add_linear(np, NR_IRQS, &xtensa_mx_irq_domain_ops,
  				&xtensa_mx_irq_chip);
  	irq_set_default_host(root_domain);
  	secondary_init_irq();
  	return 0;
  }
  IRQCHIP_DECLARE(xtensa_mx_irq_chip, "cdns,xtensa-mx", xtensa_mx_init);