Blame view

drivers/irqchip/irq-mxs.c 6.57 KB
289569f90   Shawn Guo   ARM: mxs: Add int...
1
2
  /*
   * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
3
4
   * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
   *	Add Alphascale ASM9260 support.
289569f90   Shawn Guo   ARM: mxs: Add int...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License along
   * with this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/irq.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
24
  #include <linux/irqchip.h>
83a84efce   Shawn Guo   ARM: mxs: adopt i...
25
  #include <linux/irqdomain.h>
289569f90   Shawn Guo   ARM: mxs: Add int...
26
  #include <linux/io.h>
83a84efce   Shawn Guo   ARM: mxs: adopt i...
27
  #include <linux/of.h>
8256aa712   Shawn Guo   ARM: mxs: get ico...
28
  #include <linux/of_address.h>
83a84efce   Shawn Guo   ARM: mxs: adopt i...
29
  #include <linux/of_irq.h>
cec6bae8c   Shawn Guo   ARM: mxs: call st...
30
  #include <linux/stmp_device.h>
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
31
  #include <asm/exception.h>
289569f90   Shawn Guo   ARM: mxs: Add int...
32

7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
33
  #include "alphascale_asm9260-icoll.h"
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
34
35
36
37
38
39
40
41
42
  /*
   * 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...
43
44
45
  #define HW_ICOLL_VECTOR				0x0000
  #define HW_ICOLL_LEVELACK			0x0010
  #define HW_ICOLL_CTRL				0x0020
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
46
  #define HW_ICOLL_STAT_OFFSET			0x0070
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
47
48
49
  #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...
50
  #define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0	0x1
83a84efce   Shawn Guo   ARM: mxs: adopt i...
51
  #define ICOLL_NUM_IRQS		128
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
52
53
54
55
  enum icoll_type {
  	ICOLL,
  	ASM9260_ICOLL,
  };
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
56
57
58
59
60
61
  struct icoll_priv {
  	void __iomem *vector;
  	void __iomem *levelack;
  	void __iomem *ctrl;
  	void __iomem *stat;
  	void __iomem *intr;
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
62
63
  	void __iomem *clear;
  	enum icoll_type type;
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
64
65
66
  };
  
  static struct icoll_priv icoll_priv;
83a84efce   Shawn Guo   ARM: mxs: adopt i...
67
  static struct irq_domain *icoll_domain;
289569f90   Shawn Guo   ARM: mxs: Add int...
68

7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  /* 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:...
85
  static void icoll_ack_irq(struct irq_data *d)
289569f90   Shawn Guo   ARM: mxs: Add int...
86
87
88
89
90
91
92
  {
  	/*
  	 * 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...
93
  			icoll_priv.levelack);
289569f90   Shawn Guo   ARM: mxs: Add int...
94
  }
bf0c11183   Uwe Kleine-König   ARM: 6744/1: mxs:...
95
  static void icoll_mask_irq(struct irq_data *d)
289569f90   Shawn Guo   ARM: mxs: Add int...
96
  {
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
97
98
  	__raw_writel(BM_ICOLL_INTR_ENABLE,
  			icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
289569f90   Shawn Guo   ARM: mxs: Add int...
99
  }
bf0c11183   Uwe Kleine-König   ARM: 6744/1: mxs:...
100
  static void icoll_unmask_irq(struct irq_data *d)
289569f90   Shawn Guo   ARM: mxs: Add int...
101
  {
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
102
103
  	__raw_writel(BM_ICOLL_INTR_ENABLE,
  			icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
289569f90   Shawn Guo   ARM: mxs: Add int...
104
  }
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  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...
120
  static struct irq_chip mxs_icoll_chip = {
bf0c11183   Uwe Kleine-König   ARM: 6744/1: mxs:...
121
122
123
  	.irq_ack = icoll_ack_irq,
  	.irq_mask = icoll_mask_irq,
  	.irq_unmask = icoll_unmask_irq,
289569f90   Shawn Guo   ARM: mxs: Add int...
124
  };
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
125
126
127
128
129
  static struct irq_chip asm9260_icoll_chip = {
  	.irq_ack = icoll_ack_irq,
  	.irq_mask = asm9260_mask_irq,
  	.irq_unmask = asm9260_unmask_irq,
  };
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
130
131
132
  asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
  {
  	u32 irqnr;
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
133
134
  	irqnr = __raw_readl(icoll_priv.stat);
  	__raw_writel(irqnr, icoll_priv.vector);
b3410e5f4   Marc Zyngier   irqchip: mxs: Con...
135
  	handle_domain_irq(icoll_domain, irqnr, regs);
4e0a1b8c0   Shawn Guo   ARM: mxs: select ...
136
  }
83a84efce   Shawn Guo   ARM: mxs: adopt i...
137
138
  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...
139
  {
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
140
141
142
143
144
145
146
147
  	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...
148
149
150
  
  	return 0;
  }
289569f90   Shawn Guo   ARM: mxs: Add int...
151

960097365   Krzysztof Kozlowski   irqchip: Constify...
152
  static const struct irq_domain_ops icoll_irq_domain_ops = {
83a84efce   Shawn Guo   ARM: mxs: adopt i...
153
154
155
  	.map = icoll_irq_domain_map,
  	.xlate = irq_domain_xlate_onecell,
  };
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
156
157
158
159
160
161
162
163
164
165
166
  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)
  		panic("%s: unable to create irq domain", np->full_name);
  }
  
  static void __iomem * __init icoll_init_iobase(struct device_node *np)
83a84efce   Shawn Guo   ARM: mxs: adopt i...
167
  {
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
168
169
170
  	void __iomem *icoll_base;
  
  	icoll_base = of_io_request_and_map(np, 0, np->name);
edf8fcdc6   Vladimir Zapolskiy   irqchip/mxs: Fix ...
171
  	if (IS_ERR(icoll_base))
e59a8451b   Oleksij Rempel   irqchip/mxs: Pani...
172
  		panic("%s: unable to map resource", np->full_name);
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
173
174
175
176
177
178
179
  	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 ...
180
  	icoll_priv.type = ICOLL;
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
181
182
183
184
185
186
  	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 ...
187
  	icoll_priv.clear	= NULL;
8256aa712   Shawn Guo   ARM: mxs: get ico...
188

289569f90   Shawn Guo   ARM: mxs: Add int...
189
190
191
192
  	/*
  	 * Interrupt Collector reset, which initializes the priority
  	 * for each irq to level 0.
  	 */
25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
193
  	stmp_reset_block(icoll_priv.ctrl);
289569f90   Shawn Guo   ARM: mxs: Add int...
194

25e34b443   Oleksij Rempel   irqchip/mxs: Prep...
195
  	icoll_add_domain(np, ICOLL_NUM_IRQS);
e59a8451b   Oleksij Rempel   irqchip/mxs: Pani...
196
197
  
  	return 0;
83a84efce   Shawn Guo   ARM: mxs: adopt i...
198
  }
6a8e95b07   Shawn Guo   ARM: mxs: move ic...
199
  IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  
  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 ...
227
  	set_handle_irq(icoll_handle_irq);
7e4ac676e   Oleksij Rempel   irqchip/mxs: Add ...
228
229
230
231
  
  	return 0;
  }
  IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);