Blame view

drivers/irqchip/irq-mxs.c 6.02 KB
162163332   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
289569f90   Shawn Guo   ARM: mxs: Add int...
2
3
  /*
   * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
4
5
   * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
   *	Add Alphascale ASM9260 support.
289569f90   Shawn Guo   ARM: mxs: Add int...
6
7
8
9
10
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/irq.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
11
  #include <linux/irqchip.h>
83a84efce   Shawn Guo   ARM: mxs: adopt i...
12
  #include <linux/irqdomain.h>
289569f90   Shawn Guo   ARM: mxs: Add int...
13
  #include <linux/io.h>
83a84efce   Shawn Guo   ARM: mxs: adopt i...
14
  #include <linux/of.h>
8256aa712   Shawn Guo   ARM: mxs: get ico...
15
  #include <linux/of_address.h>
83a84efce   Shawn Guo   ARM: mxs: adopt i...
16
  #include <linux/of_irq.h>
cec6bae8c   Shawn Guo   ARM: mxs: call st...
17
  #include <linux/stmp_device.h>
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
18
  #include <asm/exception.h>
289569f90   Shawn Guo   ARM: mxs: Add int...
19

7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
20
  #include "alphascale_asm9260-icoll.h"
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
21
22
23
24
25
26
27
28
29
  /*
   * this device provide 4 offsets for each register:
   * 0x0 - plain read write mode
   * 0x4 - set mode, OR logic.
   * 0x8 - clr mode, XOR logic.
   * 0xc - togle mode.
   */
  #define SET_REG 4
  #define CLR_REG 8
289569f90   Shawn Guo   ARM: mxs: Add int...
30
31
32
  #define HW_ICOLL_VECTOR				0x0000
  #define HW_ICOLL_LEVELACK			0x0010
  #define HW_ICOLL_CTRL				0x0020
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
33
  #define HW_ICOLL_STAT_OFFSET			0x0070
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
34
35
36
  #define HW_ICOLL_INTERRUPT0			0x0120
  #define HW_ICOLL_INTERRUPTn(n)			((n) * 0x10)
  #define BM_ICOLL_INTR_ENABLE			BIT(2)
289569f90   Shawn Guo   ARM: mxs: Add int...
37
  #define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0	0x1
83a84efce   Shawn Guo   ARM: mxs: adopt i...
38
  #define ICOLL_NUM_IRQS		128
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
39
40
41
42
  enum icoll_type {
  	ICOLL,
  	ASM9260_ICOLL,
  };
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
43
44
45
46
47
48
  struct icoll_priv {
  	void __iomem *vector;
  	void __iomem *levelack;
  	void __iomem *ctrl;
  	void __iomem *stat;
  	void __iomem *intr;
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
49
50
  	void __iomem *clear;
  	enum icoll_type type;
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
51
52
53
  };
  
  static struct icoll_priv icoll_priv;
83a84efce   Shawn Guo   ARM: mxs: adopt i...
54
  static struct irq_domain *icoll_domain;
289569f90   Shawn Guo   ARM: mxs: Add int...
55

7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  /* calculate bit offset depending on number of intterupt per register */
  static u32 icoll_intr_bitshift(struct irq_data *d, u32 bit)
  {
  	/*
  	 * mask lower part of hwirq to convert it
  	 * in 0, 1, 2 or 3 and then multiply it by 8 (or shift by 3)
  	 */
  	return bit << ((d->hwirq & 3) << 3);
  }
  
  /* calculate mem offset depending on number of intterupt per register */
  static void __iomem *icoll_intr_reg(struct irq_data *d)
  {
  	/* offset = hwirq / intr_per_reg * 0x10 */
  	return icoll_priv.intr + ((d->hwirq >> 2) * 0x10);
  }
bf0c11183   Uwe Kleine-König   ARM: 6744/1: mxs:...
72
  static void icoll_ack_irq(struct irq_data *d)
289569f90   Shawn Guo   ARM: mxs: Add int...
73
74
75
76
77
78
79
  {
  	/*
  	 * The Interrupt Collector is able to prioritize irqs.
  	 * Currently only level 0 is used. So acking can use
  	 * BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
  	 */
  	__raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
80
  			icoll_priv.levelack);
289569f90   Shawn Guo   ARM: mxs: Add int...
81
  }
bf0c11183   Uwe Kleine-König   ARM: 6744/1: mxs:...
82
  static void icoll_mask_irq(struct irq_data *d)
289569f90   Shawn Guo   ARM: mxs: Add int...
83
  {
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
84
85
  	__raw_writel(BM_ICOLL_INTR_ENABLE,
  			icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
289569f90   Shawn Guo   ARM: mxs: Add int...
86
  }
bf0c11183   Uwe Kleine-König   ARM: 6744/1: mxs:...
87
  static void icoll_unmask_irq(struct irq_data *d)
289569f90   Shawn Guo   ARM: mxs: Add int...
88
  {
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
89
90
  	__raw_writel(BM_ICOLL_INTR_ENABLE,
  			icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
289569f90   Shawn Guo   ARM: mxs: Add int...
91
  }
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  static void asm9260_mask_irq(struct irq_data *d)
  {
  	__raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
  			icoll_intr_reg(d) + CLR_REG);
  }
  
  static void asm9260_unmask_irq(struct irq_data *d)
  {
  	__raw_writel(ASM9260_BM_CLEAR_BIT(d->hwirq),
  		     icoll_priv.clear +
  		     ASM9260_HW_ICOLL_CLEARn(d->hwirq));
  
  	__raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
  			icoll_intr_reg(d) + SET_REG);
  }
289569f90   Shawn Guo   ARM: mxs: Add int...
107
  static struct irq_chip mxs_icoll_chip = {
bf0c11183   Uwe Kleine-König   ARM: 6744/1: mxs:...
108
109
110
  	.irq_ack = icoll_ack_irq,
  	.irq_mask = icoll_mask_irq,
  	.irq_unmask = icoll_unmask_irq,
88e20c74e   Stefan Wahren   irqchip/mxs: Enab...
111
112
  	.flags = IRQCHIP_MASK_ON_SUSPEND |
  		 IRQCHIP_SKIP_SET_WAKE,
289569f90   Shawn Guo   ARM: mxs: Add int...
113
  };
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
114
115
116
117
  static struct irq_chip asm9260_icoll_chip = {
  	.irq_ack = icoll_ack_irq,
  	.irq_mask = asm9260_mask_irq,
  	.irq_unmask = asm9260_unmask_irq,
88e20c74e   Stefan Wahren   irqchip/mxs: Enab...
118
119
  	.flags = IRQCHIP_MASK_ON_SUSPEND |
  		 IRQCHIP_SKIP_SET_WAKE,
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
120
  };
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
121
122
123
  asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
  {
  	u32 irqnr;
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
124
125
  	irqnr = __raw_readl(icoll_priv.stat);
  	__raw_writel(irqnr, icoll_priv.vector);
b3410e5f4   Marc Zyngier   irqchip: mxs: Con...
126
  	handle_domain_irq(icoll_domain, irqnr, regs);
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
127
  }
83a84efce   Shawn Guo   ARM: mxs: adopt i...
128
129
  static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
  				irq_hw_number_t hw)
289569f90   Shawn Guo   ARM: mxs: Add int...
130
  {
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
131
132
133
134
135
136
137
138
  	struct irq_chip *chip;
  
  	if (icoll_priv.type == ICOLL)
  		chip = &mxs_icoll_chip;
  	else
  		chip = &asm9260_icoll_chip;
  
  	irq_set_chip_and_handler(virq, chip, handle_level_irq);
83a84efce   Shawn Guo   ARM: mxs: adopt i...
139
140
141
  
  	return 0;
  }
289569f90   Shawn Guo   ARM: mxs: Add int...
142

960097365   Krzysztof Kozlowski   irqchip: Constify...
143
  static const struct irq_domain_ops icoll_irq_domain_ops = {
83a84efce   Shawn Guo   ARM: mxs: adopt i...
144
145
146
  	.map = icoll_irq_domain_map,
  	.xlate = irq_domain_xlate_onecell,
  };
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
147
148
149
150
151
152
153
  static void __init icoll_add_domain(struct device_node *np,
  			  int num)
  {
  	icoll_domain = irq_domain_add_linear(np, num,
  					     &icoll_irq_domain_ops, NULL);
  
  	if (!icoll_domain)
e81f54c66   Rob Herring   irqchip: Convert ...
154
  		panic("%pOF: unable to create irq domain", np);
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
155
156
157
  }
  
  static void __iomem * __init icoll_init_iobase(struct device_node *np)
83a84efce   Shawn Guo   ARM: mxs: adopt i...
158
  {
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
159
160
161
  	void __iomem *icoll_base;
  
  	icoll_base = of_io_request_and_map(np, 0, np->name);
edf8fcdc6   Vladimir Zapolskiy   irqchip/mxs: Fix ...
162
  	if (IS_ERR(icoll_base))
e81f54c66   Rob Herring   irqchip: Convert ...
163
  		panic("%pOF: unable to map resource", np);
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
164
165
166
167
168
169
170
  	return icoll_base;
  }
  
  static int __init icoll_of_init(struct device_node *np,
  			  struct device_node *interrupt_parent)
  {
  	void __iomem *icoll_base;
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
171
  	icoll_priv.type = ICOLL;
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
172
173
174
175
176
177
  	icoll_base		= icoll_init_iobase(np);
  	icoll_priv.vector	= icoll_base + HW_ICOLL_VECTOR;
  	icoll_priv.levelack	= icoll_base + HW_ICOLL_LEVELACK;
  	icoll_priv.ctrl		= icoll_base + HW_ICOLL_CTRL;
  	icoll_priv.stat		= icoll_base + HW_ICOLL_STAT_OFFSET;
  	icoll_priv.intr		= icoll_base + HW_ICOLL_INTERRUPT0;
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
178
  	icoll_priv.clear	= NULL;
8256aa712   Shawn Guo   ARM: mxs: get ico...
179

289569f90   Shawn Guo   ARM: mxs: Add int...
180
181
182
183
  	/*
  	 * Interrupt Collector reset, which initializes the priority
  	 * for each irq to level 0.
  	 */
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
184
  	stmp_reset_block(icoll_priv.ctrl);
289569f90   Shawn Guo   ARM: mxs: Add int...
185

25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
186
  	icoll_add_domain(np, ICOLL_NUM_IRQS);
e59a8451b   Oleksij Rempel   irqchip/mxs: Pani...
187
188
  
  	return 0;
83a84efce   Shawn Guo   ARM: mxs: adopt i...
189
  }
6a8e95b07   Shawn Guo   ARM: mxs: move ic...
190
  IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  
  static int __init asm9260_of_init(struct device_node *np,
  			  struct device_node *interrupt_parent)
  {
  	void __iomem *icoll_base;
  	int i;
  
  	icoll_priv.type = ASM9260_ICOLL;
  
  	icoll_base = icoll_init_iobase(np);
  	icoll_priv.vector	= icoll_base + ASM9260_HW_ICOLL_VECTOR;
  	icoll_priv.levelack	= icoll_base + ASM9260_HW_ICOLL_LEVELACK;
  	icoll_priv.ctrl		= icoll_base + ASM9260_HW_ICOLL_CTRL;
  	icoll_priv.stat		= icoll_base + ASM9260_HW_ICOLL_STAT_OFFSET;
  	icoll_priv.intr		= icoll_base + ASM9260_HW_ICOLL_INTERRUPT0;
  	icoll_priv.clear	= icoll_base + ASM9260_HW_ICOLL_CLEAR0;
  
  	writel_relaxed(ASM9260_BM_CTRL_IRQ_ENABLE,
  			icoll_priv.ctrl);
  	/*
  	 * ASM9260 don't provide reset bit. So, we need to set level 0
  	 * manually.
  	 */
  	for (i = 0; i < 16 * 0x10; i += 0x10)
  		writel(0, icoll_priv.intr + i);
  
  	icoll_add_domain(np, ASM9260_NUM_IRQS);
c5b635203   Oleksij Rempel   irqchip/mxs: Add ...
218
  	set_handle_irq(icoll_handle_irq);
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
219
220
221
222
  
  	return 0;
  }
  IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);