Blame view

drivers/irqchip/irq-armada-370-xp.c 15.8 KB
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * Marvell Armada 370 and Armada XP SoC IRQ handling
   *
   * Copyright (C) 2012 Marvell
   *
   * Lior Amsalem <alior@marvell.com>
   * Gregory CLEMENT <gregory.clement@free-electrons.com>
   * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
   * Ben Dooks <ben.dooks@codethink.co.uk>
   *
   * 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/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/irq.h>
  #include <linux/interrupt.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
21
  #include <linux/irqchip.h>
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
22
  #include <linux/irqchip/chained_irq.h>
d7df84b3c   Thomas Petazzoni   irqchip: irq-arma...
23
  #include <linux/cpu.h>
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
24
25
26
  #include <linux/io.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
31f614edb   Thomas Petazzoni   irqchip: armada-3...
27
  #include <linux/of_pci.h>
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
28
  #include <linux/irqdomain.h>
31f614edb   Thomas Petazzoni   irqchip: armada-3...
29
  #include <linux/slab.h>
0f077eb5c   Thomas Petazzoni   irqchip: armada-3...
30
  #include <linux/syscore_ops.h>
31f614edb   Thomas Petazzoni   irqchip: armada-3...
31
  #include <linux/msi.h>
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
32
33
  #include <asm/mach/arch.h>
  #include <asm/exception.h>
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
34
  #include <asm/smp_plat.h>
9339d432f   Thomas Petazzoni   irqchip: move IRQ...
35
  #include <asm/mach/irq.h>
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
36
37
38
  /* Interrupt Controller Registers Map */
  #define ARMADA_370_XP_INT_SET_MASK_OFFS		(0x48)
  #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS	(0x4C)
28da06dfd   Maxime Ripard   irqchip: armada-3...
39
40
  #define ARMADA_370_XP_INT_FABRIC_MASK_OFFS	(0x54)
  #define ARMADA_370_XP_INT_CAUSE_PERF(cpu)	(1 << cpu)
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
41

f3e16ccd0   Ben Dooks   ARM: mvebu: MPIC:...
42
  #define ARMADA_370_XP_INT_CONTROL		(0x00)
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
43
44
  #define ARMADA_370_XP_INT_SET_ENABLE_OFFS	(0x30)
  #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS	(0x34)
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
45
  #define ARMADA_370_XP_INT_SOURCE_CTL(irq)	(0x100 + irq*4)
8cc3cfc5c   Thomas Gleixner   irqchip: armanda:...
46
  #define ARMADA_370_XP_INT_SOURCE_CPU_MASK	0xF
758e83667   Grzegorz Jaszczyk   irqchip: armada-3...
47
  #define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid)	((BIT(0) | BIT(8)) << cpuid)
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
48
49
  
  #define ARMADA_370_XP_CPU_INTACK_OFFS		(0x44)
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
50
  #define ARMADA_375_PPI_CAUSE			(0x10)
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
51

344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
52
53
54
  #define ARMADA_370_XP_SW_TRIG_INT_OFFS           (0x4)
  #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS          (0xc)
  #define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS        (0x8)
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
55
  #define ARMADA_370_XP_MAX_PER_CPU_IRQS		(28)
5ec69017c   Thomas Petazzoni   irqchip: armada-3...
56
57
58
  #define IPI_DOORBELL_START                      (0)
  #define IPI_DOORBELL_END                        (8)
  #define IPI_DOORBELL_MASK                       0xFF
31f614edb   Thomas Petazzoni   irqchip: armada-3...
59
60
61
62
  #define PCI_MSI_DOORBELL_START                  (16)
  #define PCI_MSI_DOORBELL_NR                     (16)
  #define PCI_MSI_DOORBELL_END                    (32)
  #define PCI_MSI_DOORBELL_MASK                   0xFFFF0000
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
63

9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
64
65
66
  static void __iomem *per_cpu_int_base;
  static void __iomem *main_int_base;
  static struct irq_domain *armada_370_xp_mpic_domain;
0f077eb5c   Thomas Petazzoni   irqchip: armada-3...
67
  static u32 doorbell_mask_reg;
5724be846   Maxime Ripard   irqchip: armada-3...
68
  static int parent_irq;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
69
70
  #ifdef CONFIG_PCI_MSI
  static struct irq_domain *armada_370_xp_msi_domain;
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
71
  static struct irq_domain *armada_370_xp_msi_inner_domain;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
72
73
74
75
  static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
  static DEFINE_MUTEX(msi_used_lock);
  static phys_addr_t msi_doorbell_addr;
  #endif
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
76

2c299de52   Ezequiel Garcia   irqchip: armada-3...
77
78
  static inline bool is_percpu_irq(irq_hw_number_t irq)
  {
080481f97   Maxime Ripard   irqchip: armada-3...
79
  	if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS)
2c299de52   Ezequiel Garcia   irqchip: armada-3...
80
  		return true;
080481f97   Maxime Ripard   irqchip: armada-3...
81
82
  
  	return false;
2c299de52   Ezequiel Garcia   irqchip: armada-3...
83
  }
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
84
85
86
  /*
   * In SMP mode:
   * For shared global interrupts, mask/unmask global enable bit
097ef18d5   Marek BElisko   arm: irq-armada-3...
87
   * For CPU interrupts, mask/unmask the calling CPU's bit
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
88
   */
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
89
90
  static void armada_370_xp_irq_mask(struct irq_data *d)
  {
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
91
  	irq_hw_number_t hwirq = irqd_to_hwirq(d);
2c299de52   Ezequiel Garcia   irqchip: armada-3...
92
  	if (!is_percpu_irq(hwirq))
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
93
94
95
96
97
  		writel(hwirq, main_int_base +
  				ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
  	else
  		writel(hwirq, per_cpu_int_base +
  				ARMADA_370_XP_INT_SET_MASK_OFFS);
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
98
99
100
101
  }
  
  static void armada_370_xp_irq_unmask(struct irq_data *d)
  {
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
102
  	irq_hw_number_t hwirq = irqd_to_hwirq(d);
2c299de52   Ezequiel Garcia   irqchip: armada-3...
103
  	if (!is_percpu_irq(hwirq))
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
104
105
106
107
108
  		writel(hwirq, main_int_base +
  				ARMADA_370_XP_INT_SET_ENABLE_OFFS);
  	else
  		writel(hwirq, per_cpu_int_base +
  				ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
109
  }
31f614edb   Thomas Petazzoni   irqchip: armada-3...
110
  #ifdef CONFIG_PCI_MSI
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
111
  static struct irq_chip armada_370_xp_msi_irq_chip = {
f692a172d   Thomas Petazzoni   irqchip/armada-37...
112
  	.name = "MPIC MSI",
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
113
114
115
  	.irq_mask = pci_msi_mask_irq,
  	.irq_unmask = pci_msi_unmask_irq,
  };
31f614edb   Thomas Petazzoni   irqchip: armada-3...
116

fcc392d50   Thomas Petazzoni   irqchip/armada-37...
117
  static struct msi_domain_info armada_370_xp_msi_domain_info = {
a71b9412c   Thomas Petazzoni   irqchip/armada-37...
118
119
  	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
  		   MSI_FLAG_MULTI_PCI_MSI),
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
120
121
  	.chip	= &armada_370_xp_msi_irq_chip,
  };
31f614edb   Thomas Petazzoni   irqchip: armada-3...
122

fcc392d50   Thomas Petazzoni   irqchip/armada-37...
123
  static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
31f614edb   Thomas Petazzoni   irqchip: armada-3...
124
  {
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
125
126
127
  	msg->address_lo = lower_32_bits(msi_doorbell_addr);
  	msg->address_hi = upper_32_bits(msi_doorbell_addr);
  	msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START);
31f614edb   Thomas Petazzoni   irqchip: armada-3...
128
  }
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
129
130
  static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data,
  					  const struct cpumask *mask, bool force)
31f614edb   Thomas Petazzoni   irqchip: armada-3...
131
  {
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
132
133
  	 return -EINVAL;
  }
31f614edb   Thomas Petazzoni   irqchip: armada-3...
134

fcc392d50   Thomas Petazzoni   irqchip/armada-37...
135
  static struct irq_chip armada_370_xp_msi_bottom_irq_chip = {
f692a172d   Thomas Petazzoni   irqchip/armada-37...
136
  	.name			= "MPIC MSI",
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
137
138
139
  	.irq_compose_msi_msg	= armada_370_xp_compose_msi_msg,
  	.irq_set_affinity	= armada_370_xp_msi_set_affinity,
  };
3930115e0   Alexander Gordeev   irqchip: armada-3...
140

fcc392d50   Thomas Petazzoni   irqchip/armada-37...
141
142
143
  static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq,
  				   unsigned int nr_irqs, void *args)
  {
a71b9412c   Thomas Petazzoni   irqchip/armada-37...
144
  	int hwirq, i;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
145

fcc392d50   Thomas Petazzoni   irqchip/armada-37...
146
  	mutex_lock(&msi_used_lock);
a71b9412c   Thomas Petazzoni   irqchip/armada-37...
147
148
149
  
  	hwirq = bitmap_find_next_zero_area(msi_used, PCI_MSI_DOORBELL_NR,
  					   0, nr_irqs, 0);
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
150
151
152
  	if (hwirq >= PCI_MSI_DOORBELL_NR) {
  		mutex_unlock(&msi_used_lock);
  		return -ENOSPC;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
153
  	}
a71b9412c   Thomas Petazzoni   irqchip/armada-37...
154
  	bitmap_set(msi_used, hwirq, nr_irqs);
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
155
  	mutex_unlock(&msi_used_lock);
31f614edb   Thomas Petazzoni   irqchip: armada-3...
156

a71b9412c   Thomas Petazzoni   irqchip/armada-37...
157
158
159
160
161
162
  	for (i = 0; i < nr_irqs; i++) {
  		irq_domain_set_info(domain, virq + i, hwirq + i,
  				    &armada_370_xp_msi_bottom_irq_chip,
  				    domain->host_data, handle_simple_irq,
  				    NULL, NULL);
  	}
31f614edb   Thomas Petazzoni   irqchip: armada-3...
163

fcc392d50   Thomas Petazzoni   irqchip/armada-37...
164
  	return hwirq;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
165
  }
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
166
167
  static void armada_370_xp_msi_free(struct irq_domain *domain,
  				   unsigned int virq, unsigned int nr_irqs)
31f614edb   Thomas Petazzoni   irqchip: armada-3...
168
  {
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
169
  	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
31f614edb   Thomas Petazzoni   irqchip: armada-3...
170

fcc392d50   Thomas Petazzoni   irqchip/armada-37...
171
  	mutex_lock(&msi_used_lock);
a71b9412c   Thomas Petazzoni   irqchip/armada-37...
172
  	bitmap_clear(msi_used, d->hwirq, nr_irqs);
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
173
  	mutex_unlock(&msi_used_lock);
31f614edb   Thomas Petazzoni   irqchip: armada-3...
174
  }
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
175
176
177
  static const struct irq_domain_ops armada_370_xp_msi_domain_ops = {
  	.alloc	= armada_370_xp_msi_alloc,
  	.free	= armada_370_xp_msi_free,
31f614edb   Thomas Petazzoni   irqchip: armada-3...
178
179
180
181
182
  };
  
  static int armada_370_xp_msi_init(struct device_node *node,
  				  phys_addr_t main_int_phys_base)
  {
31f614edb   Thomas Petazzoni   irqchip: armada-3...
183
  	u32 reg;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
184
185
186
  
  	msi_doorbell_addr = main_int_phys_base +
  		ARMADA_370_XP_SW_TRIG_INT_OFFS;
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
187
188
189
190
  	armada_370_xp_msi_inner_domain =
  		irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
  				      &armada_370_xp_msi_domain_ops, NULL);
  	if (!armada_370_xp_msi_inner_domain)
31f614edb   Thomas Petazzoni   irqchip: armada-3...
191
  		return -ENOMEM;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
192
  	armada_370_xp_msi_domain =
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
193
194
195
  		pci_msi_create_irq_domain(of_node_to_fwnode(node),
  					  &armada_370_xp_msi_domain_info,
  					  armada_370_xp_msi_inner_domain);
31f614edb   Thomas Petazzoni   irqchip: armada-3...
196
  	if (!armada_370_xp_msi_domain) {
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
197
  		irq_domain_remove(armada_370_xp_msi_inner_domain);
31f614edb   Thomas Petazzoni   irqchip: armada-3...
198
199
  		return -ENOMEM;
  	}
31f614edb   Thomas Petazzoni   irqchip: armada-3...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  	reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
  		| PCI_MSI_DOORBELL_MASK;
  
  	writel(reg, per_cpu_int_base +
  	       ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
  
  	/* Unmask IPI interrupt */
  	writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  
  	return 0;
  }
  #else
  static inline int armada_370_xp_msi_init(struct device_node *node,
  					 phys_addr_t main_int_phys_base)
  {
  	return 0;
  }
  #endif
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
218
  #ifdef CONFIG_SMP
19e61d414   Arnaud Ebalard   ARM: mvebu: fix c...
219
  static DEFINE_RAW_SPINLOCK(irq_controller_lock);
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
220
221
222
  static int armada_xp_set_affinity(struct irq_data *d,
  				  const struct cpumask *mask_val, bool force)
  {
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
223
  	irq_hw_number_t hwirq = irqd_to_hwirq(d);
8cc3cfc5c   Thomas Gleixner   irqchip: armanda:...
224
  	unsigned long reg, mask;
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
225
  	int cpu;
8cc3cfc5c   Thomas Gleixner   irqchip: armanda:...
226
227
228
  	/* Select a single core from the affinity mask which is online */
  	cpu = cpumask_any_and(mask_val, cpu_online_mask);
  	mask = 1UL << cpu_logical_map(cpu);
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
229
230
  
  	raw_spin_lock(&irq_controller_lock);
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
231
  	reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
8cc3cfc5c   Thomas Gleixner   irqchip: armanda:...
232
  	reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask;
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
233
  	writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
3202bf015   Gregory CLEMENT   arm: mvebu: Impro...
234
  	raw_spin_unlock(&irq_controller_lock);
1dacf194b   Thomas Petazzoni   irqchip: irq-arma...
235
  	return IRQ_SET_MASK_OK;
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
236
237
  }
  #endif
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
238
  static struct irq_chip armada_370_xp_irq_chip = {
f692a172d   Thomas Petazzoni   irqchip/armada-37...
239
  	.name		= "MPIC",
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
240
241
242
  	.irq_mask       = armada_370_xp_irq_mask,
  	.irq_mask_ack   = armada_370_xp_irq_mask,
  	.irq_unmask     = armada_370_xp_irq_unmask,
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
243
244
245
  #ifdef CONFIG_SMP
  	.irq_set_affinity = armada_xp_set_affinity,
  #endif
0d8e1d80b   Gregory CLEMENT   irqchip: armada-3...
246
  	.flags		= IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
247
248
249
250
251
252
  };
  
  static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
  				      unsigned int virq, irq_hw_number_t hw)
  {
  	armada_370_xp_irq_mask(irq_get_irq_data(virq));
2c299de52   Ezequiel Garcia   irqchip: armada-3...
253
  	if (!is_percpu_irq(hw))
600468d06   Gregory CLEMENT   arm: mvebu: Fix t...
254
255
256
257
  		writel(hw, per_cpu_int_base +
  			ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  	else
  		writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
258
  	irq_set_status_flags(virq, IRQ_LEVEL);
3a6f08a37   Gregory CLEMENT   arm: mvebu: Add s...
259

2c299de52   Ezequiel Garcia   irqchip: armada-3...
260
  	if (is_percpu_irq(hw)) {
3a6f08a37   Gregory CLEMENT   arm: mvebu: Add s...
261
262
263
264
265
266
267
268
  		irq_set_percpu_devid(virq);
  		irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
  					handle_percpu_devid_irq);
  
  	} else {
  		irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
  					handle_level_irq);
  	}
d17cab445   Rob Herring   irqchip: Kill off...
269
  	irq_set_probe(virq);
353d6d6c8   Thomas Petazzoni   irqchip/armada-37...
270
  	irq_clear_status_flags(virq, IRQ_NOAUTOEN);
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
271
272
273
  
  	return 0;
  }
d7df84b3c   Thomas Petazzoni   irqchip: irq-arma...
274
  static void armada_xp_mpic_smp_cpu_init(void)
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
275
  {
b73842b75   Thomas Petazzoni   irqchip: armada-3...
276
277
278
279
280
281
282
283
  	u32 control;
  	int nr_irqs, i;
  
  	control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
  	nr_irqs = (control >> 2) & 0x3ff;
  
  	for (i = 0; i < nr_irqs; i++)
  		writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS);
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
284
285
286
287
  	/* Clear pending IPIs */
  	writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
  
  	/* Enable first 8 IPIs */
5ec69017c   Thomas Petazzoni   irqchip: armada-3...
288
  	writel(IPI_DOORBELL_MASK, per_cpu_int_base +
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
289
290
291
292
293
  		ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
  
  	/* Unmask IPI interrupt */
  	writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  }
d7df84b3c   Thomas Petazzoni   irqchip: irq-arma...
294

28da06dfd   Maxime Ripard   irqchip: armada-3...
295
296
297
298
299
300
301
302
  static void armada_xp_mpic_perf_init(void)
  {
  	unsigned long cpuid = cpu_logical_map(smp_processor_id());
  
  	/* Enable Performance Counter Overflow interrupts */
  	writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
  	       per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
  }
933a24b06   Ezequiel Garcia   irqchip: armada-3...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  #ifdef CONFIG_SMP
  static void armada_mpic_send_doorbell(const struct cpumask *mask,
  				      unsigned int irq)
  {
  	int cpu;
  	unsigned long map = 0;
  
  	/* Convert our logical CPU mask into a physical one. */
  	for_each_cpu(cpu, mask)
  		map |= 1 << cpu_logical_map(cpu);
  
  	/*
  	 * Ensure that stores to Normal memory are visible to the
  	 * other CPUs before issuing the IPI.
  	 */
  	dsb();
  
  	/* submit softirq */
  	writel((map << 8) | irq, main_int_base +
  		ARMADA_370_XP_SW_TRIG_INT_OFFS);
  }
cb5ff2d24   Richard Cochran   irqchip/armada-37...
324
  static int armada_xp_mpic_starting_cpu(unsigned int cpu)
d7df84b3c   Thomas Petazzoni   irqchip: irq-arma...
325
  {
cb5ff2d24   Richard Cochran   irqchip/armada-37...
326
327
328
  	armada_xp_mpic_perf_init();
  	armada_xp_mpic_smp_cpu_init();
  	return 0;
d7df84b3c   Thomas Petazzoni   irqchip: irq-arma...
329
  }
cb5ff2d24   Richard Cochran   irqchip/armada-37...
330
  static int mpic_cascaded_starting_cpu(unsigned int cpu)
5724be846   Maxime Ripard   irqchip: armada-3...
331
  {
cb5ff2d24   Richard Cochran   irqchip/armada-37...
332
333
334
  	armada_xp_mpic_perf_init();
  	enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
  	return 0;
5724be846   Maxime Ripard   irqchip: armada-3...
335
  }
c76c15e6f   Arnd Bergmann   irqchip/armada: A...
336
  #endif
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
337

960097365   Krzysztof Kozlowski   irqchip: Constify...
338
  static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
339
340
341
  	.map = armada_370_xp_mpic_irq_map,
  	.xlate = irq_domain_xlate_onecell,
  };
9b8cf779f   Ezequiel Garcia   irqchip: armada-3...
342
  #ifdef CONFIG_PCI_MSI
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
343
  static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
9b8cf779f   Ezequiel Garcia   irqchip: armada-3...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
  {
  	u32 msimask, msinr;
  
  	msimask = readl_relaxed(per_cpu_int_base +
  				ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
  		& PCI_MSI_DOORBELL_MASK;
  
  	writel(~msimask, per_cpu_int_base +
  	       ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
  
  	for (msinr = PCI_MSI_DOORBELL_START;
  	     msinr < PCI_MSI_DOORBELL_END; msinr++) {
  		int irq;
  
  		if (!(msimask & BIT(msinr)))
  			continue;
e89c6a06b   Marc Zyngier   irqchip: armada-3...
360
  		if (is_chained) {
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
361
  			irq = irq_find_mapping(armada_370_xp_msi_inner_domain,
0636bab67   Thomas Petazzoni   irqchip/armada-37...
362
  					       msinr - PCI_MSI_DOORBELL_START);
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
363
  			generic_handle_irq(irq);
e89c6a06b   Marc Zyngier   irqchip: armada-3...
364
  		} else {
0636bab67   Thomas Petazzoni   irqchip/armada-37...
365
  			irq = msinr - PCI_MSI_DOORBELL_START;
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
366
  			handle_domain_irq(armada_370_xp_msi_inner_domain,
e89c6a06b   Marc Zyngier   irqchip: armada-3...
367
368
  					  irq, regs);
  		}
9b8cf779f   Ezequiel Garcia   irqchip: armada-3...
369
370
371
  	}
  }
  #else
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
372
  static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {}
9b8cf779f   Ezequiel Garcia   irqchip: armada-3...
373
  #endif
bd0b9ac40   Thomas Gleixner   genirq: Remove ir...
374
  static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc)
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
375
  {
5b29264c6   Jiang Liu   irqchip: Use irq_...
376
  	struct irq_chip *chip = irq_desc_get_chip(desc);
758e83667   Grzegorz Jaszczyk   irqchip: armada-3...
377
  	unsigned long irqmap, irqn, irqsrc, cpuid;
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
378
379
380
381
382
  	unsigned int cascade_irq;
  
  	chained_irq_enter(chip, desc);
  
  	irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE);
758e83667   Grzegorz Jaszczyk   irqchip: armada-3...
383
  	cpuid = cpu_logical_map(smp_processor_id());
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
384
385
  
  	for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
758e83667   Grzegorz Jaszczyk   irqchip: armada-3...
386
387
388
389
390
391
392
393
394
395
396
397
398
  		irqsrc = readl_relaxed(main_int_base +
  				       ARMADA_370_XP_INT_SOURCE_CTL(irqn));
  
  		/* Check if the interrupt is not masked on current CPU.
  		 * Test IRQ (0-1) and FIQ (8-9) mask bits.
  		 */
  		if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid)))
  			continue;
  
  		if (irqn == 1) {
  			armada_370_xp_handle_msi_irq(NULL, true);
  			continue;
  		}
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
399
400
401
402
403
404
  		cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn);
  		generic_handle_irq(cascade_irq);
  	}
  
  	chained_irq_exit(chip, desc);
  }
8783dd3a3   Stephen Boyd   irqchip: Remove a...
405
  static void __exception_irq_entry
9339d432f   Thomas Petazzoni   irqchip: move IRQ...
406
  armada_370_xp_handle_irq(struct pt_regs *regs)
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
407
408
409
410
411
412
413
  {
  	u32 irqstat, irqnr;
  
  	do {
  		irqstat = readl_relaxed(per_cpu_int_base +
  					ARMADA_370_XP_CPU_INTACK_OFFS);
  		irqnr = irqstat & 0x3FF;
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
414
415
  		if (irqnr > 1022)
  			break;
31f614edb   Thomas Petazzoni   irqchip: armada-3...
416
  		if (irqnr > 1) {
e89c6a06b   Marc Zyngier   irqchip: armada-3...
417
418
  			handle_domain_irq(armada_370_xp_mpic_domain,
  					  irqnr, regs);
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
419
420
  			continue;
  		}
31f614edb   Thomas Petazzoni   irqchip: armada-3...
421

31f614edb   Thomas Petazzoni   irqchip: armada-3...
422
  		/* MSI handling */
9b8cf779f   Ezequiel Garcia   irqchip: armada-3...
423
  		if (irqnr == 1)
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
424
  			armada_370_xp_handle_msi_irq(regs, false);
31f614edb   Thomas Petazzoni   irqchip: armada-3...
425

344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
426
427
428
429
430
431
432
  #ifdef CONFIG_SMP
  		/* IPI Handling */
  		if (irqnr == 0) {
  			u32 ipimask, ipinr;
  
  			ipimask = readl_relaxed(per_cpu_int_base +
  						ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
5ec69017c   Thomas Petazzoni   irqchip: armada-3...
433
  				& IPI_DOORBELL_MASK;
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
434

a6f089e95   Lior Amsalem   irqchip: armada-3...
435
  			writel(~ipimask, per_cpu_int_base +
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
436
437
438
  				ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
  
  			/* Handle all pending doorbells */
5ec69017c   Thomas Petazzoni   irqchip: armada-3...
439
440
  			for (ipinr = IPI_DOORBELL_START;
  			     ipinr < IPI_DOORBELL_END; ipinr++) {
344e873e5   Gregory CLEMENT   arm: mvebu: Add I...
441
442
443
444
445
446
  				if (ipimask & (0x1 << ipinr))
  					handle_IPI(ipinr, regs);
  			}
  			continue;
  		}
  #endif
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
447

9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
448
449
  	} while (1);
  }
0f077eb5c   Thomas Petazzoni   irqchip: armada-3...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
  static int armada_370_xp_mpic_suspend(void)
  {
  	doorbell_mask_reg = readl(per_cpu_int_base +
  				  ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
  	return 0;
  }
  
  static void armada_370_xp_mpic_resume(void)
  {
  	int nirqs;
  	irq_hw_number_t irq;
  
  	/* Re-enable interrupts */
  	nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff;
  	for (irq = 0; irq < nirqs; irq++) {
  		struct irq_data *data;
  		int virq;
  
  		virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq);
  		if (virq == 0)
  			continue;
080481f97   Maxime Ripard   irqchip: armada-3...
471
  		if (!is_percpu_irq(irq))
0f077eb5c   Thomas Petazzoni   irqchip: armada-3...
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  			writel(irq, per_cpu_int_base +
  			       ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  		else
  			writel(irq, main_int_base +
  			       ARMADA_370_XP_INT_SET_ENABLE_OFFS);
  
  		data = irq_get_irq_data(virq);
  		if (!irqd_irq_disabled(data))
  			armada_370_xp_irq_unmask(data);
  	}
  
  	/* Reconfigure doorbells for IPIs and MSIs */
  	writel(doorbell_mask_reg,
  	       per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
  	if (doorbell_mask_reg & IPI_DOORBELL_MASK)
  		writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  	if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
  		writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
  }
6c8809024   Ben Dooks   irqchip/armada-37...
491
  static struct syscore_ops armada_370_xp_mpic_syscore_ops = {
0f077eb5c   Thomas Petazzoni   irqchip: armada-3...
492
493
494
  	.suspend	= armada_370_xp_mpic_suspend,
  	.resume		= armada_370_xp_mpic_resume,
  };
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
495
496
  static int __init armada_370_xp_mpic_of_init(struct device_node *node,
  					     struct device_node *parent)
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
497
  {
627dfcc24   Thomas Petazzoni   irqchip: armada-3...
498
  	struct resource main_int_res, per_cpu_int_res;
5724be846   Maxime Ripard   irqchip: armada-3...
499
  	int nr_irqs, i;
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
500
  	u32 control;
627dfcc24   Thomas Petazzoni   irqchip: armada-3...
501
502
  	BUG_ON(of_address_to_resource(node, 0, &main_int_res));
  	BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res));
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
503

627dfcc24   Thomas Petazzoni   irqchip: armada-3...
504
505
506
507
508
509
510
511
512
  	BUG_ON(!request_mem_region(main_int_res.start,
  				   resource_size(&main_int_res),
  				   node->full_name));
  	BUG_ON(!request_mem_region(per_cpu_int_res.start,
  				   resource_size(&per_cpu_int_res),
  				   node->full_name));
  
  	main_int_base = ioremap(main_int_res.start,
  				resource_size(&main_int_res));
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
513
  	BUG_ON(!main_int_base);
627dfcc24   Thomas Petazzoni   irqchip: armada-3...
514
515
516
  
  	per_cpu_int_base = ioremap(per_cpu_int_res.start,
  				   resource_size(&per_cpu_int_res));
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
517
518
519
  	BUG_ON(!per_cpu_int_base);
  
  	control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
b73842b75   Thomas Petazzoni   irqchip: armada-3...
520
521
522
523
  	nr_irqs = (control >> 2) & 0x3ff;
  
  	for (i = 0; i < nr_irqs; i++)
  		writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
524
525
  
  	armada_370_xp_mpic_domain =
b73842b75   Thomas Petazzoni   irqchip: armada-3...
526
  		irq_domain_add_linear(node, nr_irqs,
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
527
  				&armada_370_xp_mpic_irq_ops, NULL);
627dfcc24   Thomas Petazzoni   irqchip: armada-3...
528
  	BUG_ON(!armada_370_xp_mpic_domain);
fcc392d50   Thomas Petazzoni   irqchip/armada-37...
529
  	armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED;
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
530

933a24b06   Ezequiel Garcia   irqchip: armada-3...
531
  	/* Setup for the boot CPU */
28da06dfd   Maxime Ripard   irqchip: armada-3...
532
  	armada_xp_mpic_perf_init();
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
533
  	armada_xp_mpic_smp_cpu_init();
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
534

31f614edb   Thomas Petazzoni   irqchip: armada-3...
535
  	armada_370_xp_msi_init(node, main_int_res.start);
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
536
537
538
539
  	parent_irq = irq_of_parse_and_map(node, 0);
  	if (parent_irq <= 0) {
  		irq_set_default_host(armada_370_xp_mpic_domain);
  		set_handle_irq(armada_370_xp_handle_irq);
ef37d337e   Thomas Petazzoni   irqchip: irq-arma...
540
541
  #ifdef CONFIG_SMP
  		set_smp_cross_call(armada_mpic_send_doorbell);
cb5ff2d24   Richard Cochran   irqchip/armada-37...
542
543
544
  		cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING,
  					  "AP_IRQ_ARMADA_XP_STARTING",
  					  armada_xp_mpic_starting_cpu, NULL);
ef37d337e   Thomas Petazzoni   irqchip: irq-arma...
545
  #endif
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
546
  	} else {
5724be846   Maxime Ripard   irqchip: armada-3...
547
  #ifdef CONFIG_SMP
cb5ff2d24   Richard Cochran   irqchip/armada-37...
548
549
550
  		cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_CASC_STARTING,
  					  "AP_IRQ_ARMADA_CASC_STARTING",
  					  mpic_cascaded_starting_cpu, NULL);
5724be846   Maxime Ripard   irqchip: armada-3...
551
  #endif
bc69b8adf   Ezequiel Garcia   irqchip: armada-3...
552
553
554
  		irq_set_chained_handler(parent_irq,
  					armada_370_xp_mpic_handle_cascade_irq);
  	}
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
555

0f077eb5c   Thomas Petazzoni   irqchip: armada-3...
556
  	register_syscore_ops(&armada_370_xp_mpic_syscore_ops);
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
557
  	return 0;
9ae6f740b   Thomas Petazzoni   arm: mach-mvebu: ...
558
  }
b313ada8c   Thomas Petazzoni   irqchip: armada-3...
559

9339d432f   Thomas Petazzoni   irqchip: move IRQ...
560
  IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init);