summaryrefslogtreecommitdiffstats
path: root/arch/mips/include/asm/mips-gic.h
blob: 084cac1c5ea2c8f6472af5fbe5ca4b152cf09447 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2017 Imagination Technologies
 * Author: Paul Burton <paul.burton@mips.com>
 */

#ifndef __MIPS_ASM_MIPS_CPS_H__
# error Please include asm/mips-cps.h rather than asm/mips-gic.h
#endif

#ifndef __MIPS_ASM_MIPS_GIC_H__
#define __MIPS_ASM_MIPS_GIC_H__

#include <linux/bitops.h>

/* The base address of the GIC registers */
extern void __iomem *mips_gic_base;

/* Offsets from the GIC base address to various control blocks */
#define MIPS_GIC_SHARED_OFS	0x00000
#define MIPS_GIC_SHARED_SZ	0x08000
#define MIPS_GIC_LOCAL_OFS	0x08000
#define MIPS_GIC_LOCAL_SZ	0x04000
#define MIPS_GIC_REDIR_OFS	0x0c000
#define MIPS_GIC_REDIR_SZ	0x04000
#define MIPS_GIC_USER_OFS	0x10000
#define MIPS_GIC_USER_SZ	0x10000

/* For read-only shared registers */
#define GIC_ACCESSOR_RO(sz, off, name)					\
	CPS_ACCESSOR_RO(gic, sz, MIPS_GIC_SHARED_OFS + off, name)

/* For read-write shared registers */
#define GIC_ACCESSOR_RW(sz, off, name)					\
	CPS_ACCESSOR_RW(gic, sz, MIPS_GIC_SHARED_OFS + off, name)

/* For read-only local registers */
#define GIC_VX_ACCESSOR_RO(sz, off, name)				\
	CPS_ACCESSOR_RO(gic, sz, MIPS_GIC_LOCAL_OFS + off, vl_##name)	\
	CPS_ACCESSOR_RO(gic, sz, MIPS_GIC_REDIR_OFS + off, vo_##name)

/* For read-write local registers */
#define GIC_VX_ACCESSOR_RW(sz, off, name)				\
	CPS_ACCESSOR_RW(gic, sz, MIPS_GIC_LOCAL_OFS + off, vl_##name)	\
	CPS_ACCESSOR_RW(gic, sz, MIPS_GIC_REDIR_OFS + off, vo_##name)

/* For read-only shared per-interrupt registers */
#define GIC_ACCESSOR_RO_INTR_REG(sz, off, stride, name)			\
static inline void __iomem *addr_gic_##name(unsigned int intr)		\
{									\
	return mips_gic_base + (off) + (intr * (stride));		\
}									\
									\
static inline unsigned int read_gic_##name(unsigned int intr)		\
{									\
	BUILD_BUG_ON(sz != 32);						\
	return __raw_readl(addr_gic_##name(intr));			\
}

/* For read-write shared per-interrupt registers */
#define GIC_ACCESSOR_RW_INTR_REG(sz, off, stride, name)			\
	GIC_ACCESSOR_RO_INTR_REG(sz, off, stride, name)			\
									\
static inline void write_gic_##name(unsigned int intr,			\
				    unsigned int val)			\
{									\
	BUILD_BUG_ON(sz != 32);						\
	__raw_writel(val, addr_gic_##name(intr));			\
}

/* For read-only local per-interrupt registers */
#define GIC_VX_ACCESSOR_RO_INTR_REG(sz, off, stride, name)		\
	GIC_ACCESSOR_RO_INTR_REG(sz, MIPS_GIC_LOCAL_OFS + off,		\
				 stride, vl_##name)			\
	GIC_ACCESSOR_RO_INTR_REG(sz, MIPS_GIC_REDIR_OFS + off,		\
				 stride, vo_##name)

/* For read-write local per-interrupt registers */
#define GIC_VX_ACCESSOR_RW_INTR_REG(sz, off, stride, name)		\
	GIC_ACCESSOR_RW_INTR_REG(sz, MIPS_GIC_LOCAL_OFS + off,		\
				 stride, vl_##name)			\
	GIC_ACCESSOR_RW_INTR_REG(sz, MIPS_GIC_REDIR_OFS + off,		\
				 stride, vo_##name)

/* For read-only shared bit-per-interrupt registers */
#define GIC_ACCESSOR_RO_INTR_BIT(off, name)				\
static inline void __iomem *addr_gic_##name(void)			\
{									\
	return mips_gic_base + (off);					\
}									\
									\
static inline unsigned int read_gic_##name(unsigned int intr)		\
{									\
	void __iomem *addr = addr_gic_##name();				\
	unsigned int val;						\
									\
	if (mips_cm_is64) {						\
		addr += (intr / 64) * sizeof(uint64_t);			\
		val = __raw_readq(addr) >> intr % 64;			\
	} else {							\
		addr += (intr / 32) * sizeof(uint32_t);			\
		val = __raw_readl(addr) >> intr % 32;			\
	}								\
									\
	return val & 0x1;						\
}

/* For read-write shared bit-per-interrupt registers */
#define GIC_ACCESSOR_RW_INTR_BIT(off, name)				\
	GIC_ACCESSOR_RO_INTR_BIT(off, name)				\
									\
static inline void write_gic_##name(unsigned int intr)			\
{									\
	void __iomem *addr = addr_gic_##name();				\
									\
	if (mips_cm_is64) {						\
		addr += (intr / 64) * sizeof(uint64_t);			\
		__raw_writeq(BIT(intr % 64), addr);			\
	} else {							\
		addr += (intr / 32) * sizeof(uint32_t);			\
		__raw_writel(BIT(intr % 32), addr);			\
	}								\
}									\
									\
static inline void change_gic_##name(unsigned int intr,			\
				     unsigned int val)			\
{									\
	void __iomem *addr = addr_gic_##name();				\
									\
	if (mips_cm_is64) {						\
		uint64_t _val;						\
									\
		addr += (intr / 64) * sizeof(uint64_t);			\
		_val = __raw_readq(addr);				\
		_val &= ~BIT_ULL(intr % 64);				\
		_val |= (uint64_t)val << (intr % 64);			\
		__raw_writeq(_val, addr);				\
	} else {							\
		uint32_t _val;						\
									\
		addr += (intr / 32) * sizeof(uint32_t);			\
		_val = __raw_readl(addr);				\
		_val &= ~BIT(intr % 32);				\
		_val |= val << (intr % 32);				\
		__raw_writel(_val, addr);				\
	}								\
}

/* For read-only local bit-per-interrupt registers */
#define GIC_VX_ACCESSOR_RO_INTR_BIT(sz, off, name)			\
	GIC_ACCESSOR_RO_INTR_BIT(sz, MIPS_GIC_LOCAL_OFS + off,		\
				 vl_##name)				\
	GIC_ACCESSOR_RO_INTR_BIT(sz, MIPS_GIC_REDIR_OFS + off,		\
				 vo_##name)

/* For read-write local bit-per-interrupt registers */
#define GIC_VX_ACCESSOR_RW_INTR_BIT(sz, off, name)			\
	GIC_ACCESSOR_RW_INTR_BIT(sz, MIPS_GIC_LOCAL_OFS + off,		\
				 vl_##name)				\
	GIC_ACCESSOR_RW_INTR_BIT(sz, MIPS_GIC_REDIR_OFS + off,		\
				 vo_##name)

/* GIC_SH_CONFIG - Information about the GIC configuration */
GIC_ACCESSOR_RW(32, 0x000, config)
#define GIC_CONFIG_COUNTSTOP		BIT(28)
#define GIC_CONFIG_COUNTBITS		GENMASK(27, 24)
#define GIC_CONFIG_NUMINTERRUPTS	GENMASK(23, 16)
#define GIC_CONFIG_PVPS			GENMASK(6, 0)

/* GIC_SH_COUNTER - Shared global counter value */
GIC_ACCESSOR_RW(64, 0x010, counter)
GIC_ACCESSOR_RW(32, 0x010, counter_32l)
GIC_ACCESSOR_RW(32, 0x014, counter_32h)

/* GIC_SH_POL_* - Configures interrupt polarity */
GIC_ACCESSOR_RW_INTR_BIT(0x100, pol)
#define GIC_POL_ACTIVE_LOW		0	/* when level triggered */
#define GIC_POL_ACTIVE_HIGH		1	/* when level triggered */
#define GIC_POL_FALLING_EDGE		0	/* when single-edge triggered */
#define GIC_POL_RISING_EDGE		1	/* when single-edge triggered */

/* GIC_SH_TRIG_* - Configures interrupts to be edge or level triggered */
GIC_ACCESSOR_RW_INTR_BIT(0x180, trig)
#define GIC_TRIG_LEVEL			0
#define GIC_TRIG_EDGE			1

/* GIC_SH_DUAL_* - Configures whether interrupts trigger on both edges */
GIC_ACCESSOR_RW_INTR_BIT(0x200, dual)
#define GIC_DUAL_SINGLE			0	/* when edge-triggered */
#define GIC_DUAL_DUAL			1	/* when edge-triggered */

/* GIC_SH_WEDGE - Write an 'edge', ie. trigger an interrupt */
GIC_ACCESSOR_RW(32, 0x280, wedge)
#define GIC_WEDGE_RW			BIT(31)
#define GIC_WEDGE_INTR			GENMASK(7, 0)

/* GIC_SH_RMASK_* - Reset/clear shared interrupt mask bits */
GIC_ACCESSOR_RW_INTR_BIT(0x300, rmask)

/* GIC_SH_SMASK_* - Set shared interrupt mask bits */
GIC_ACCESSOR_RW_INTR_BIT(0x380, smask)

/* GIC_SH_MASK_* - Read the current shared interrupt mask */
GIC_ACCESSOR_RO_INTR_BIT(0x400, mask)

/* GIC_SH_PEND_* - Read currently pending shared interrupts */
GIC_ACCESSOR_RO_INTR_BIT(0x480, pend)

/* GIC_SH_MAPx_PIN - Map shared interrupts to a particular CPU pin */
GIC_ACCESSOR_RW_INTR_REG(32, 0x500, 0x4, map_pin)
#define GIC_MAP_PIN_MAP_TO_PIN		BIT(31)
#define GIC_MAP_PIN_MAP_TO_NMI		BIT(30)
#define GIC_MAP_PIN_MAP			GENMASK(5, 0)

/* GIC_SH_MAPx_VP - Map shared interrupts to a particular Virtual Processor */
GIC_ACCESSOR_RW_INTR_REG(32, 0x2000, 0x20, map_vp)

/* GIC_Vx_CTL - VP-level interrupt control */
GIC_VX_ACCESSOR_RW(32, 0x000, ctl)
#define GIC_VX_CTL_FDC_ROUTABLE		BIT(4)
#define GIC_VX_CTL_SWINT_ROUTABLE	BIT(3)
#define GIC_VX_CTL_PERFCNT_ROUTABLE	BIT(2)
#define GIC_VX_CTL_TIMER_ROUTABLE	BIT(1)
#define GIC_VX_CTL_EIC			BIT(0)

/* GIC_Vx_PEND - Read currently pending local interrupts */
GIC_VX_ACCESSOR_RO(32, 0x004, pend)

/* GIC_Vx_MASK - Read the current local interrupt mask */
GIC_VX_ACCESSOR_RO(32, 0x008, mask)

/* GIC_Vx_RMASK - Reset/clear local interrupt mask bits */
GIC_VX_ACCESSOR_RW(32, 0x00c, rmask)

/* GIC_Vx_SMASK - Set local interrupt mask bits */
GIC_VX_ACCESSOR_RW(32, 0x010, smask)

/* GIC_Vx_*_MAP - Route local interrupts to the desired pins */
GIC_VX_ACCESSOR_RW_INTR_REG(32, 0x040, 0x4, map)

/* GIC_Vx_WD_MAP - Route the local watchdog timer interrupt */
GIC_VX_ACCESSOR_RW(32, 0x040, wd_map)

/* GIC_Vx_COMPARE_MAP - Route the local count/compare interrupt */
GIC_VX_ACCESSOR_RW(32, 0x044, compare_map)

/* GIC_Vx_TIMER_MAP - Route the local CPU timer (cp0 count/compare) interrupt */
GIC_VX_ACCESSOR_RW(32, 0x048, timer_map)

/* GIC_Vx_FDC_MAP - Route the local fast debug channel interrupt */
GIC_VX_ACCESSOR_RW(32, 0x04c, fdc_map)

/* GIC_Vx_PERFCTR_MAP - Route the local performance counter interrupt */
GIC_VX_ACCESSOR_RW(32, 0x050, perfctr_map)

/* GIC_Vx_SWINT0_MAP - Route the local software interrupt 0 */
GIC_VX_ACCESSOR_RW(32, 0x054, swint0_map)

/* GIC_Vx_SWINT1_MAP - Route the local software interrupt 1 */
GIC_VX_ACCESSOR_RW(32, 0x058, swint1_map)

/* GIC_Vx_OTHER - Configure access to other Virtual Processor registers */
GIC_VX_ACCESSOR_RW(32, 0x080, other)
#define GIC_VX_OTHER_VPNUM		GENMASK(5, 0)

/* GIC_Vx_IDENT - Retrieve the local Virtual Processor's ID */
GIC_VX_ACCESSOR_RO(32, 0x088, ident)
#define GIC_VX_IDENT_VPNUM		GENMASK(5, 0)

/* GIC_Vx_COMPARE - Value to compare with GIC_SH_COUNTER */
GIC_VX_ACCESSOR_RW(64, 0x0a0, compare)

/* GIC_Vx_EIC_SHADOW_SET_BASE - Set shadow register set for each interrupt */
GIC_VX_ACCESSOR_RW_INTR_REG(32, 0x100, 0x4, eic_shadow_set)

/**
 * enum mips_gic_local_interrupt - GIC local interrupts
 * @GIC_LOCAL_INT_WD: GIC watchdog timer interrupt
 * @GIC_LOCAL_INT_COMPARE: GIC count/compare interrupt
 * @GIC_LOCAL_INT_TIMER: CP0 count/compare interrupt
 * @GIC_LOCAL_INT_PERFCTR: Performance counter interrupt
 * @GIC_LOCAL_INT_SWINT0: Software interrupt 0
 * @GIC_LOCAL_INT_SWINT1: Software interrupt 1
 * @GIC_LOCAL_INT_FDC: Fast debug channel interrupt
 * @GIC_NUM_LOCAL_INTRS: The number of local interrupts
 *
 * Enumerates interrupts provided by the GIC that are local to a VP.
 */
enum mips_gic_local_interrupt {
	GIC_LOCAL_INT_WD,
	GIC_LOCAL_INT_COMPARE,
	GIC_LOCAL_INT_TIMER,
	GIC_LOCAL_INT_PERFCTR,
	GIC_LOCAL_INT_SWINT0,
	GIC_LOCAL_INT_SWINT1,
	GIC_LOCAL_INT_FDC,
	GIC_NUM_LOCAL_INTRS
};

/**
 * mips_gic_present() - Determine whether a GIC is present
 *
 * Determines whether a MIPS Global Interrupt Controller (GIC) is present in
 * the system that the kernel is running on.
 *
 * Return true if a GIC is present, else false.
 */
static inline bool mips_gic_present(void)
{
	return IS_ENABLED(CONFIG_MIPS_GIC) && mips_gic_base;
}

/**
 * mips_gic_vx_map_reg() - Return GIC_Vx_<intr>_MAP register offset
 * @intr: A GIC local interrupt
 *
 * Determine the index of the GIC_VL_<intr>_MAP or GIC_VO_<intr>_MAP register
 * within the block of GIC map registers. This is almost the same as the order
 * of interrupts in the pending & mask registers, as used by enum
 * mips_gic_local_interrupt, but moves the FDC interrupt & thus offsets the
 * interrupts after it...
 *
 * Return: The map register index corresponding to @intr.
 *
 * The return value is suitable for use with the (read|write)_gic_v[lo]_map
 * accessor functions.
 */
static inline unsigned int
mips_gic_vx_map_reg(enum mips_gic_local_interrupt intr)
{
	/* WD, Compare & Timer are 1:1 */
	if (intr <= GIC_LOCAL_INT_TIMER)
		return intr;

	/* FDC moves to after Timer... */
	if (intr == GIC_LOCAL_INT_FDC)
		return GIC_LOCAL_INT_TIMER + 1;

	/* As a result everything else is offset by 1 */
	return intr + 1;
}

/**
 * gic_get_c0_compare_int() - Return cp0 count/compare interrupt virq
 *
 * Determine the virq number to use for the coprocessor 0 count/compare
 * interrupt, which may be routed via the GIC.
 *
 * Returns the virq number or a negative error number.
 */
extern int gic_get_c0_compare_int(void);

/**
 * gic_get_c0_perfcount_int() - Return performance counter interrupt virq
 *
 * Determine the virq number to use for CPU performance counter interrupts,
 * which may be routed via the GIC.
 *
 * Returns the virq number or a negative error number.
 */
extern int gic_get_c0_perfcount_int(void);

/**
 * gic_get_c0_fdc_int() - Return fast debug channel interrupt virq
 *
 * Determine the virq number to use for fast debug channel (FDC) interrupts,
 * which may be routed via the GIC.
 *
 * Returns the virq number or a negative error number.
 */
extern int gic_get_c0_fdc_int(void);

#endif /* __MIPS_ASM_MIPS_CPS_H__ */