irq.c 12.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * 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.
 *
 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
14
#include <linux/irq.h>
15 16 17 18 19 20 21
#include <asm/irq_cpu.h>
#include <asm/mipsregs.h>
#include <bcm63xx_cpu.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_io.h>
#include <bcm63xx_irq.h>

22
static void __dispatch_internal(void) __maybe_unused;
23 24 25 26 27
static void __dispatch_internal_64(void) __maybe_unused;
static void __internal_irq_mask_32(unsigned int irq) __maybe_unused;
static void __internal_irq_mask_64(unsigned int irq) __maybe_unused;
static void __internal_irq_unmask_32(unsigned int irq) __maybe_unused;
static void __internal_irq_unmask_64(unsigned int irq) __maybe_unused;
28 29

#ifndef BCMCPU_RUNTIME_DETECT
30 31 32 33 34 35 36 37 38 39 40
#ifdef CONFIG_BCM63XX_CPU_6328
#define irq_stat_reg		PERF_IRQSTAT_6328_REG
#define irq_mask_reg		PERF_IRQMASK_6328_REG
#define irq_bits		64
#define is_ext_irq_cascaded	1
#define ext_irq_start		(BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE)
#define ext_irq_end		(BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE)
#define ext_irq_count		4
#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6328
#define ext_irq_cfg_reg2	0
#endif
41 42 43
#ifdef CONFIG_BCM63XX_CPU_6338
#define irq_stat_reg		PERF_IRQSTAT_6338_REG
#define irq_mask_reg		PERF_IRQMASK_6338_REG
44
#define irq_bits		32
45 46 47
#define is_ext_irq_cascaded	0
#define ext_irq_start		0
#define ext_irq_end		0
48 49 50
#define ext_irq_count		4
#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6338
#define ext_irq_cfg_reg2	0
51 52 53 54
#endif
#ifdef CONFIG_BCM63XX_CPU_6345
#define irq_stat_reg		PERF_IRQSTAT_6345_REG
#define irq_mask_reg		PERF_IRQMASK_6345_REG
55
#define irq_bits		32
56 57 58
#define is_ext_irq_cascaded	0
#define ext_irq_start		0
#define ext_irq_end		0
59 60 61
#define ext_irq_count		0
#define ext_irq_cfg_reg1	0
#define ext_irq_cfg_reg2	0
62 63 64 65
#endif
#ifdef CONFIG_BCM63XX_CPU_6348
#define irq_stat_reg		PERF_IRQSTAT_6348_REG
#define irq_mask_reg		PERF_IRQMASK_6348_REG
66
#define irq_bits		32
67 68 69
#define is_ext_irq_cascaded	0
#define ext_irq_start		0
#define ext_irq_end		0
70 71 72
#define ext_irq_count		4
#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6348
#define ext_irq_cfg_reg2	0
73 74 75 76
#endif
#ifdef CONFIG_BCM63XX_CPU_6358
#define irq_stat_reg		PERF_IRQSTAT_6358_REG
#define irq_mask_reg		PERF_IRQMASK_6358_REG
77
#define irq_bits		32
78 79 80
#define is_ext_irq_cascaded	1
#define ext_irq_start		(BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE)
#define ext_irq_end		(BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE)
81 82 83
#define ext_irq_count		4
#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6358
#define ext_irq_cfg_reg2	0
84
#endif
85 86 87 88 89 90 91 92 93 94 95
#ifdef CONFIG_BCM63XX_CPU_6368
#define irq_stat_reg		PERF_IRQSTAT_6368_REG
#define irq_mask_reg		PERF_IRQMASK_6368_REG
#define irq_bits		64
#define is_ext_irq_cascaded	1
#define ext_irq_start		(BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE)
#define ext_irq_end		(BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE)
#define ext_irq_count		6
#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6368
#define ext_irq_cfg_reg2	PERF_EXTIRQ_CFG_REG2_6368
#endif
96

97 98 99 100 101 102 103 104 105
#if irq_bits == 32
#define dispatch_internal			__dispatch_internal
#define internal_irq_mask			__internal_irq_mask_32
#define internal_irq_unmask			__internal_irq_unmask_32
#else
#define dispatch_internal			__dispatch_internal_64
#define internal_irq_mask			__internal_irq_mask_64
#define internal_irq_unmask			__internal_irq_unmask_64
#endif
106 107 108 109 110 111 112 113 114 115 116

#define irq_stat_addr	(bcm63xx_regset_address(RSET_PERF) + irq_stat_reg)
#define irq_mask_addr	(bcm63xx_regset_address(RSET_PERF) + irq_mask_reg)

static inline void bcm63xx_init_irq(void)
{
}
#else /* ! BCMCPU_RUNTIME_DETECT */

static u32 irq_stat_addr, irq_mask_addr;
static void (*dispatch_internal)(void);
117
static int is_ext_irq_cascaded;
118
static unsigned int ext_irq_count;
119
static unsigned int ext_irq_start, ext_irq_end;
120
static unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2;
121 122
static void (*internal_irq_mask)(unsigned int irq);
static void (*internal_irq_unmask)(unsigned int irq);
123 124 125

static void bcm63xx_init_irq(void)
{
126 127
	int irq_bits;

128 129 130 131
	irq_stat_addr = bcm63xx_regset_address(RSET_PERF);
	irq_mask_addr = bcm63xx_regset_address(RSET_PERF);

	switch (bcm63xx_get_cpu_id()) {
132 133 134 135 136 137 138 139 140 141
	case BCM6328_CPU_ID:
		irq_stat_addr += PERF_IRQSTAT_6328_REG;
		irq_mask_addr += PERF_IRQMASK_6328_REG;
		irq_bits = 64;
		ext_irq_count = 4;
		is_ext_irq_cascaded = 1;
		ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
		ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
		break;
142 143 144
	case BCM6338_CPU_ID:
		irq_stat_addr += PERF_IRQSTAT_6338_REG;
		irq_mask_addr += PERF_IRQMASK_6338_REG;
145
		irq_bits = 32;
146 147 148 149
		break;
	case BCM6345_CPU_ID:
		irq_stat_addr += PERF_IRQSTAT_6345_REG;
		irq_mask_addr += PERF_IRQMASK_6345_REG;
150
		irq_bits = 32;
151 152 153 154
		break;
	case BCM6348_CPU_ID:
		irq_stat_addr += PERF_IRQSTAT_6348_REG;
		irq_mask_addr += PERF_IRQMASK_6348_REG;
155
		irq_bits = 32;
156 157
		ext_irq_count = 4;
		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348;
158 159 160 161
		break;
	case BCM6358_CPU_ID:
		irq_stat_addr += PERF_IRQSTAT_6358_REG;
		irq_mask_addr += PERF_IRQMASK_6358_REG;
162
		irq_bits = 32;
163
		ext_irq_count = 4;
164 165 166
		is_ext_irq_cascaded = 1;
		ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
		ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
167
		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358;
168
		break;
169 170 171 172 173 174 175 176 177 178 179
	case BCM6368_CPU_ID:
		irq_stat_addr += PERF_IRQSTAT_6368_REG;
		irq_mask_addr += PERF_IRQMASK_6368_REG;
		irq_bits = 64;
		ext_irq_count = 6;
		is_ext_irq_cascaded = 1;
		ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE;
		ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE;
		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368;
		ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368;
		break;
180 181 182 183
	default:
		BUG();
	}

184 185 186 187 188 189 190 191 192
	if (irq_bits == 32) {
		dispatch_internal = __dispatch_internal;
		internal_irq_mask = __internal_irq_mask_32;
		internal_irq_unmask = __internal_irq_unmask_32;
	} else {
		dispatch_internal = __dispatch_internal_64;
		internal_irq_mask = __internal_irq_mask_64;
		internal_irq_unmask = __internal_irq_unmask_64;
	}
193 194 195
}
#endif /* ! BCMCPU_RUNTIME_DETECT */

196 197 198 199 200 201 202
static inline u32 get_ext_irq_perf_reg(int irq)
{
	if (irq < 4)
		return ext_irq_cfg_reg1;
	return ext_irq_cfg_reg2;
}

203 204
static inline void handle_internal(int intbit)
{
205 206 207 208 209
	if (is_ext_irq_cascaded &&
	    intbit >= ext_irq_start && intbit <= ext_irq_end)
		do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
	else
		do_IRQ(intbit + IRQ_INTERNAL_BASE);
210 211
}

212 213 214 215 216 217
/*
 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
 * prioritize any interrupt relatively to another. the static counter
 * will resume the loop where it ended the last time we left this
 * function.
 */
218
static void __dispatch_internal(void)
219 220 221 222
{
	u32 pending;
	static int i;

223
	pending = bcm_readl(irq_stat_addr) & bcm_readl(irq_mask_addr);
224 225 226 227 228 229 230 231 232

	if (!pending)
		return ;

	while (1) {
		int to_call = i;

		i = (i + 1) & 0x1f;
		if (pending & (1 << to_call)) {
233
			handle_internal(to_call);
234 235 236 237 238
			break;
		}
	}
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
static void __dispatch_internal_64(void)
{
	u64 pending;
	static int i;

	pending = bcm_readq(irq_stat_addr) & bcm_readq(irq_mask_addr);

	if (!pending)
		return ;

	while (1) {
		int to_call = i;

		i = (i + 1) & 0x3f;
		if (pending & (1ull << to_call)) {
			handle_internal(to_call);
			break;
		}
	}
}

260 261 262 263 264 265 266 267 268 269 270 271 272
asmlinkage void plat_irq_dispatch(void)
{
	u32 cause;

	do {
		cause = read_c0_cause() & read_c0_status() & ST0_IM;

		if (!cause)
			break;

		if (cause & CAUSEF_IP7)
			do_IRQ(7);
		if (cause & CAUSEF_IP2)
273
			dispatch_internal();
274 275 276 277 278 279 280 281 282 283
		if (!is_ext_irq_cascaded) {
			if (cause & CAUSEF_IP3)
				do_IRQ(IRQ_EXT_0);
			if (cause & CAUSEF_IP4)
				do_IRQ(IRQ_EXT_1);
			if (cause & CAUSEF_IP5)
				do_IRQ(IRQ_EXT_2);
			if (cause & CAUSEF_IP6)
				do_IRQ(IRQ_EXT_3);
		}
284 285 286 287 288 289 290
	} while (1);
}

/*
 * internal IRQs operations: only mask/unmask on PERF irq mask
 * register.
 */
291
static void __internal_irq_mask_32(unsigned int irq)
292 293 294
{
	u32 mask;

295
	mask = bcm_readl(irq_mask_addr);
296
	mask &= ~(1 << irq);
297
	bcm_writel(mask, irq_mask_addr);
298 299
}

300 301 302 303 304 305 306 307 308 309
static void __internal_irq_mask_64(unsigned int irq)
{
	u64 mask;

	mask = bcm_readq(irq_mask_addr);
	mask &= ~(1ull << irq);
	bcm_writeq(mask, irq_mask_addr);
}

static void __internal_irq_unmask_32(unsigned int irq)
310 311 312
{
	u32 mask;

313
	mask = bcm_readl(irq_mask_addr);
314
	mask |= (1 << irq);
315
	bcm_writel(mask, irq_mask_addr);
316 317
}

318 319 320 321 322 323 324 325 326
static void __internal_irq_unmask_64(unsigned int irq)
{
	u64 mask;

	mask = bcm_readq(irq_mask_addr);
	mask |= (1ull << irq);
	bcm_writeq(mask, irq_mask_addr);
}

327 328 329 330 331 332 333 334 335 336
static void bcm63xx_internal_irq_mask(struct irq_data *d)
{
	internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);
}

static void bcm63xx_internal_irq_unmask(struct irq_data *d)
{
	internal_irq_unmask(d->irq - IRQ_INTERNAL_BASE);
}

337 338 339 340
/*
 * external IRQs operations: mask/unmask and clear on PERF external
 * irq control register.
 */
341
static void bcm63xx_external_irq_mask(struct irq_data *d)
342
{
343
	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
344
	u32 reg, regaddr;
345

346 347 348 349 350 351 352 353 354
	regaddr = get_ext_irq_perf_reg(irq);
	reg = bcm_perf_readl(regaddr);

	if (BCMCPU_IS_6348())
		reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4);
	else
		reg &= ~EXTIRQ_CFG_MASK(irq % 4);

	bcm_perf_writel(reg, regaddr);
355 356
	if (is_ext_irq_cascaded)
		internal_irq_mask(irq + ext_irq_start);
357 358
}

359
static void bcm63xx_external_irq_unmask(struct irq_data *d)
360
{
361
	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
362 363 364 365 366 367 368 369 370 371 372
	u32 reg, regaddr;

	regaddr = get_ext_irq_perf_reg(irq);
	reg = bcm_perf_readl(regaddr);

	if (BCMCPU_IS_6348())
		reg |= EXTIRQ_CFG_MASK_6348(irq % 4);
	else
		reg |= EXTIRQ_CFG_MASK(irq % 4);

	bcm_perf_writel(reg, regaddr);
373

374 375
	if (is_ext_irq_cascaded)
		internal_irq_unmask(irq + ext_irq_start);
376 377
}

378
static void bcm63xx_external_irq_clear(struct irq_data *d)
379
{
380
	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
381 382 383 384
	u32 reg, regaddr;

	regaddr = get_ext_irq_perf_reg(irq);
	reg = bcm_perf_readl(regaddr);
385

386 387 388 389 390 391
	if (BCMCPU_IS_6348())
		reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4);
	else
		reg |= EXTIRQ_CFG_CLEAR(irq % 4);

	bcm_perf_writel(reg, regaddr);
392 393
}

394
static int bcm63xx_external_irq_set_type(struct irq_data *d,
395 396
					 unsigned int flow_type)
{
397
	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
398 399
	u32 reg, regaddr;
	int levelsense, sense, bothedge;
400 401 402 403 404 405

	flow_type &= IRQ_TYPE_SENSE_MASK;

	if (flow_type == IRQ_TYPE_NONE)
		flow_type = IRQ_TYPE_LEVEL_LOW;

406
	levelsense = sense = bothedge = 0;
407 408
	switch (flow_type) {
	case IRQ_TYPE_EDGE_BOTH:
409
		bothedge = 1;
410 411 412
		break;

	case IRQ_TYPE_EDGE_RISING:
413
		sense = 1;
414 415 416 417 418 419
		break;

	case IRQ_TYPE_EDGE_FALLING:
		break;

	case IRQ_TYPE_LEVEL_HIGH:
420 421
		levelsense = 1;
		sense = 1;
422 423 424
		break;

	case IRQ_TYPE_LEVEL_LOW:
425
		levelsense = 1;
426 427 428 429 430 431
		break;

	default:
		printk(KERN_ERR "bogus flow type combination given !\n");
		return -EINVAL;
	}
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451

	regaddr = get_ext_irq_perf_reg(irq);
	reg = bcm_perf_readl(regaddr);
	irq %= 4;

	if (BCMCPU_IS_6348()) {
		if (levelsense)
			reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq);
		else
			reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq);
		if (sense)
			reg |= EXTIRQ_CFG_SENSE_6348(irq);
		else
			reg &= ~EXTIRQ_CFG_SENSE_6348(irq);
		if (bothedge)
			reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq);
		else
			reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
	}

452
	if (BCMCPU_IS_6338() || BCMCPU_IS_6358() || BCMCPU_IS_6368()) {
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
		if (levelsense)
			reg |= EXTIRQ_CFG_LEVELSENSE(irq);
		else
			reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
		if (sense)
			reg |= EXTIRQ_CFG_SENSE(irq);
		else
			reg &= ~EXTIRQ_CFG_SENSE(irq);
		if (bothedge)
			reg |= EXTIRQ_CFG_BOTHEDGE(irq);
		else
			reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
	}

	bcm_perf_writel(reg, regaddr);
468

469 470 471 472 473
	irqd_set_trigger_type(d, flow_type);
	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
		__irq_set_handler_locked(d->irq, handle_level_irq);
	else
		__irq_set_handler_locked(d->irq, handle_edge_irq);
474

475
	return IRQ_SET_MASK_OK_NOCOPY;
476 477 478 479
}

static struct irq_chip bcm63xx_internal_irq_chip = {
	.name		= "bcm63xx_ipic",
480 481
	.irq_mask	= bcm63xx_internal_irq_mask,
	.irq_unmask	= bcm63xx_internal_irq_unmask,
482 483 484 485
};

static struct irq_chip bcm63xx_external_irq_chip = {
	.name		= "bcm63xx_epic",
486
	.irq_ack	= bcm63xx_external_irq_clear,
487

488 489
	.irq_mask	= bcm63xx_external_irq_mask,
	.irq_unmask	= bcm63xx_external_irq_unmask,
490

491
	.irq_set_type	= bcm63xx_external_irq_set_type,
492 493 494 495 496
};

static struct irqaction cpu_ip2_cascade_action = {
	.handler	= no_action,
	.name		= "cascade_ip2",
497
	.flags		= IRQF_NO_THREAD,
498 499
};

500 501 502 503 504 505
static struct irqaction cpu_ext_cascade_action = {
	.handler	= no_action,
	.name		= "cascade_extirq",
	.flags		= IRQF_NO_THREAD,
};

506 507 508 509
void __init arch_init_irq(void)
{
	int i;

510
	bcm63xx_init_irq();
511 512
	mips_cpu_irq_init();
	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
513
		irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
514 515
					 handle_level_irq);

516
	for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i)
517
		irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
518 519
					 handle_edge_irq);

520
	if (!is_ext_irq_cascaded) {
521
		for (i = 3; i < 3 + ext_irq_count; ++i)
522 523 524 525
			setup_irq(MIPS_CPU_IRQ_BASE + i, &cpu_ext_cascade_action);
	}

	setup_irq(MIPS_CPU_IRQ_BASE + 2, &cpu_ip2_cascade_action);
526
}